我正在尝试转换电视剧集的播出日期时间。我使用的 API 向我返回以下确切值:
Datetime: 2015-05-31 21:00
Country: US
Timezone: GMT-5 +DST
现在我正在尝试使用给定的数据检索我的时区(欧洲/罗马(的播出日期时间。我正在做的是:
$date = new DateTime('2015-05-31 21:00', new DateTimeZone('GMT-5 +DST'));
$date->setTimezone(new DateTimeZone('Europe/Rome'));
echo $date->format('Y-m-d H:i:s');
哪些打印:
2015-06-01 04:00:00
我对是否是正确的方法有些怀疑,因为其他提供相同信息的网站说上述剧集在我的国家(IT/意大利(已于 6 月 1 日 03:00(而不是 04:00(播出。
那么我做得对吗,我比较结果的网站是错误的吗?
如果使用new DateTimeZone('GMT-5')
,则会获得相同的值。 GMT-5 +DST
不是时区名称的有效值,类 DateTimeZone
的构造函数可能会尽可能多地使用它从提供给它的参数中成功解析的内容。
我认为您应该"手动"解析Timezone:
的值,如果字符串以 +DST
结尾,则将偏移量调整 1 小时。
如果您知道返回的时区始终GMT-5
那么您只需检查字符串是否GMT-5 +DST
并使用GMT-4
即可。
否则,您可以尝试使用正则表达式解析收到的时区:
// The timezone received from the API
$timezone = 'GMT-5 +DST';
$m = array();
if (preg_match('/^GMT([+-]d+)( +DST)?$/', $timezone, $m)) {
if ($m[2] == ' +DST') {
// Compute the correct offset using DST
$tz = sprintf('Etc/GMT%+d', -(intval($m[1])+1));
} else {
// No DST; use the correct name of the time zone
$tz = sprintf('Etc/GMT%+d', -intval($m[1]));
}
} else {
// The timezone name has a different format; use it as is
// You should do better checks here
$tz = $timestamp;
}
$date = new DateTime('2015-05-31 21:00', new DateTimeZone($tz));
$date->setTimezone(new DateTimeZone('Europe/Rome'));
echo $date->format('Y-m-d H:i:s');
更新:正如 @matt-johnson 所注意到的,GMT
时区的正确名称Etc/GMT
后跟偏移量。
PHP 5.5
和PHP 5.6
接受并正确解释不带Etc/
前缀的GMT
时区。旧版本(5.3
,5.4
(抛出异常并带有消息'DateTimeZone::__construct(): Unknown or bad timezone (GMT-4)'
我更新了上面的代码以使用正确的名称。您必须在行中注意一些事项:
$tz = sprintf('Etc/GMT%+d', -(intval($m[1])+1));
sprintf()
格式字符串上的+
符号强制在数字前面生成+
符号(如果数字为正数(; 对于负数,无论是否使用它,都会产生-
符号;intval($m[1])+1
中的+1
进行DST
校正;- 由于 PHP 使用的时区数据库的奇怪行为,计算值的符号发生了变化(请注意它前面的
-
(;该行为在文档中进行了解释:警告:
请不要使用此处列出的任何时区(UTC 除外(,它们仅出于向后兼容的原因而存在。
警告
如果您忽略上述警告,另请注意,提供 PHP 时区支持的 IANA 时区数据库使用 POSIX 样式符号,这会导致 Etc/GMT+n 和 Etc/GMT-n 时区与常用时区颠倒。
GMT-5
被上面的代码转换为Etc/GMT+5
,GMT-5 +DST
被转换为Etc/GMT+4
。
这应该有效。 它会解析您提供的值,手动将输入时区调整为 UTC。 然后使用 PHP 的时区(即 IANA 时区(转换为所需的目标时区。
// The values received from the API
$datetime = '2015-05-31 21:00';
$timezone = 'GMT-5 +DST';
// Parse the time zone
$m = array();
preg_match('/GMT([+-]d+)( +DST)?/', $timezone, $m);
$offset = intval($m[1]);
$dst = sizeof($m) == 3;
// Adjust for the DST flag
if ($dst) $offset++;
// Apply the offset to the datetime given
$dt = new DateTime($datetime, new DateTimeZone("UTC"));
$dt->setTimestamp($dt->getTimestamp() - ($offset * 3600));
// Convert it to whatever time zone you like
$dt->setTimezone(new DateTimeZone('Europe/Rome'));
echo $dt->format('Y-m-d H:i:s');