0

I am having some difficulties converting a string to datetime in PHP.

The string can end with both CEST and CET as timezone, so I guess I have to somehow split before that? I am not looking to store the CET or CEST. I only want the date converted to a datetime.

This is the strings:

Dec 27 2017, 22:46:15 CET and Dec 27 2017, 22:46:15 CEST

But also, the spaces in the strings are   and not regular spaces. So in HTML it looks like this:

Dec 27 2017, 22:46:15 CET and Dec 27 2017, 22:46:15 CEST

I want it converted in to: 2017-12-27 22:46:15 and then insert that to my database. I figured out how to change this in an SQL query (my column is called lastlogin) and I do that by doing this:

STR_TO_DATE(SUBSTRING_INDEX(REPLACE(lastlogin,char(160),' '),'C',1), '%b %d %Y, %T')

But I'd really like to do this in PHP. So let's say my variable with the date is called $lastlogin, how would I apply that SQL logic to my PHP? the char(160) in SQL is the  , I am not sure about PHP.

Edit: I am storing it in $character->lastlogin variable. (note: apparently it is getting   instead of  )

$character->lastlogin = str_replace(' ', ' ', $character->lastlogin);
$datetime = DateTime::createFromFormat('M d Y, H:i:s e', $character->lastlogin);
$character->lastlogin = $datetime->format('Y-m-d H:i:s');

Gives me this error: Fatal error: Uncaught Error: Call to a member function format() on boolean

Lee Cheung
  • 101
  • 2
  • 9

2 Answers2

0

The accepted answer is correct, but not optimal.

Here's what we know:

  • the input date string is from html, and might contain special characters encoded as html entities
  • html entities are decoded as utf-8
    • but, createFromFormat expects plain old ascii*
  • we should never underestimate errors/failures
  • the date has a specific format

*I deduced this after some trial and error, I read this in the docs:

 (space)      Zero or more spaces, tabs, NBSP (U+A0), or           Example: "\t", " "
              NNBSP (U+202F) characters

..and further noted that in UTF-8, NBSP is U+C2 U+A0, not just A0.


$dateWithHtmlEntities = 'Dec 27 2017, 22:46:15 CET';
$dateWithUTF8Chars = html_entity_decode($dateWithHtmlEntities);
$dateInPlainASCII = iconv('UTF-8', 'US-ASCII//TRANSLIT//IGNORE', $dateWithUTF8Chars);
$dateTimeObject = DateTime::createFromFormat('M d Y, H:i:s T', $dateInPlainASCII);

if (!$dateTimeObject) {
    // TODO handle errors your own way
    throw new InvalidArgumentException('Date could not be parsed: ' . $decodedDate);
}

echo 'Final date: ' . $dateTimeObject->format('Y-m-d H:i:s');

PS: US-ASCII//TRANSLIT//IGNORE means "convert to ascii; translate possible characters (e.g. ü => ue); and ignore the rest that can't be translated".

PPS: using encoding/decoding functions instead of str_replace has the advantage of automatically handling any other special characters that you didn't consider. Alternatively, a potentially better approach, (than str_replace), is to use regex (preg_replace) with character classes.

PPPS: Can't stress this enough - always handle errors and failures, especially when experimenting with new code.

Et voilà: https://3v4l.org/CLgfO

Christian
  • 27,509
  • 17
  • 111
  • 155
-1

You should use DateTime::createFromFormat

// CET Timezone (+01:00)
$datetime = DateTime::createFromFormat('M d Y, H:i:s e', 'Dec 27 2017, 22:46:15 CET');

// change timezone to UTC (+00:00) (if necessary)
$datetime->setTimeZone(new DateTimeZone('UTC'));

$output = $datetime->format('Y-m-d H:i:s');
// output: 2017-12-27 21:46:15
Ben
  • 5,069
  • 4
  • 18
  • 26
  • I am getting this error: `Fatal error: Uncaught Error: Call to a member function format() on boolean in`. I don't think it recognizes it as a date with the ` ` in the string. Also I don't want to change the timezones. It doesn't matter if it is CEST or CET. I just want to grab the date and time before that (even if it has 1 hour difference). – Lee Cheung Dec 28 '17 at 14:23
  • what is you date string? because you provided incorrect date string, you should ensure it has this format `Dec 27 2017, 22:46:15 CET`, `Dec 27 2017, 22:46:15 CEST` etc.. – Ben Dec 28 '17 at 14:27
  • It is a string in HTML, as I wrote above. It uses ` ` instead of normal spaces. Right now I tried this: `$datetime = DateTime::createFromFormat('M d Y, H:i:s e', str_replace(" ", " ", $lastlogin));` and `$output = $datetime->format('Y-m-d H:i:s');` but then it says it is a boolean? – Lee Cheung Dec 28 '17 at 14:30
  • String is: `Dec 27 2017, 22:46:15 CET` – Lee Cheung Dec 28 '17 at 14:30
  • the better way you should replace the string before parsing with `DateTime`, `$str = str_replace(' ', ' ', $str);` – Ben Dec 28 '17 at 14:40
  • I don't understand why it thinks it's a boolean all of a sudden... I'll update my code in my post. Please have a look – Lee Cheung Dec 28 '17 at 14:48
  • In my database, if I change to a VARCHAR and just insert the string, I get this value: `Dec 27 2017, 22:46:15 CET` – Lee Cheung Dec 28 '17 at 14:53
  • you get this error because you provided incorrect datetime format for `DateTime` parsing – Ben Dec 28 '17 at 15:00
  • i found no problem after replacing ` `, it got `Dec 27 2017, 22:46:15 CET` and got the output correctly – Ben Dec 28 '17 at 15:01
  • I got it to work when I put it all on same line. Very weird! It just would not work, and kept saying it was a boolean when I didn't have it on same line (WTF????). My line: `DateTime::createFromFormat('M d Y, H:i:s e', str_replace(' ', ' ', $character->lastlogin))->format('Y-m-d H:i:s')` Thanks for your help! +1 Rep!!! – Lee Cheung Dec 28 '17 at 15:03
  • @LeeCheung if you check the docs, it says the return type of `createFromFormat` is `DateTime|false`, meaning either a date object or a boolean false. When you try calling the format method on false, you get the error you're saying. In the end, Ben's code unfortunately did not handle failures properly - always do that and you'll be avoid a whole lot of weird errors. – Christian Aug 19 '23 at 07:49