Решение проблемы 2038 года на PHP 5.2
19 января 2038 года в 03:14:07 по Всемирному времени, в приложениях, работающих под 32-битной версией PHP, может произойти сбой. В этот момент, стандартные функции работы с датой и временем перестанут корректно обрабатывать текущее время.
Все это связано с тем, что для представления даты и времени используется целое число. Данное число представляет собой количество секунд прошедших с начала эпохи UNIX, а именно с полночи 1 января 1970 года по Всемирному времени. В 32-битной версии PHP для хранения этого числа используется 32-битное целое со знаком. Максимально возможным значением для такого типа является 2 147 483 647, которое соответствует указанной критической дате.
Существует большая степень вероятности того, что к 2038 году все функционирующие системы и PHP-интерпретаторы будут 64-битные. Такое изменение, даст возможность беспрепятственно работать с датой примерно на 290 миллиардов лет вперед. Однако возникает сомнение, что это произойдет к 2038 году.
Еще одной проблемой 2038 года является невозможность работы с датами в прошлом и в будущем, уже сейчас. Под данное ограничение подпадают даты находящиеся до 1902 и после 2037 года.
Пути решения
Существует 2 способа решения данной проблемы:
- Не делать ничего, т.к. к 2038 году проблема будет решена естественным образом;
- Использовать встроенный класс «DateTime».
Первый способ полностью себя исчерпывает и, несмотря на свою простоту, является недальновидным.
Второй способ, напротив, имеет право на существование. О нем и пойдет речь далее.
Класс «DateTime»
Класс «DateTime», появившийся в PHP 5.2, призван полностью решить проблему 2038 года. Однако, программистам, привыкшим к стандартным функциям обработки даты и времени, работать с этим классом будет неудобно. Для ускорения процесса разработки приложений лучше всего использовать функции являющиеся интерфейсом к классу «DateTime».
Чтобы упростить жизнь программистов, я написал несколько аналогов часто используемых функций работы с датой и временем. Стоит заметить, что входные параметры и поведение функций не идентично встроенным аналогам.
Функция получения текущей метки времени Unix
// Улучшенная функция «time» // (функция возвращает текущую метку времени) // function enhanced_time() { $DateTime_obj = new DateTime(); return $DateTime_obj->format("U"); }
Функции вывода даты и времени в определенном формате
// Улучшенная функция «date» // (функция форматирует системную дату/время) // function enhanced_date($format_str, $timestamp = NULL, $timezone = NULL) { // // Явно объявить переменные // settype($format_str, "string"); // Формат, в котором следует выводить дату и время // // Если не указана метка времени - использовать текущую метку времени // if (is_null($timestamp)) { $timestamp = "now"; } else // Иначе - использовать переданную метку времени { $timestamp = "@" . number_format($timestamp, 0, ".", ""); } // // Если не указан часовой пояс - использовать часовой пояс по умолчанию // if (!is_string($timezone)) { $timezone = date_default_timezone_get(); } // // Создать экземпляр класса DateTime с необходимыми параметрами // $DateTime_obj = new DateTime($timestamp); $DateTime_obj->setTimezone(new DateTimeZone($timezone)); // // Вернуть дату и время в указанном формате // return $DateTime_obj->format($format_str); } // // Улучшенная функция «gmdate» // (функция форматирует системную дату/время по Гринвичу) // function enhanced_gmdate($format_str, $timestamp = NULL) { return enhanced_date($format_str, $timestamp, "UTC"); }
Функция получения информации о метке времени Unix
// Улучшенная функция «getdate» // (функция возвращает информацию о дате/времени) // function enhanced_getdate($timestamp = NULL, $timezone = NULL) { // // Получить информации о дате/времени в виде неассоциативного массива // $arr = explode("=", enhanced_date("s=i=H=d=w=m=Y=z=l=F=U", $timestamp, $timezone)); // // Вернуть информацию о дате/времени в виде ассоциативного массива // return array( "seconds" => (int) $arr[0], "minutes" => (int) $arr[1], "hours" => (int) $arr[2], "mday" => (int) $arr[3], "wday" => (int) $arr[4], "mon" => (int) $arr[5], "year" => (int) $arr[6], "yday" => (int) $arr[7], "weekday" => $arr[8], "month" => $arr[9], 0 => $arr[10] ); }
Функции формирования метки времени Unix для заданной даты и времени
// Улучшенная функция «mktime» // (функция возвращает метку времени для заданной даты) // function enhanced_mktime($timezone = NULL, $hour = NULL, $minute = NULL, $second = NULL, $month = NULL, $day = NULL, $year = NULL) { // // Если не указан часовой пояс - использовать часовой пояс по умолчанию // if (!is_string($timezone)) { $timezone = date_default_timezone_get(); } // // Получить массив информации о времени по умолчанию // $default_datetime_arr = enhanced_getdate(NULL, $timezone); // // Заполнить значениями по умолчанию, параметры, которые не были переданы в функцию // if (is_null($hour)) { $hour = $default_datetime_arr["hours"]; } if (is_null($minute)) { $minute = $default_datetime_arr["minutes"]; } if (is_null($second)) { $second = $default_datetime_arr["seconds"]; } if (is_null($month)) { $month = $default_datetime_arr["mon"]; } if (is_null($day)) { $day = $default_datetime_arr["mday"]; } if (is_null($year)) { $year = $default_datetime_arr["year"]; } // // Изменить тип переменных при необходимости // settype($hour, "integer"); settype($minute, "integer"); settype($second, "integer"); settype($month, "integer"); settype($day, "integer"); settype($year, "integer"); // // Создать экземпляр класса DateTime с необходимыми параметрами // $DateTime_obj = new DateTime(); $DateTime_obj->setTimezone(new DateTimeZone($timezone)); $DateTime_obj->setDate($year, $month, $day); $DateTime_obj->setTime($hour, $minute, $second); // // Вернуть метку времени // return $DateTime_obj->format("U"); } // // Улучшенная функция «gmmktime» // (возвращает метку времени Unix для времени по Гринвичу) // function enhanced_gmmktime($hour = NULL, $minute = NULL, $second = NULL, $month = NULL, $day = NULL, $year = NULL) { return enhanced_mktime("UTC", $hour, $minute, $second, $month, $day, $year); }
Заключение
Не стоит забывать, что теперь, при использовании типичных функций, в обязательном порядке, следует указывать часовой пояс по умолчанию. Делать это необходимо следующим образом:
// Установить часовой пояс по умолчанию // date_default_timezone_set("Asia/Bangkok");
Также приведу несколько вариантов применения указанных функций:
// Получить и вывести текущую метку времени Unix // $timestamp_now = time(); echo $timestamp_now . " "; $timestamp_now_e = enhanced_time(); echo $timestamp_now_e . " "; // // Получить и вывести информацию о текущей метке времени Unix // echo "<pre>"; var_dump(getdate($timestamp_now)); var_dump(enhanced_getdate($timestamp_now_e)); echo "</pre>"; // // Вывести текущую дату и время в указанном формате // echo date(«Y-m-d H:i:s», $timestamp_now). " "; echo enhanced_date(«Y-m-d H:i:s», $timestamp_now_e). " "; // // Получить и вывести метку времени Unix для даты и времени в далеком будущем // $year = 2050; $month = 3; $day = 18; $hour = 22; $minute = 56; $second = 53; $timestamp_future = mktime($hour, $minute, $second, $month, $day, $year); echo $timestamp_future. " "; $timestamp_future_e = enhanced_mktime(NULL, $hour, $minute, $second, $month, $day, $year); echo $timestamp_future_e. " "; // // Вывести дату и время в будущем в указанном формате // echo date(«Y-m-d H:i:s», «2531257013»). " "; echo enhanced_date(«Y-m-d H:i:s», «2531257013»). " ";
Ссылки
Проблема 2038 года
UNIX-время
Date/Time Функции
Класс «DateTime»
Список поддерживаемых часовых поясов