Когда дело доходит до отображения даты и времени, не существует единого формата, подходящего для любой ситуации. При таком широком международном и региональном различии форматирования и множестве различных ситуаций, в которых используются даты и время, каждый случай уникален — от подробных и лирических предложений, таких как понедельник 21 марта в 12:18 (EST)
, до загадочных и компактный выход как сегодня
.
Но когда дело доходит до программной работы с датами — хранения, сравнения и сортировки с ними — действительно нет необходимости в таком большом разнообразии форматов. Действительно, гораздо разумнее объединить использование программных дат и использовать один и тот же формат для всего .
Так какой формат лучше всего использовать?
Это должно было бы отметить несколько полей — думайте о них как о «обязательных» функциях:
- он хранит полную дату и время, предоставляя всю необработанную информацию, необходимую для создания любого другого формата
- мы можем использовать его в качестве входных данных для создания и преобразования дат — например. он должен быть совместим с аргументом timestamp
new Date
конструктораnew Date
в JavaScript - он всегда использует один и тот же фиксированный часовой пояс, чтобы обеспечить систему отсчета для сравнения
Помимо этих основных требований, есть несколько «полезных» функций:
- его формат изначально сортируется без дополнительного преобразования (т. е. если он используется в качестве имени файла, он создает хронологический порядок сортировки)
- у него нет внутреннего пробела — чтобы избежать разбора неоднозначностей, таких как чрезмерное непрерывное пространство или разница табуляции / пробела
- это обеспечивает, по крайней мере, некоторый уровень читабельности, главным образом ради программистов
- это должно дать нам хотя бы некоторую «бесплатную» информацию, т.е. такие значения, как год или час, которые легко получить при разборе строки, без необходимости каждый раз создавать и анализировать новые объекты даты
Как насчет часовых поясов?
На самом деле нет необходимости хранить даты и время в разных часовых поясах — каждая данная временная метка имеет UTC / GMT- эквивалент. Поэтому самое простое — записать все метки времени в формате UTC , преобразовав их в местное время только при выводе клиенту. Это дает нам общую основу для программного сравнения дат без потери информации, необходимой для интернационализации.
Поскольку JavaScript оценивается на клиенте, он использует часы клиента в качестве эталона времени. Таким образом, любая отметка времени, прошедшая через конструктор Date
автоматически преобразуется в местное время пользователя .
Моя окончательная рекомендация
В прошлом, пока я на самом деле не сел и, хотя это серьезно, я обычно использовал формат даты RFC 2822 (например, "Mon, 21 Mar 2011 00:18:56 +0000"
) — в основном потому, что его так легко генерировать в PHP (определяется символом форматирования "r"
), и он помечает все обязательные поля. Но это не изначально сортируемый и не тот выбор, к которому я в итоге пришел:
Моя окончательная рекомендация — ISO 8601 (2004) в UTC .
Существует несколько вариаций этого формата, и один из них, который я специально выбрал, использует разделитель даты и времени "T"
и указатель часового пояса "Z"
(который обозначает UTC ); другие варианты используют пробел в качестве разделителя или обозначают часовой пояс как "+00:00"
, но я не рекомендую ни один из них — пробел делает его несовместимым с new Date
в Firefox, и более длинный и более сложный указатель часового пояса просто ненужно.
Таким образом, отметки времени, о которых мы говорим, выглядят так: "2011-03-21T00:18:56Z"
Хотя этот формат генерировать не так просто, как другие (см. Раздел «Как использовать формат ISO» ниже), он помечает все остальные поля, как обязательные, так и потенциальные. И это также предоставляет ряд дополнительных преимуществ — бонусные функции, которые можно сказать, которые укрепляют его статус как идеальный выбор:
- это всегда постоянная длина
- этот формат изначально используется JSON (например, в JavaScript, если вы передаете объект
Date
черезJSON.stringify
, он преобразуется в этот формат) - это формат и профиль, рекомендованные W3C
- он совместим с
DATESTAMP
столбцаDATESTAMP
в SQL , хотя и с «расслабленным» синтаксисом
Проблема, обнаруженная мной в PHP и MySQL, заключается в том, что когда метка времени в этом формате хранится в столбце DATESTAMP
, его разделитель "T"
заменяется пробелом, а маркер "Z"
удаляется , преобразуя его в строгий. Формат DATESTAMP
. Но это не то, что мы хотим, и не приемлемо.
Все, что я делаю, чтобы это исправить — это переконвертирую вывод, но мне бы хотелось услышать о более постоянном решении — опции конфигурации MySQL или что-то еще?
Как использовать формат ISO
В PHP нет заранее определенной константы или токена для создания этого точного формата, хотя токен форматирования "c"
создает один очень похожий (тот же стандарт, другой вариант). Затем мы можем преобразовать различия с помощью замены строк; но я считаю, что самое простое, что нужно сделать, это просто скомпилировать его вручную из отдельных компонентов даты и времени, используя функцию gmdate для получения UTC :
$timestamp = gmdate('Ym-dTH:i:sZ');
После того, как у нас есть временная метка, мы можем использовать ее на клиенте в качестве аргумента временной метки для new Date
. Например, мы могли бы сначала вывести необработанную временную метку в статический HTML :
<dl class="listing"> <dt>Date created:</dt> <dd id="listing-timestamp"><?php echo gmdate('Ym-dTH:i:sZ'); ?></dd> </dl>
А затем используйте JavaScript, чтобы преобразовать его в переформатированную локальную дату и время, например:
var dd = document.getElementById("listing-timestamp"); dd.innerHTML = new Date(dd.innerHTML).toLocaleString();
Конечно, механизм передачи временных меток между клиентом и сервером сильно зависит от приложения. Сценарий PHP может выводить метку времени непосредственно в сгенерированный сервером JavaScript; это может быть ответ на запрос Ajax, или он может быть записан в статический HTML , как в примере выше. Как бы то ни было, наиболее распространенным вариантом использования, вероятно, являются сгенерированные сервером временные метки, которые выводятся клиенту для преобразования , и описанные мной процессы будут это делать. Но как насчет других случаев?
Возможно, вы работаете полностью на сервере и выводите только полностью отформатированные, готовые к использованию даты. PHP предоставляет множество способов внутреннего преобразования одной даты в другую, но наиболее простой способ — передать временную метку через strtotime (чтобы преобразовать ее в числовое значение), а затем использовать ее в качестве второго аргумента на сегодняшний день :
$rfcdate = date('r', strtotime($timestamp));
И наоборот, вы можете генерировать и конвертировать полностью на клиенте, и в этом случае вам потребуется способ создания метки времени в JavaScript. Следующая функция сделает работу:
function getTimestamp() { var dateobj = new Date(); dateobj.setTime(dateobj.getTime() + (dateobj.getTimezoneOffset() * 60000)); var datetime = { date : [ dateobj.getFullYear(), dateobj.getMonth() + 1, dateobj.getDate() ], time : [ dateobj.getHours(), dateobj.getMinutes(), dateobj.getSeconds() ] }; for(var key in datetime) { if(!datetime.hasOwnProperty(key)) { continue; } for(var i in datetime[key]) { if(!datetime[key].hasOwnProperty(i)) { continue; } var n = datetime[key][i]; datetime[key][i] = (n < 10 ? '0' : '') + n; } } return datetime.date.join('-') + 'T' + datetime.time.join(':') + 'Z'; }
Полученная временная метка может быть затем передана new Date
и оттуда обработана во время локали:
var timestamp = getTimestamp(); ... var rfcdate = new Date(timestamp).toString();