php strtotime и часовые пояса не работают должным образом - PullRequest
0 голосов
/ 05 января 2019

Наличие неожиданных результатов при использовании strtotime с часовым поясом и расчетами. Мой PHP установлен на UTC. strtotime("-6 days 00:00:00 America/Chicago");, кажется, дает неожиданные результаты. Я подумал, что, предоставив часовой пояс, он будет «вычислен в этом часовом поясе», но это не так.

В моем примере я ожидаю, что результаты будут одинаковыми, независимо от часового пояса PHP по умолчанию, поскольку в strtotime используется «Америка / Чикаго». Вместо этого он, кажется, берет текущий день (согласно часовому поясу по умолчанию) и вычитает дни, а затем вычисляет 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

Я сделал песочницу, чтобы воссоздать эту проблему постоянно здесь . Разница заключается в том, что временная метка предоставляется вместо использования time(), чтобы показать, как часовой пояс по умолчанию влияет на результаты в зависимости от времени текущего дня.

Ответы [ 3 ]

0 голосов
/ 05 января 2019

Прежде всего, давайте начнем с объяснения нескольких небольших вещей:

  • метки времени Unix всегда в UTC
  • Отметка времени, которую вы указали 1546578000 в формате ISO 8601: " 2019-01-04 05: 00: 00.000000 ", очевидно, UTC + 00: 00
  • CST отстает от UTC на шесть часов, то есть UTC-06: 00
  • Ваша функция 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

0 голосов
/ 05 января 2019

С помощью @Dharman, указывающего, как strtotime на самом деле обрабатывает вещи (что я все еще считаю неправильным), я смог изменить несколько вещей и получить ожидаемые результаты независимо от часового пояса php по умолчанию. Это обеспечивает ожидаемые результаты ... где вычисления (вычитание дней и установка 00:00:00) происходят в указанном часовом поясе.

пример песочницы

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   
0 голосов
/ 05 января 2019

Часовой пояс в строке окончательно учитывается:

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"
...