如何修复我的时间代码中这个非常令人沮丧的时间错误?
How to fix this very frustrating time bug in my time code?
我有这两个功能:
function time_is_older_than($timestamp, $time_string)
{
if (strtotime($timestamp) < strtotime('-' . $time_string))
return true;
return false;
}
function time_is_younger_than($timestamp, $time_string)
{
if (strtotime($timestamp) > strtotime('-' . $time_string))
return true;
return false;
}
它们使我能够做一些整洁的事情,例如:
if (time_is_older_than($last_time_some_action_happened, '5 minutes'))
do_it_again();
他们通常工作,除了每六个月一小时,当我时区切换为“夏令时”或“冬令时”。这意味着时钟在午夜增加或推迟一小时(根据这个时区)。
PHP 手册为 strtotime
说明了这一点:
The Unix timestamp that this function returns does not contain information about time zones. In order to do calculations with date/time information, you should use the more capable DateTimeImmutable.
但是,如果我提供完全相同的 date/time 字符串,例如在末尾添加“+08:00”而不是“+00:00”,我将返回不同的秒数。所以 strtotime()
在解析提供的时间时确实理解时区,即使返回的整数显然不包含此信息。 (我也不期望或要求它。)
我花了无数个小时来调试这个,测试了无数的东西,只是坐在这里思考,但我无法弄清楚到底是什么让我的代码失败,特别是一个小时。特别是我需要改变的是什么。为 strtotime()
设置第二个参数似乎是可行的,但我无法使其正常工作。
我很长一段时间最热门的“线索”是 strtotime('-' . $time_string)
部分最终使用与提供的时间戳字符串不同的时区,但我大部分时间都向它提供时区数据! $last_time_some_action_happened
的示例可能类似于 2020-10-28 02:22:41.123456+01
.
我用 date_default_timezone_set()
设置了时区。
我怀疑我只需要做一些非常小的改变,但我已经试验了这么多、这么久,甚至中间休息一下,我的大脑再也看不清楚了。我敢打赌解决方案非常简单。
请不要告诉我使用DateTimeImmutable
。这将从根本上改变我的整个结构,并要求我以非常不同的方式做事。也许我 应该 ,甚至 会 ,在某些时候,但现在,我只想修复我的这个罕见但仍然非常烦人的错误现有代码。 (如果可能的话,我非常相信是这样的。)
您能否考虑一个解决方案:
在时间戳字符串中(如 Thu, 21 Dec 2000 16:01:07 +0200),添加一个 timezone 标签,指定不区分夏令时的时区。
我能够重现您遇到的问题:
date_default_timezone_set('Pacific/Auckland');
// Daylight saving time 2020 in New Zealand began at 2:00am on Sunday, 27 September
$current = strtotime('2020-09-27 02:04:00');
$d1 = strtotime('2020-09-27 02:05:00', $current);
$d2 = strtotime('-5 minutes', $current);
var_dump($d1 > $d2); // false
var_dump(date('Y-m-d H:i:s', $d1)); // 2020-09-27 03:05:00
var_dump(date('Y-m-d H:i:s', $d2)); // 2020-09-27 03:59:00
此人似乎遇到了与您相同的问题,并且可能看起来是一个错误。
DateTime::modify and DST switch
解决方案是将日期转换为 UTC,然后进行比较:
// Convert to UTC and compare
$d1 = new \DateTime('2020-09-27 02:05:00', new \DateTimeZone('Pacific/Auckland'));
$d2 = new \DateTime('2020-09-27 02:04:00', new \DateTimeZone('Pacific/Auckland'));
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-5 minutes');
$d2->setTimezone(new \DateTimeZone('Pacific/Auckland'));
var_dump($d1 > $d2); // true
var_dump($d1->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T03:05:00.000+13:00
var_dump($d2->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T01:59:00.000+12:00
我已经更新了你的函数:
function time_is_older_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 < $d2;
}
function time_is_younger_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 > $d2;
}
我有这两个功能:
function time_is_older_than($timestamp, $time_string)
{
if (strtotime($timestamp) < strtotime('-' . $time_string))
return true;
return false;
}
function time_is_younger_than($timestamp, $time_string)
{
if (strtotime($timestamp) > strtotime('-' . $time_string))
return true;
return false;
}
它们使我能够做一些整洁的事情,例如:
if (time_is_older_than($last_time_some_action_happened, '5 minutes'))
do_it_again();
他们通常工作,除了每六个月一小时,当我时区切换为“夏令时”或“冬令时”。这意味着时钟在午夜增加或推迟一小时(根据这个时区)。
PHP 手册为 strtotime
说明了这一点:
The Unix timestamp that this function returns does not contain information about time zones. In order to do calculations with date/time information, you should use the more capable DateTimeImmutable.
但是,如果我提供完全相同的 date/time 字符串,例如在末尾添加“+08:00”而不是“+00:00”,我将返回不同的秒数。所以 strtotime()
在解析提供的时间时确实理解时区,即使返回的整数显然不包含此信息。 (我也不期望或要求它。)
我花了无数个小时来调试这个,测试了无数的东西,只是坐在这里思考,但我无法弄清楚到底是什么让我的代码失败,特别是一个小时。特别是我需要改变的是什么。为 strtotime()
设置第二个参数似乎是可行的,但我无法使其正常工作。
我很长一段时间最热门的“线索”是 strtotime('-' . $time_string)
部分最终使用与提供的时间戳字符串不同的时区,但我大部分时间都向它提供时区数据! $last_time_some_action_happened
的示例可能类似于 2020-10-28 02:22:41.123456+01
.
我用 date_default_timezone_set()
设置了时区。
我怀疑我只需要做一些非常小的改变,但我已经试验了这么多、这么久,甚至中间休息一下,我的大脑再也看不清楚了。我敢打赌解决方案非常简单。
请不要告诉我使用DateTimeImmutable
。这将从根本上改变我的整个结构,并要求我以非常不同的方式做事。也许我 应该 ,甚至 会 ,在某些时候,但现在,我只想修复我的这个罕见但仍然非常烦人的错误现有代码。 (如果可能的话,我非常相信是这样的。)
您能否考虑一个解决方案: 在时间戳字符串中(如 Thu, 21 Dec 2000 16:01:07 +0200),添加一个 timezone 标签,指定不区分夏令时的时区。
我能够重现您遇到的问题:
date_default_timezone_set('Pacific/Auckland');
// Daylight saving time 2020 in New Zealand began at 2:00am on Sunday, 27 September
$current = strtotime('2020-09-27 02:04:00');
$d1 = strtotime('2020-09-27 02:05:00', $current);
$d2 = strtotime('-5 minutes', $current);
var_dump($d1 > $d2); // false
var_dump(date('Y-m-d H:i:s', $d1)); // 2020-09-27 03:05:00
var_dump(date('Y-m-d H:i:s', $d2)); // 2020-09-27 03:59:00
此人似乎遇到了与您相同的问题,并且可能看起来是一个错误。 DateTime::modify and DST switch 解决方案是将日期转换为 UTC,然后进行比较:
// Convert to UTC and compare
$d1 = new \DateTime('2020-09-27 02:05:00', new \DateTimeZone('Pacific/Auckland'));
$d2 = new \DateTime('2020-09-27 02:04:00', new \DateTimeZone('Pacific/Auckland'));
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-5 minutes');
$d2->setTimezone(new \DateTimeZone('Pacific/Auckland'));
var_dump($d1 > $d2); // true
var_dump($d1->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T03:05:00.000+13:00
var_dump($d2->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T01:59:00.000+12:00
我已经更新了你的函数:
function time_is_older_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 < $d2;
}
function time_is_younger_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 > $d2;
}