Как хранить очень старые даты в базе данных?
Это не проблема, с которой я столкнулся, но представьте себе, что кто-то строит веб-сайт о средневековых временах и хочет хранить даты, как они это сделают?
Спецификация для MySQL DATE
говорит, что она не опустится ниже 1000 года. Что имеет смысл, когда формат YYYY-MM-DD
. Как вы можете хранить информацию о смерти Кеннета II из Шотландии в 995 году? Конечно, вы можете сохранить его как строку, но есть ли реальные варианты типа даты?
Ответы
Ответ 1
Фактически, вы можете хранить даты ниже 1000 года в MySQL, несмотря на то, что даже документация разъяснение:
mysql> describe test;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| birth | date | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
- вам все равно нужно вводить год в формате YYYY:
mysql> insert into test values (1, '0995-03-05');
Query OK, 1 row affected (0.02 sec)
mysql> select * from test;
+------+------------+
| id | birth |
+------+------------+
| 1 | 0995-03-05 |
+------+------------+
1 row in set (0.00 sec)
- и вы сможете работать с этим в качестве даты:
mysql> select birth + interval 5 day from test;
+------------------------+
| birth + interval 5 day |
+------------------------+
| 0995-03-10 |
+------------------------+
1 row in set (0.03 sec)
Что касается безопасности. Я никогда не сталкивался с ситуацией, когда это не будет работать в MySQL 5.x(это, по сути, не означает, что он будет работать на 100%, но, по крайней мере, он надежен с определенной вероятностью)
О датах BC (ниже Христа). Я думаю, что это просто - в MySQL нет способа сохранить отрицательные даты. То есть вам нужно будет хранить год отдельно как целое поле со знаком:
mysql> select '0001-05-04' - interval 1 year as above_bc, '0001-05-04' - interval 2 year as below_bc;
+------------+----------+
| above_bc | below_bc |
+------------+----------+
| 0000-05-04 | NULL |
+------------+----------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1441 | Datetime function: datetime field overflow |
+---------+------+--------------------------------------------+
1 row in set (0.00 sec)
Но я думаю, что в любом случае (ниже/выше года 0) лучше хранить даты в виде целых чисел в этом случае - это не будет полагаться на недокументированную функцию. Однако вам нужно будет работать с этими 3 полями не как даты (так что в некотором смысле это не решение вашей проблемы)
Ответ 2
Выберите dbms, который поддерживает то, что вы хотите сделать. Среди других бесплатных систем управления базами данных PostgreSQL поддерживает временной диапазон от 4713 до 294276 н.э.
Если вы разбиваете дату на отдельные столбцы на год, месяц и день, вам также нужно больше таблиц и ограничений, чтобы гарантировать, что значения в этих столбцах представляют фактические даты. Если эти столбцы позволяют хранить значение {2013, 2, 29}, ваша таблица будет разбита. Dbms, поддерживающий даты в вашем диапазоне, полностью избегает этой проблемы.
Другие проблемы, которые могут возникнуть в
- Неверная арифметика даты в датах, выходящих за пределы допустимого диапазона.
- Неправильное форматирование, специфичное для локали, для дат, находящихся за пределами допустимого диапазона.
- Удивительное поведение от функций даты и времени в датах, выходящих за пределы допустимого диапазона.
- григорианский странный календарь.
Григорианский календарный странность? В Великобритании, на следующий день после 2 сентября 1752 года - 14 сентября 1752 года. PostgreSQL документирует свое обоснование, игнорируя это следующим образом.
PostgreSQL использует юлианские даты для всех вычислений даты/времени. Это полезное свойство правильного расчета дат с 4713 г. до н.э. в будущем, используя предположение, что продолжительность года 365.2425 дней.
Соглашения о дате до 19-го века делают для интересного чтения, но недостаточно согласованы, чтобы гарантировать кодирование в дату/время обработчик.
Ответ 3
К сожалению, я считаю, что в настоящее время самый простой вариант - хранить год, месяц и день в отдельных полях с годом как smallint
.
Ответ 4
Для цитаты из http://dev.mysql.com/doc/refman/5.6/en/datetime.html
For the DATE and DATETIME range descriptions, "supported" means that although earlier values might work, there is no guarantee.
Итак, есть хорошее изменение, что более широкий диапазон будет работать при достаточно сконфигурированной установке MySQL.
Не используйте TIMESTAMP, который, как представляется, имеет отрицательный диапазон.
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.
Вот пример JavaScript, как далеко до эпохи UNIX (1) вы можете получить 2 ^ 36 секунд * -1000 (чтобы получить миллисекунды для Javascript).
d = new Date((Math.pow(2, 36) - 1) * -1000)
Sun May 13 -208 18:27:45 GMT+0200 (Westeuropäische Sommerzeit)
Поэтому я предлагаю хранить исторические даты как BIGINT относительно эпохи.
См. http://dev.mysql.com/doc/refman/5.6/en/integer-types.html для MxSQL 5.6.
(1)
epoch = new Date(0)
Thu Jan 01 1970 01:00:00 GMT+0100 (Westeuropäische Normalzeit)
epoch.toUTCString()
"Thu, 01 Jan 1970 00:00:00 GMT"