php strtotime和时区行为不符合预期

时间:2019-01-05 06:11:13

标签: php datetime strtotime

将strtotime与时区和计算一起使用时会产生意外结果。我的PHP设置为UTC。 strtotime("-6 days 00:00:00 America/Chicago");似乎给出了意外的结果。我认为,通过提供时区,可以在该时区中“计算”出来,但情况并非如此。

在我的示例中,无论PHP的默认时区为何,我都希望结果相同,因为strtotime中使用了“ America / Chicago”。取而代之的是,它似乎要花费当前日期(根据默认时区)并减去天数,然后在提供的时区中计算00:00:00。

我格式化错误吗?

我为混乱的代码表示歉意,但这只是一个例子,展示了我在说什么。

date_default_timezone_set("UTC");
echo date_default_timezone_get();
echo "</br>";
echo "</br>";

echo convertTS(time(), "America/Chicago");

echo "</br>";
echo "</br>";

$test = strtotime("-6 days 00:00:00 America/Chicago");

echo $test;

echo "</br>";

echo convertTS($test, "America/Chicago");

echo "</br>";

$test = strtotime("-7 days 00:00:00 America/Chicago");

echo $test;

echo "</br>";

echo convertTS($test, "America/Chicago");
echo "</br>";
echo "</br>";
echo "-----------------";
echo "</br>";
echo "</br>";

///now in America/Chicago

date_default_timezone_set("America/Chicago");
echo date_default_timezone_get();
echo "</br>";
echo "</br>";

echo convertTS(time(), "America/Chicago");

echo "</br>";
echo "</br>";

$test = strtotime("-6 days 00:00:00 America/Chicago");

echo $test;

echo "</br>";

echo convertTS($test, "America/Chicago");

echo "</br>";

$test = strtotime("-7 days 00:00:00 America/Chicago");

echo $test;

echo "</br>";

echo convertTS($test, "America/Chicago");

function convertTS($timestamp, $timezone)
{
    if( empty($timestamp) )
    {
        return 'n/a';
    }
    else
    {
        //output the time
        $dt = new DateTime('@'.$timestamp);
        $dt->setTimeZone(new DateTimeZone($timezone));
        return $dt->format('D, m/d/y @ g:i:s a T');
    }
}

我在运行时得到的上述结果:

UTC

Fri, 01/04/19 @ 11:39:15 pm CST

1546149600
Sun, 12/30/18 @ 12:00:00 am CST
1546063200
Sat, 12/29/18 @ 12:00:00 am CST

-----------------

America/Chicago

Fri, 01/04/19 @ 11:39:15 pm CST

1546063200
Sat, 12/29/18 @ 12:00:00 am CST
1545976800
Fri, 12/28/18 @ 12:00:00 am CST

我制作了一个沙盒来随时here来重新创建此问题。提供时间戳是不同的,而不是使用time()来显示默认时区如何根据当前时间影响结果。

3 个答案:

答案 0 :(得分:1)

首先,让我们开始解释一些小事情:

  • Unix时间戳始终采用UTC
  • 您以ISO 8601格式提供的 1546578000 时间戳为:“ 2019-01-04 05:00:00.000000 ”,显然是UTC + 00:00
  • CST比UTC(即UTC-06:00)晚6小时
  • 您的函数convertTS将始终返回与默认时区无关的相同结果
  • 减去多少天都没有关系,所以让我们简化一下,说-2天

调用convertTS函数时,它将采用UTC的时间戳,并以CST时区进行转换和显示。当您将1546578000作为参数传递时,您将获得“ 2019-01-03 23:00:00.000000”。

strtotime接受2个参数:int strtotime ( string $time [, int $now = time() ] )。但是PHP文档说,此函数的两个参数都使用默认时区,除非在该参数中指定了时区。在您的示例中,您每次都在第一个参数中明确指定时区。第二个参数读取默认时间戳,并在计算相对日期之前将UTC UNIX时间戳转换为该时区。

正在发生的事情的一些伪代码

当时区为UTC时:

date_default_timezone_set("UTC");
strtotime("-2 days 00:00:00 America/Chicago", 1546578000);
  1. 将1546578000转换为 UTC 2019-01-04 05:00:00.000000
  2. 将时间设置为00:00:00 CST:2019-01-04 06:00:00.000000 *UTC+00:00*
  3. 减去2天:2019-01-02 06:00:00.000000
  4. 将时间戳记返回为整数

默认时区为CST时:

date_default_timezone_set("America/Chicago");
strtotime("-2 days 00:00:00 America/Chicago", 1546578000);
  1. 将1546578000转换为 CST 2019-01-03 23:00:00.000000
  2. 将时间设置为00:00:00 CST:2019-01-03 06:00:00.000000 *UTC+00:00*
  3. 减去2天:2019-01-01 06:00:00.000000
  4. 将时间戳记返回为整数,当然是UTC + 00:00,请参见第一点

相对日期的PHP解析器并不完美。实际上,我认为将UNIX时间戳转换为默认时区是错误的……否则我的理解也可能有缺陷。

我如何收集数据:

我将代码更改为使用DateTime类。

date_default_timezone_set("UTC");

$dt = (new \DateTime())->setTimestamp(1546578000);
$dt->modify("-2 days 00:00:00 America/Chicago");
echo "UTC: ".$dt->format('D, m/d/y @ g:i:s a T');

echo "</br>";
echo "</br>";
echo "-----------------";
echo "</br>";
echo "</br>";

///now in America/Chicago
date_default_timezone_set("America/Chicago");

$dt = (new \DateTime())->setTimestamp(1546578000);
$dt->modify("-2 days 00:00:00 America/Chicago");
echo "CST: ".$dt->format('D, m/d/y @ g:i:s a T');

然后,我使用XDebug调试代码,并查看$dt变量的内部状态。
enter image description here

更改时区后:
enter image description here

答案 1 :(得分:0)

必须明确考虑字符串中的时区:

date_default_timezone_set('UTC');
var_dump(date('r', strtotime('-6 days 00:00:00 America/Chicago')));
var_dump(date('r', strtotime('-6 days 00:00:00 Europe/Madrid')));
string(31) "Sun, 30 Dec 2018 06:00:00 +0000"
string(31) "Sat, 29 Dec 2018 23:00:00 +0000"

也许令人困惑的是,结果是在配置的时区中显示

date_default_timezone_set('UTC');
var_dump(date('r', strtotime('-6 days 00:00:00 America/Chicago')));
date_default_timezone_set('Europe/Madrid');
var_dump(date('r', strtotime('-6 days 00:00:00 America/Chicago')));
string(31) "Sun, 30 Dec 2018 06:00:00 +0000"
string(31) "Sun, 30 Dec 2018 07:00:00 +0100"

...但实际时间相同:

date_default_timezone_set('UTC');
var_dump(strtotime('-6 days 00:00:00 America/Chicago'));
date_default_timezone_set('Europe/Madrid');
var_dump(strtotime('-6 days 00:00:00 America/Chicago'));
string(10) "1546149600"
string(10) "1546149600"

答案 2 :(得分:0)

在@Dharman的帮助下,指出strtotime如何实际处理事情(我仍然认为这是错误的),无论php的默认时区如何,我都可以修改一些内容并得出期望的结果。这样可以提供我期望的结果...在指定的时区中进行计算(减去天数并设置为00:00:00)。

sandbox example

date_default_timezone_set("UTC");

$time = time();
//$time = 1546578000;
$tz = "Europe/Amsterdam";

$dt = new DateTime('@'.$time);
$dt->setTimeZone(new DateTimeZone('UTC'));
echo $dt->format('D, m/d/y @ g:i:s a T');
echo "\n";
echo $dt->format('U');

echo "\n";
echo "\n";

$dt = new DateTime('@'.$time);
$dt->setTimeZone(new DateTimeZone('UTC'));
$dt->modify("-2 days 00:00:00");
echo $dt->format('D, m/d/y @ g:i:s a T');
echo "\n";
echo $dt->format('U');

echo "\n";
echo "\n";
echo "---------------";
echo "\n";
echo "\n";

$dt = new DateTime('@'.$time);
$dt->setTimeZone(new DateTimeZone($tz));
echo $dt->format('D, m/d/y @ g:i:s a T');
echo "\n";
echo $dt->format('U');

echo "\n";
echo "\n";

$dt = new DateTime('@'.$time);
$dt->setTimeZone(new DateTimeZone($tz));
$dt->modify("-2 days 00:00:00");
echo $dt->format('D, m/d/y @ g:i:s a T');
echo "\n";
echo $dt->format('U');

在这种情况下将输出:

Sat, 01/05/19 @ 6:51:18 pm UTC
1546714278

Thu, 01/03/19 @ 12:00:00 am UTC
1546473600

---------------

Sat, 01/05/19 @ 7:51:18 pm CET
1546714278

Thu, 01/03/19 @ 12:00:00 am CET
1546470000