Решение проблемы 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»
Список поддерживаемых часовых поясов