日期时间格式化和时区

4
尝试使用DateTime::createFromFormat解析日期时,PHP无法识别时区。

示例:

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

将输出

2012-01-24MSK16:53:52

现在,当我尝试从相同的格式解析该字符串时
var_dump(\DateTime::createFromFormat('Y-m-dTH:i:s', '2012-01-24MSK16:53:52'));

我理解

bool(false)

如果我不在字符串中加入时区,它就能正常工作。

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

将会给予

2012-01-2417:17:24

并解析它

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

将会给予

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

测试环境如下:

  • PHP 5.3.6-13ubuntu3.3,带有Suhosin补丁 (cli) (构建日期:2011年12月13日 18:18:37)
  • PHP 5.3.9 (cli) (构建日期:2012年1月18日 20:02:33)

只有在考虑时区时才会出现问题。这是一个Bug吗?还是我做错了什么?谢谢!


我应该看到:php > $t = new \DateTime(); echo $t->format('Y-m-dH:i:s'); 2012-01-2417:17:24 php > var_dump(\DateTime::createFromFormat('Y-m-dH:i:s', "2012-01-2417:17:24")); object(DateTime)#3 (3) { ["date"]=> string(19) "2012-01-24 17:17:24" ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Europe/Moscow" } - fckt
我知道答案,但是:糟糕!由于以下原因,您的答案无法提交:声望值低于100的用户在提问后8小时内无法回答自己的问题。您可以在7小时后自行回答。在此之前,请使用评论或编辑您的问题。 - fckt
1
Gordon,非常感谢您的编辑!!!我刚刚在 Twitter 上关注了您=) - fckt
Y-m-dTH:i:s 是什么格式?你是不是想要 Y-m-d\TH:i:s - salathe
2个回答

14
似乎是PHP的一个bug(或至少是未记录的限制)...如果我们尝试4种可能的空格排列:
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'));

我们进行了测试(PHP 5.3、5.4rc6和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"
}

因此看起来时区标识符和/或小时对空格很敏感...继续测试:

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'));

产生正确的结果。并且:
var_dump(\DateTime::createFromFormat('TY-m-d', 'MSK2012-01-24'));
var_dump(\DateTime::createFromFormat('T Y-m-d', 'MSK 2012-01-24'));

产生:

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

所以,是的,时区指定符对尾随空格敏感...
编辑:它是敏感于空白字符的
如果我们查看parse_date.c timelib_parse_from_format() on line 25075,我们可以看到所有4个时区格式都以相同的方式解析!这意味着在解析方面,解析格式标识符之间没有任何区别,因此它们是可互换的。
那似乎已经足够成为一个错误(或缺少功能)。但是,让我们看看当您使用时区标识符时会发生什么,即 timelib_get_zone()。好吧,在继续查看时,我们可以看到当它不是 GMT 或时间偏移量时,我们会调用 timelib_lookup_zone()
然后我们发现了错误。在 timelib_lookup_zone 的第 768 行中,我们可以看到它将消耗整个输入字符串直到遇到 \0(null)、) 或空格之一:
while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
    ++*ptr;
}

关于修复它,这有点棘手。要仅仅修复此问题,需要为每个时区重新实现格式解析器。对于T解析器来说,这很容易,因为它始终是一个3个字母的字符串。但对于其他解析器来说,情况会更加有趣,因为存在可变字母,因此空格敏感度可能成为一个问题。
简而言之,我建议只需在时区标识符后添加一个尾随空格即可解决问题...

就此而言,“T”并不总是一个三个字母的字符串。例如,它也可以是“CEST”。 - Derick

0
你可以检查一下你的php.ini文件是否有默认的date.timezone设置,如果没有,就使用date_default_timezone_set来设置它,因为DateTime类依赖于此。

根据第二个示例的输出,他已经设置了默认时区。而且我怀疑这与此无关。 - Gordon
要从带有时区的格式创建 \DateTime 对象,您需要通过空格分隔时间和时区。 php > var_dump(\DateTime::createFromFormat('Y-m-dH:i:s T', "2012-01-2417:15:59 MSK")); object(DateTime)#16 (3) { ["date"]=> string(19) "2012-01-24 17:15:59" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "MSK" }或者php > var_dump(\DateTime::createFromFormat('Y-m-d T H:i:s', "2012-01-24 MSK 17:15:59")); object(DateTime)#21 (3) { ["date"]=> string(19) "2012-01-24 17:15:59" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "MSK" } - fckt
但是我无法回答,因为:但是:糟糕!您的答案无法提交,因为:声望不足100的用户在提问后8小时内不能回答自己的问题。您可以在7小时后自行回答。在此之前,请使用评论或编辑您的问题。 - fckt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接