#php
#php
Вопрос:
Вероятно, это просто я тупой, но…
<?php
$startDate = date_create('2019-11-01');
$endDate = date_create('2019-12-01');
$interval = date_diff($startDate, $endDate);
var_dump($interval);
?>
в PHPfiddle выдает (как и ожидалось):
object(DateInterval) #3 (15)
{ [«y»]=> int(0)
[«m»]=> int(1) <=== это то, что вызывает у меня проблемы на моем локальном компьютере
[«d»] => int(0)
[«h»]=> int(0)
[«i»]=> int (0)
[«s»]=> int(0)
[«weekday»]=> int(0)
[«weekday_behavior»]=> int(0)
[«first_last_day_of»]=> int(0)
[«invert»]=> int(0)
[«days»]=> int(30)
[«special_type»]=> int(0)
[«special_amount»]=> int(0)
[«have_weekday_relative»]=> int(0)
[«have_special_relative»]=> int(0) }
Проверьте значение m
, которое является количеством месяцев.
Однако на моем локальном компьютере тот же код выдает:
class DateInterval#4 (16) {
public $y =>
int(0)
public $m =>
int(0) <====== why ??!!
public $d =>
int(30)
public $h =>
int(0)
public $i =>
int(0)
public $s =>
int(0)
public $f =>
double(0)
public $weekday =>
int(0)
public $weekday_behavior =>
int(0)
public $first_last_day_of =>
int(0)
public $invert =>
int(0)
public $days =>
int(30)
public $special_type =>
int(0)
public $special_amount =>
int(0)
public $have_weekday_relative =>
int(0)
public $have_special_relative =>
int(0)
}
Хорошо, я признаю, что это 30 дней, но ответ, который я искал, был 1 месяц и $interval->m
равен нулю0.
Чего мне не хватает? Я не знаю, какую версию PHP использует веб-сайт PHPfiddle, но я использую 7.3.11 локально.
Комментарии:
1. PHPFiddle работает 5.6.40
2. Я бы сказал, что вы нашли ошибку в более новом PHP. Разница между датами с двумя разными значениями месяца должна возвращать интервал с ненулевым полем месяцев…
3. FWIW, обозначающий любую временную дельту в «месяцах», нарушается по определению, поскольку нет единого определенного значения для длины месяца … 🙂 Не ваша вина, в основном ошибка PHP…
4. Теперь это не так. Календарный месяц не является постоянной длиной, но он используется повсеместно в повседневной жизни. Разница между одной и той же датой в последовательных месяцах — это разница в месяц, будь то 28, 29, 30 или 31 день. Возможность вычислять дельты в терминах этих разбитых значений, возвращаемых, в
date_diff
основном полезна именно потому , что календарные месяцы (и годы) не переводятся в фиксированное количество дней; если бы все месяцы были одинаковой длины, вы могли бы просто получить дельту в днях и разделить.5. @deceze: я изучаю календари как хобби; вы не говорите мне ничего, чего я не знаю. 🙂 Я просто говорю, что в повседневной жизни у нас есть контракты, интервалы выставления счетов за аренду и кредитную карту и другие вещи, которые указаны в месяцах. Крайние случаи будут крайними случаями, но утилита все еще существует.
Ответ №1:
Вероятно, разница в часовом поясе.
попробуйте это:
<?php
date_default_timezone_set('UTC');
$startDate = date_create('2019-11-01');
$endDate = date_create('2019-12-01');
$interval = date_diff($startDate, $endDate);
var_dump($interval);
/*
object(DateInterval)#3 (16) {
["y"]=>
int(0)
["m"]=>
int(1)
...
*/
также попробуйте это:
<?php
$startDate = date_create('2019-11-01 23:00');
$endDate = date_create('2019-12-01 23:00');
$interval = date_diff($startDate, $endDate);
var_dump($interval);
/* outputs the same ["m"]=> int(1) as above */
Вероятно, date_diff ищет разницу между меткой времени UTC и часовым поясом по умолчанию в вашем PHP, установленным на некоторое положительное значение.
Допустим, у вас есть некоторый часовой пояс по умолчанию, который равен UTC 02:00, поэтому date_create('2019-11-01')
фактически устанавливается дата 2019-10-31 22:00:00 (UTC) и date_create('2019-12-01')
фактически устанавливается дата 2019-11-30 22:00:00 (UTC)
теперь вы можете видеть, что у них нет разницы в целый месяц.
Но вы можете уловить еще один забавный эффект:
<?php
//assuming your timezone is UTC something
$startDate = date_create('2019-10-31'); // creates a date 2019-10-30 XX:00 UTC
$endDate = date_create('2019-12-01'); // creates a date 2019-11-30 XX:00 UTC
$interval = date_diff($startDate, $endDate);
var_dump($interval);
/*
object(DateInterval)#3 (16) {
["y"]=>
int(0)
["m"]=>
int(1) sic(!)
["d"]=>
int(0) sic(!)
*/
Также у вас может быть неожиданный результат, если в вашем часовом поясе установлен переход на летнее время.
<?php
// Germany has the daylight saving shift at last Sunday of March and October.
date_default_timezone_set('Europe/Berlin');
$startDate = date_create('2020-03-28 03:00');
$endDate = date_create('2020-03-29 02:00');
$interval = date_diff($startDate, $endDate);
var_dump($interval); // ["d"]=> int(1) ["h"]=> int(0)
$startDate = date_create('2020-03-28 03:00');
$endDate = date_create('2020-03-29 03:00');
$interval = date_diff($startDate, $endDate);
var_dump($interval); // ["d"]=> int(1) ["h"]=> int(0)
$startDate = date_create('2020-03-28 03:00');
$endDate = date_create('2020-03-28 03:00'); // the same
$endDate->modify(' 23 hour');
$interval = date_diff($startDate, $endDate);
var_dump($interval); // ["d"]=> int(1) ["h"]=> int(0)
Для PHPFiddle по умолчанию установлен часовой пояс UTC, поэтому по умолчанию он дает разницу в один месяц.
Чтобы избежать этого, просто выполните все вычисления даты в UTC с самого начала:
<?php
$timeZone = new DateTimeZone('UTC');
$startDate = new DateTime('2019-10-31', $timeZone);
$endDate = new DateTime('2019-12-01', $timeZone);
$interval = date_diff($startDate, $endDate);
var_dump($interval);
Комментарии:
1. Я посмеялся над (тем, что я считал) вашей наивностью, когда прочитал «вероятно, часовой пояс», потому что у них обоих один и тот же часовой пояс. И тогда у меня отвисла челюсть, потому что добавление `
date_default_timezone_set('UTC');
действительно привело к разнице в один месяц. Я не утверждаю, что понимаю это, но спасибо за ответ 🙂
Ответ №2:
Да, это ошибка и связана с часовым поясом вашего сервера. Вы можете использовать это обходное решение:
$startDate = date_create('2019-11-01 UTC');
$endDate = date_create('2019-12-01 UTC');
$interval = date_diff($startDate, $endDate);
echo $interval->m; //1
Ответ №3:
Похоже, вы столкнулись с ошибкой в PHP 7.3.11, которой не было в более старой версии, используемой PhpFiddle. Также исправлена последняя стабильная версия, 7.4.11, поэтому вам следует просто обновить локальный PHP.
(Я заметил, что 7.3.11 — это то, что поставляется с macOS; если вы используете Mac, вы можете установить более новый PHP с помощью Homebrew.)
Комментарии:
1. Я обновил (XAMPP) до версии 7.4.11 и все равно получаю тот же результат.
2. Странно, должно быть, у нас разные часовые пояса. Я получил то же поведение, что и вы в 7.3.11, но получил ожидаемую 1-месячную дельту в 7.4.11. Но, похоже, вы все равно получили свой ответ, так здорово!
3. Нет, часовые пояса идентичны. Я могу
var_dump()
, если вы хотите. Оба в TZ 3 (Берлин) — что меня озадачивает — вдвойне, когда работает UTC (upvote)4. Нет, я не ставил под сомнение, что ваши часовые пояса идентичны. Я просто предположил, что причина, по которой вы все еще видите ошибку в 7.4.11, а я нет, возможно, потому, что вы и я находимся в разных часовых поясах. Но разве Берлин не UTC 2 (скоро будет UTC 1)? Что означает «TZ 3»?
5. Когда я оцениваю, я получаю
$startDate = date : 2019-11-01 00:00:00.000000, timezone : Europe/Berlin, timezone_type : 3
, поэтому я предполагаю, что я был неправ там: timezone_type (что бы ни означало 3). В любом случае, спасибо за ответ