Статьи

Дата JavaScript: плохая часть

Мы все знаем, что JavaScript имеет некоторые плохие стороны. Однако об одном из них обычно забывают и пропускают в большинстве списков «JavaScript Bad Parts» в Интернете. Это Dateобъект. Вам может быть трудно работать с ним, особенно если вы хотите поддерживать часовые пояса. В этом посте я хотел бы изложить причуды Dateв JavaScript.

основы

Чтобы создать Dateобъект, вы просто делаете:

var now = new Date();

Это создаст дату, установленную на сейчас.
Если вы хотите создать Dateв определенный день:

var year = 2015;
var month = 2-1; // February
var day = 27;
var now = new Date(year, month, day);

Здесь вы сталкиваетесь с первой причудой: месяцы пронумерованы от 0 до 11. Январь — 0, декабрь — 11.

Вы также можете указать часы, минуты, секунды и миллисекунды в качестве дополнительных параметров (здесь больше нет ошибок):

var year = 2015;
var month = 2-1; // February
var day = 27;
var hours = 14; // 2PM
var minutes = 56;
var seconds = 37;
var milliseconds = 997;
var now = new Date(year, month, day, hours, minutes, seconds, milliseconds);

Для более подробной информации о создании Date, проверьте Дата на MDN .

Форматирование

Если вы хотите отобразить дату, есть несколько доступных конвертеров. Вот наиболее часто используемые:

var date = new Date(2015, 1, 27, 14, 56, 37, 997);
console.log(date.toDateString());       // Fri Feb 27 2015
console.log(date.toTimeString());       // 14:56:37 GMT-0800 (Pacific Standard Time)
console.log(date.toLocaleDateString()); // 2/27/2015
console.log(date.toLocaleTimeString()); // 2:56:37 PM
console.log(date.toUTCString());        // Fri, 27 Feb 2015 22:56:37 GMT
console.log(date.toGMTString());        // Fri, 27 Feb 2015 22:56:37 GMT
console.log(date.toISOString());        // 2015-02-27T22:56:37.997Z
console.log(date.toJSON());             // 2015-02-27T22:56:37.997Z

Будьте осторожны с 3 и 4. Оба дают разные результаты, в зависимости от настроек компьютера пользователя и географического положения. Это может вызвать проблемы с пользовательским интерфейсом (некоторые форматы даты могут быть длинными).

Конвертеры 5 и 6 эквивалентны во всех самых популярных браузерах. Однако toGMTStringне рекомендуется.

Преобразователи 7 и 8 также эквивалентны. Кроме того, toJSONиспользуйте toISOStringвнизу, чтобы вернуть результат.

Нет существенных проблем с отображением даты, но нет способа отформатировать ее в произвольном формате (например, «ГГГГ-ММ-ДД ЧЧ: мм»), как в C # или Java.

Альтернативой для форматирования является объект Intl.DateTimeFormat из API интернационализации ECMAScript . Однако этот API еще не поддерживается старыми браузерами, большинством мобильных браузеров и Safari (проверьте совместимость браузера в MDN ).

анализ

Более сложным, чем отображение дат, является их анализ. Есть два распространенных способа анализа даты из строки:

var parsedDate1 = new Date("2015-02-27 22:56:37");
var parsedDate2 = Date.parse("2015-02-27 22:56:37");

Первый возвращает Dateобъект, второй — номер 1425106597000 (миллисекунды с 1 января 1970 года, 00:00:00 UTC).

В разборе есть одна очень неприятная деталь:

При заданной строке даты «7 марта 2014 года» parse () предполагает наличие местного часового пояса , но с учетом формата ISO, такого как «2014-03-07», будет принят часовой пояс UTC . Поэтому объекты Date, созданные с использованием этих строк, будут представлять разные моменты времени, если в системе не установлен местный часовой пояс UTC. Это означает, что две строки даты, которые выглядят эквивалентными, могут привести к двум различным значениям в зависимости от формата преобразуемой строки ( это поведение изменено в ECMAScript ed 6, так что обе будут рассматриваться как локальные ).

Источник:
MDN

Это означает, что date1и date2ниже (созданные из машины, расположенной в Сиэтле, штат Вашингтон) различаются:

var date1 = new Date("2014-01-02");
var date2 = new Date("1/2/2014");
console.log(date1); // Wed Jan 01 2014 16:00:00 GMT-0800 (Pacific Standard Time)
console.log(date2); // Thu Jan 02 2014 00:00:00 GMT-0800 (Pacific Standard Time)

Рекомендуемый формат разбора: YYYY/MM/DD— независимые локальные настройки пользователя. Он всегда предполагает смещение местного часового пояса для строки даты, заданной в качестве параметра

var date1 = new Date("2014/01/02");
console.log(date1); // Thu Jan 02 2014 00:00:00 GMT-0800 (Pacific Standard Time)

Часовые пояса

JavaScript- Dateобъект всегда предполагает локальное смещение часового пояса для машины, на которой он отображается. Что важно: JavaScript Dateне поддерживает часовые пояса, а только смещения часовых поясов. Это определение обоих из Википедии :

Часовой пояс является географическим регионом , где почти каждый замечает то же самое стандартное время. Смещение по времени является количество времени вычитают из или добавлены к UTC , чтобы получить текущее гражданское время — будь то стандартное время или Переход на летнее время.

Другими словами: часовой пояс является постоянным (пока правительство не меняло его) для некоторого места (например, Сан-Франциско) все время, в то время как смещение часового пояса изменяется (обычно, два раза в год — это то, что мы называем «дневной свет» экономия).

var myDate = new Date();
var timezoneOffset = myDate.getTimezoneOffset(); // result in minutes

Результат в минутах. Вот еще одна странность: часовые пояса, которые «западнее» от UTC (смещение = 0), имеют положительные значения (60–720), а часовые пояса, которые «восточнее» от UTC, имеют отрицательные значения (-60 — -780). Таким образом, стандартное тихоокеанское время (UTC- 8: 00 ) в JavaScript имеет смещение часового пояса 480 , а центрально-европейское время (UTC +1: 00 ) имеет смещение часового пояса -60 .

Еще один подводный камень: локальное смещение часового пояса всегда устанавливается в зависимости от значения Dateобъекта. Таким образом, для компьютера, расположенного в Сиэтле, штат Вашингтон:

var date1 = new Date(2015,1,20); // date in Pacific Standard Time (before timezone offset change)
var date2 = new Date(2015,2,20); // date in Pacific Daylight Time (after timezone offset change)
console.log(date1.getTimezoneOffset()); // 480
console.log(date2.getTimezoneOffset()); // 420

В этом случае ваше текущее «местное» смещение часового пояса (для времени, в которое вы создаете объект Date) не имеет значения.

Даже если вы создадите объект Date с датой до изменения времени, а затем измените его на дату после изменения времени — его смещение часового пояса изменится, чтобы отразить смещение для нового (текущего) значения:

var date = new Date(2015,1,20); // date in Pacific Standard Time
console.log(date.getTimezoneOffset()); // 480
date.setMonth(2); // change month so date is in Pacific Daylight Time now
console.log(date.getTimezoneOffset()); // 420

Moment.js

Moment.js — это библиотека JavaScript, предназначенная для работы на стороне клиента (в браузере) и на стороне сервера (в файле node.js). В нем много функций для разбора, отображения и манипулирования датами. Это делает работу с датами менее болезненной.

Часовой пояс Moment — библиотека, созданная поверх Moment.js — поддерживает зоны реального времени (не только смещения часовых поясов). Данные для часового пояса Момент поступают из базы данных часовых поясов IANA — самой популярной базы данных часовых поясов. Новые версии периодически выпускаются по мере изменения законов о часовых поясах в разных странах.

Подумайте об использовании Moment.js и Moment Timezone в вашем приложении, когда вы делаете более сложные операции с датами. Особенно, если вы хотите поддерживать часовые пояса.

Резюме

Действительно удивительно, что работа с датами в программировании все еще вызывает много проблем. Тем не менее, мы становимся лучше в этом. Чтобы получить исчерпывающий обзор даты / времени на языке программирования, я рекомендую Основы даты и времени от Мэтта Джонсона . Вы также можете проверить блог Мэтта Джонсона . Он пишет в основном о дате / времени в различных средах. Его статья JavaScript Тип даты ужасно испорчена, может быть хорошим дополнением к этому сообщению

Чтобы быстро понять, почему работать с часовыми поясами очень сложно, посмотрите это видео .