Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DateTime formatting and timezone

When trying to parse a Date with DateTime::createFromFormat PHP will not recognize the timezone.

Example:

$t = new \DateTime(); 
echo $t->format('Y-m-dTH:i:s');

will output

2012-01-24MSK16:53:52

Now when I try to parse that string from the same format

var_dump(\DateTime::createFromFormat('Y-m-dTH:i:s', '2012-01-24MSK16:53:52'));

I get

bool(false)

When I do not put the Timezone into the string, it works

$t = new \DateTime(); 
echo $t->format('Y-m-dH:i:s');

will give

2012-01-2417:17:24

and parsing that

var_dump(\DateTime::createFromFormat('Y-m-dH:i:s', "2012-01-2417:17:24"));

will give

object(DateTime)#3 (3) {
  ["date"]=>
  string(19) "2012-01-24 17:17:24"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Moscow"
}

Tested on

  • PHP 5.3.6-13ubuntu3.3 with Suhosin-Patch (cli) (built: Dec 13 2011 18:18:37) and
  • PHP 5.3.9 (cli) (built: Jan 18 2012 20:02:33)

Problem appears just if we are take care about timezone. Is it a bug? Or what I do wrong? Thank you in advance!

like image 952
fckt Avatar asked Dec 10 '22 03:12

fckt


1 Answers

It looks like a bug (or at least an undocumented limitation) with PHP... If we try the 4 possible whitespace permutations:

var_dump(\DateTime::createFromFormat('Y-m-dTH:i:s', '2012-01-24MSK16:53:52'));
var_dump(\DateTime::createFromFormat('Y-m-d T H:i:s', '2012-01-24 MSK 16:53:52'));
var_dump(\DateTime::createFromFormat('Y-m-d TH:i:s', '2012-01-24 MSK16:53:52'));
var_dump(\DateTime::createFromFormat('Y-m-dT H:i:s', '2012-01-24MSK 16:53:52'));

We get (tested PHP 5.3, 5.4rc6 and Trunk):

bool(false)
object(DateTime)#2 (3) {
  ["date"]=>
  string(19) "2012-01-24 16:53:52"
  ["timezone_type"]=>
  int(2)
  ["timezone"]=>
  string(3) "MSK"
}
bool(false)
object(DateTime)#3 (3) {
  ["date"]=>
  string(19) "2012-01-24 16:53:52"
  ["timezone_type"]=>
  int(2)
  ["timezone"]=>
  string(3) "MSK"
}

So that seems to point that the timezone identifier and/or hour are sensitive to whitespace... Testing further:

var_dump(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-01-24 16:53:52'));
var_dump(\DateTime::createFromFormat('Y-m-dH:i:s', '2012-01-2416:53:52'));

Yields the proper results. And:

var_dump(\DateTime::createFromFormat('TY-m-d', 'MSK2012-01-24'));
var_dump(\DateTime::createFromFormat('T Y-m-d', 'MSK 2012-01-24'));

Yields:

bool(false)
object(DateTime)#4 (3) {
  ["date"]=>
  string(19) "2012-01-24 01:49:26"
  ["timezone_type"]=>
  int(2)
  ["timezone"]=>
  string(3) "MSK"
}

So yes, it does appear that the timezone specifier is sensitive to trailing whitespace...

Edit: It is white-space sensitive

If we look at parse_date.c timelib_parse_from_format() on line 25075, we can see that all 4 timezone formats are parsed the same way! That means there is no difference at all between the format identifiers for parsing, and therefore for parsing they are interchangable.

That alone seems like enough of a bug (or lack of a feature) to go on. But, let's see what happens in timelib_get_zone() which is called when you use a timezone identifier. Well, looking on, we can see that we call timelib_lookup_zone() when it's not GMT or a time offset.

And there we found the bug. On line 768 of timelib_lookup_zone, we can see that it will consume the entire input string up to one of either \0 (null), ) or a space:

while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
    ++*ptr;
}

With respect to fixing it, that's a little more tricky. To just fix this issue, would require re-implementing the format parsers for each timezone. For the T parser, this is easy, since it's always a 3 letter string. But for the others, it's a little more interesting, since there are variable letters, and as such whitespace sensitivity may be an issue.

So in short, I would suggest just adding a trailing white-space to your timezone identifiers and being done with it...

like image 63
ircmaxell Avatar answered Feb 26 '23 12:02

ircmaxell