简体   繁体   English

php strtotime 和时区未按预期运行

[英]php strtotime and timezones not behaving as expected

Having unexpected results when using strtotime with timezone and calculations.将 strtotime 与时区和计算结合使用时出现意外结果。 My PHP is set to UTC.我的 PHP 设置为 UTC。 strtotime("-6 days 00:00:00 America/Chicago"); seems to be giving unexpected results.似乎给出了意想不到的结果。 I thought by providing the timezone it would be 'calculated IN' that timezone, but that doesn't appear to the case.我认为通过提供时区,它将在该时区中“计算”,但事实并非如此。

In my example I expect the results to be the same, regardless of PHP's default timezone because 'America/Chicago' is used in strtotime.在我的示例中,无论 PHP 的默认时区如何,我都希望结果相同,因为 strtotime 中使用了“美国/芝加哥”。 Instead it seems to take the current day (according to default timezone) and subtract days then calculate 00:00:00 in the timezone supplied.相反,它似乎采用当天(根据默认时区)并减去天数,然后在提供的时区中计算 00:00:00。

Am I formatting this wrong?我格式错了吗?

I apologize for the messy code, but it is just an example demonstrating what I am talking about.对于凌乱的代码,我深表歉意,但这只是一个演示我所谈论内容的示例。

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

result of the above at the time I ran it :我运行它时的上述结果:

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

I made a sandbox to recreate this issue at all times here .我在这里制作了一个沙箱来随时重现这个问题。 The difference being a timestamp is provided rather than using time() to show how the default timezone affects the results depending on time of the current day.不同之处在于提供时间戳而不是使用time()来显示默认时区如何根据当天的时间影响结果。

First of all, let's start by explaining few small things:首先,让我们从解释一些小事情开始:

  • Unix timestamps are always in UTC Unix 时间戳始终采用 UTC
  • The timestamp you supplied 1546578000 in ISO 8601 format is: " 2019-01-04 05:00:00.000000 ", obviously UTC+00:00您以 ISO 8601 格式提供1546578000的时间戳为:“ 2019-01-04 05:00:00.000000 ”,显然是 UTC+00:00
  • CST is six hours behind UTC ie UTC−06:00 CST 比 UTC 晚六个小时,即 UTC-06:00
  • Your function convertTS will always return the same results irrelevant of the default timezone您的函数convertTS将始终返回与默认时区无关的相同结果
  • It doesn't matter how many days you subtract, so let's make it simpler and say -2 days减去多少天并不重要,所以让我们简单点说 -2 天

When you call your convertTS function it takes the timestamp in UTC and converts and displays it in CST timezone.当您调用convertTS函数时,它会采用 UTC 中的时间戳,并在 CST 时区中转换和显示它。 When you pass 1546578000 as an argument you will get "2019-01-03 23:00:00.000000".当您将 1546578000 作为参数传递时,您将得到“2019-01-03 23:00:00.000000”。

strtotime takes in 2 parameters: int strtotime ( string $time [, int $now = time() ] ) . strtotime接受 2 个参数: int strtotime ( string $time [, int $now = time() ] ) However PHP documentation says that both parameters of this function use the default time zone unless a time zone is specified in that parameter.但是 PHP 文档说这个函数的两个参数都使用默认时区,除非在该参数中指定了时区。 In your example you explicitly give the timezone in the first parameter each time.在您的示例中,您每次都在第一个参数中明确给出时区。 The second parameter reads the default timestamp and converts the UTC UNIX timestamp into that timezone before the calculation of the relative date.第二个参数读取默认时间戳,并在计算相对日期之前将 UTC UNIX 时间戳转换为该时区。

Some pseudo code of what is happening一些正在发生的事情的伪代码

When the timezone is UTC:当时区为 UTC 时:

date_default_timezone_set("UTC");
strtotime("-2 days 00:00:00 America/Chicago", 1546578000);
  1. Convert 1546578000 to UTC : 2019-01-04 05:00:00.000000将 1546578000 转换为UTC2019-01-04 05:00:00.000000
  2. Set time to 00:00:00 CST: 2019-01-04 06:00:00.000000 *UTC+00:00*将时间设置为 00:00:00 CST: 2019-01-04 06:00:00.000000 *UTC+00:00*
  3. Subtract 2 days: 2019-01-02 06:00:00.000000减去 2 天: 2019-01-02 06:00:00.000000
  4. Return the timestamp as integer以整数形式返回时间戳

When the default timezone is CST:当默认时区为 CST 时:

date_default_timezone_set("America/Chicago");
strtotime("-2 days 00:00:00 America/Chicago", 1546578000);
  1. Convert 1546578000 to CST : 2019-01-03 23:00:00.000000将 1546578000 转换为CST2019-01-03 23:00:00.000000
  2. Set time to 00:00:00 CST: 2019-01-03 06:00:00.000000 *UTC+00:00*将时间设置为 00:00:00 CST: 2019-01-03 06:00:00.000000 *UTC+00:00*
  3. Subtract 2 days: 2019-01-01 06:00:00.000000减去 2 天: 2019-01-01 06:00:00.000000
  4. Return the timestamp as integer, of course in UTC+00:00, see the first point以整数形式返回时间戳,当然是UTC+00:00,看第一点

PHP parser for relative dates is not perfect.用于相对日期的 PHP 解析器并不完美。 In fact I believe it is wrong when it converts UNIX timestamp to the default timezone... or my understanding of it might also be flawed.事实上,我认为将 UNIX 时间戳转换为默认时区是错误的……或者我对它的理解也可能有缺陷。

How I gathered the data:我是如何收集数据的:

I changed the code to use DateTime class.我更改了代码以使用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');

I then used XDebug to debug the code and see what are the internal states of the $dt variable.然后我使用 XDebug 调试代码并查看$dt变量的内部状态。
在此处输入图片说明

And after changing the timezone:更改时区后:
在此处输入图片说明

The time zone in the string is definitively taken into account:字符串中的时区被明确考虑在内:

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"

Perhaps the confusing part is that the result is displayed in the configured time zone:也许令人困惑的部分是结果显示在配置的时区中:

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"

... but the actual moment in time is the same: ...但实际的时间是一样的:

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"

With the help of @Dharman pointing out how strtotime actually handles things (which I still think is wrong) I was able to modify a few things and come up with the results I expect regardless of php's default time zone.在@Dharman 指出 strtotime 实际处理事情的方式(我仍然认为这是错误的)的帮助下,我能够修改一些内容并得出我期望的结果,而不管 php 的默认时区如何。 This provides the results I expect... where the calculations (subtracting days and setting to 00:00:00) happen in the timezone specified.这提供了我期望的结果......计算(减去天数并设置为 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');

Which in this case would output :在这种情况下会输出:

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   

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM