Решение проблемы 2038 года на PHP 5.2

14 мая 2011 г.

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 способа решения данной проблемы:

  1. Не делать ничего, т.к. к 2038 году проблема будет решена естественным образом;
  2. Использовать встроенный класс «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»
Список поддерживаемых часовых поясов

Теги: рубрика PHP
  • Похожие статьи
  • Предыдущие из рубрики