PHP и mySQL: 2038 год Ошибка: что это такое? Как это решить?

116

Я думал об использовании TIMESTAMP для хранения даты и времени, но прочитал, что на него есть ограничение до 2038 года. Вместо того, чтобы задавать вопрос сразу, я предпочел разбить его на мелкие части, чтобы новичкам было легко понять. Итак, мой вопрос (ы):

  1. В чем именно проблема 2038 года?
  2. Почему это происходит и что происходит, когда это происходит?
  3. Как решить эту проблему?
  4. Есть ли альтернативы его использованию, которые не создают аналогичной проблемы?
  5. Что мы можем сделать с существующими приложениями, использующими TIMESTAMP, чтобы избежать так называемой проблемы, когда она действительно возникает?

Заранее спасибо.

Devner
источник
1
Осталось 28 лет. Вы все еще используете какие-либо компьютерные технологии 1982 года? Вряд ли. Так что не волнуйтесь, потому что к 2038 году это, вероятно, больше не будет проблемой.
Гордон
24
Гордон, я делаю приложение, которое делает прогноз. Я откладываю то, сколько денег у меня есть каждый месяц, а затем могу прикинуть, когда стану миллионером. В моем случае 28 лет - это не так много, и я уверен, что я не единственный, у кого сейчас есть эта проблема (я решил ее, используя 64-битные числа для представления метки времени).
Эмиль Викстрём,
2
@Emil Используя 64-битные целые числа прямо сейчас , вы нашли простое решение конкретной проблемы. Не применимо (или необходимо) всем, но работает для вашего варианта использования. Дело в том, что если у OP нет конкретной проблемы, такой как прогнозирование, то это может быть интересная тема, но ему не о чем беспокоиться, потому что решение этой проблемы на общем уровне не является проблемой PHP (обратите внимание на тег). Просто мой 2с.
Гордон
1
К 2038 году разбор строки «ГГГГ-ММ-ДД ЧЧ: ММ: СС: мммм ...» станет самой дешевой операцией, о которой вы только можете мечтать. К 2038 году 32-битная версия устареет. Я сомневаюсь, что временная метка Unix в том виде, в каком мы ее знаем, тогда будет существовать, и если это произойдет, наши 256-битные системы будут обрабатывать даты, выходящие далеко за рамки возраста, когда 4096-битные системы выдаются в виде счастливых обедов.
Super Cat
8
Гордон, 9 лет спустя. TIMESTAMPS все еще используются. Мы до сих пор используем технологии 28-летней давности. Это называется всемирной паутиной.
sfscs

Ответы:

149

Я пометил это как вики сообщества, так что не стесняйтесь редактировать на досуге.

В чем именно проблема 2038 года?

"Проблема 2038 года (также известная как ошибка тысячелетия Unix, 2000 год по аналогии с проблемой 2000 года) может привести к сбою некоторого компьютерного программного обеспечения до или в 2038 году. Проблема затрагивает все программное обеспечение и системы, которые хранят системное время в виде подписанного 32 -битовое целое число и интерпретировать это число как количество секунд с 00:00:00 UTC 1 января 1970 г. "


Почему это происходит и что происходит, когда это происходит?

Время после 03:14:07 UTC во вторник, 19 января 2038 г., будет «циклически» и сохранено внутри как отрицательное число, которое эти системы будут интерпретировать как время 13 декабря 1901 г., а не 2038 г. Это связано с тем, что тот факт, что количество секунд с эпохи UNIX (1 января 1970 00:00:00 по Гринвичу) превысит максимальное значение компьютера для 32-битного целого числа со знаком.


Как решить эту проблему?

  • Используйте длинные типы данных (достаточно 64 бита)
  • Для MySQL (или MariaDB), если вам не нужна информация о времени, подумайте об использовании DATEтипа столбца. Если вам нужна более высокая точность, используйте DATETIMEвместо TIMESTAMP. Помните, что DATETIMEстолбцы не хранят информацию о часовом поясе, поэтому ваше приложение должно будет знать, какой часовой пояс использовался.
  • Другие возможные решения описаны в Википедии
  • Подождите, пока разработчики MySQL исправят эту ошибку, о которой сообщалось более десяти лет назад.

Есть ли альтернативы его использованию, которые не создают аналогичной проблемы?

По возможности старайтесь использовать большие типы для хранения дат в базах данных: достаточно 64-битных - длинный длинный тип в GNU C и POSIX / SuS, или sprintf('%u'...)в PHP или в расширении BCmath.


Какие варианты использования потенциально ломают, хотя мы еще не в 2038 году?

Таким образом, MySQL DATETIME имеет диапазон 1000-9999, но TIMESTAMP имеет диапазон только 1970-2038. Если ваша система хранит даты рождения, будущие даты вперед (например, 30-летнюю ипотеку) или что-то подобное, вы уже столкнетесь с этой ошибкой. Опять же, не используйте TIMESTAMP, если это будет проблемой.


Что мы можем сделать с существующими приложениями, использующими TIMESTAMP, чтобы избежать так называемой проблемы, когда она действительно возникает?

В 2038 году будет мало приложений PHP, хотя это трудно предвидеть, поскольку Интернет еще не является устаревшей платформой.

Вот процесс изменения столбца таблицы базы данных для преобразования TIMESTAMPв DATETIME. Все начинается с создания временного столбца:

# rename the old TIMESTAMP field
ALTER TABLE `myTable` CHANGE `myTimestamp` `temp_myTimestamp` int(11) NOT NULL;

# create a new DATETIME column of the same name as your old column
ALTER TABLE `myTable` ADD `myTimestamp` DATETIME NOT NULL;

# update all rows by populating your new DATETIME field
UPDATE `myTable` SET `myTimestamp` = FROM_UNIXTIME(temp_myTimestamp);

# remove the temporary column
ALTER TABLE `myTable` DROP `temp_myTimestamp`

Ресурсы

Кори Баллоу
источник
2
Потрясающие. Для меня ваш ответ наиболее полный. Большое спасибо.
Devner 06
7
В MySQL, если будущая версия изменит базовый тип данных хранилища TIMESTAMP на 64-битный, тогда нет необходимости менять код на DATETIME, верно? Я просто не думаю, что отговаривать кого-либо от использования TIMESTAMP - это правильно, потому что этот тип данных имеет свое предназначение.
pixelfreak 04
1
Подписанное поле MySQL BIGINT похоже, что оно отлично подойдет для хранения временных меток, и я провел несколько локальных тестов MySQL 5.5, которые подтверждают его работу. Очевидно, что использование подписанного было бы лучше, чем беззнаковое, поскольку вы также можете представлять даты в прошлом. Есть ли причина не использовать BIGINT для отметок времени?
zuallauz
Следует отметить, что MySQL имеет только автоматическую настройку на текущую дату / время (CURRENT_TIMESTAMP) для полей меток времени. Надеюсь, эта функция в конечном итоге будет перенесена на все типы дат.
Ray
5
Совершенно абсурдно, что MySQL (и MariaDB) не используют 64-битные целые числа для хранения временных меток в 64-битных системах. Использование DATETIME не является адекватным решением, потому что мы не имеем представления о часовом поясе. Об этом сообщалось еще в 2005 году , но до сих пор нет исправления.
Майк
14

При использовании временных меток UNIX для хранения дат вы фактически используете 32-битные целые числа, которые подсчитывают количество секунд с 1970-01-01; см. Unix Time

Это 32-битное число переполнится в 2038 году. Это проблема 2038 года.


Чтобы решить эту проблему, вы не должны использовать 32-битную временную метку UNIX для хранения ваших дат - это означает, что при использовании MySQL вы не должны использовать TIMESTAMP, но DATETIME(см. 10.3.1. Типы DATETIME, DATE и TIMESTAMP ):

DATETIMEТипа используется , когда вам нужно значение , которые содержат как информацию о дате и времени. Поддерживаемый диапазон - '1000-01-01 00:00:00'до '9999-12-31 23:59:59'.

Тип TIMESTAMPданных имеет диапазон от '1970-01-01 00:00:01'UTC до '2038-01-19 03:14:07'UTC.


(Вероятно) лучшее , что вы можете сделать для вашего приложения , чтобы избежать / исправить эту проблему, чтобы не использовать TIMESTAMP, но DATETIMEдля столбцов , которые должны содержать даты, которые не являются в период между 1970 и 2038 гг .

Однако одно небольшое замечание: очень высока вероятность (с точки зрения статистики), что ваше приложение будет несколько раз переписано до 2038 года ^^ Так что, возможно, если вам не придется иметь дело с датами в будущем , вам не придется решать эту проблему с текущей версией вашего приложения ...

Паскаль МАРТИН
источник
1
+1 Для информации. Здесь делается попытка адаптировать передовой опыт с самого начала, чтобы в дальнейшем не беспокоиться о проблеме. Поэтому, хотя на данный момент 2038 год может не казаться проблемой, я просто хочу следовать передовым методам и следовать им для каждого приложения, которое я создаю, с самого начала. Надеюсь, это имеет смысл.
Devner 06
Да, в этом есть смысл, я понимаю вашу точку зрения. Но «передовой опыт» также может означать «то, что отвечает потребностям» - если вы знаете, что метки времени будет достаточно, нет необходимости использовать дату и время (например, для хранения требуется больше памяти; и это может иметь значение, если у вас миллионы записей) ;; У меня есть приложения, в которых я использую метку времени для некоторых столбцов (например, столбцов, содержащих текущую дату) и datetime для некоторых других (например, столбцов, содержащих прошлые / будущие даты)
Паскаль МАРТИН
Я вижу, что ваша процедура также имеет смысл и хорошо работает, особенно когда речь идет о пространстве для хранения. Если все в порядке, могу я спросить, какую структуру и длину вы обычно используете для столбца TIMESTAMP в своих приложениях? Спасибо.
Devner 06
Что ж, когда я хочу использовать TIMESTAMP, это для / потому что мои данные "дата / время" подходят между 1970 и 2038 годами - и поэтому я использую тип данных MySQL TIMESTAMP.
Паскаль МАРТИН
Спасибо за информацию. Из вашего ответа я понимаю, что достаточно просто объявить столбец как TIMESTAMP, и нам не нужно указывать для него какую-либо длину (в отличие от int или var, где мы указываем длину). Я прав?
Devner 06
7

Быстрый поиск в Google поможет: проблема 2038 года

  1. Проблема 2038 года (также известная как ошибка тысячелетия в Unix, 2000 год по аналогии с проблемой 2000 года) может привести к сбою некоторых компьютерных программ до или в 2038 году.
  2. Проблема затрагивает все программное обеспечение и системы, которые хранят системное время как 32-битное целое число со знаком и интерпретируют это число как количество секунд с 00:00:00 UTC 1 января 1970 года. Самое позднее время, которое может быть представлено таким образом - 03:14:07 UTC во вторник, 19 января 2038 года. Время после этого момента будет "зацикливаться" и сохраняться внутри как отрицательное число, которое эти системы будут интерпретировать как дату 1901 года, а не 2038 года.
  3. Нет простого решения для этой проблемы для существующих комбинаций ЦП / ОС, существующих файловых систем или существующих форматов двоичных данных.
Рубенс Фариас
источник
2

http://en.wikipedia.org/wiki/Year_2038_problem содержит большую часть деталей.

В итоге:

1) + 2) Проблема в том, что многие системы хранят информацию о дате как 32-битное целое число со знаком, равное количеству секунд с 01.01.1970. Самая последняя дата, которая может быть сохранена, как это, - 03:14:07 UTC во вторник, 19 января 2038 года. Когда это произойдет, int будет "зацикливаться" и сохраняться как отрицательное число, которое будет интерпретироваться как дата в 1901 году. Что именно тогда произойдет, варьируется от системы к системе, но достаточно сказать, что это, вероятно, не будет хорошо ни для одной из них!

Для систем, которые хранят только даты в прошлом, я думаю, вам не нужно беспокоиться некоторое время! Основная проблема связана с системами, которые работают с датами в будущем. Если ваша система должна работать с датами на 28 лет вперед, вам следует начать беспокоиться прямо сейчас!

3) Используйте один из доступных альтернативных форматов даты или перейдите на 64-битную систему и используйте 64-битные целые числа. Или для баз данных используйте альтернативный формат отметки времени (например, для MySQL используйте DATETIME)

4) Смотрите 3!

5) См. 4 !!! ;)

Аддси
источник
Ваши пункты 4 и 5 напоминают мне «Звонок по ссылке». Это вызвало у меня улыбку. Спасибо и +1 за то же самое.
Devner 06
1

Братья, если вам нужно использовать PHP для отображения временных меток, это ЛУЧШЕЕ решение PHP без изменения формата UNIX_TIMESTAMP.

Используйте функцию custom_date (). Внутри него используйте DateTime. Вот решение DateTime .

Пока у вас есть UNSIGNED BIGINT (8) в качестве меток времени в базе данных. Если у вас PHP 5.2.0 ++

правый
источник
-1

Поскольку я не хотел ничего обновлять, я попросил свой сервер (MSSQL) выполнить эту работу вместо PHP!

$qry = "select DATEADD(month, 1, :date) next_date ";
$rs_tmp = $pdo->prepare($qry);
$rs_tmp->bindValue(":date", '2038/01/15');
$rs_tmp->execute();
$row_tmp = $rs_tmp->fetch(PDO::FETCH_ASSOC);

echo $row_tmp['next_date'];

Возможно, это не эффективный способ, но он работает.

Венкатеш Г.С. Рао
источник