Даты в JavaScript сломаны. Кто их исправит?

Даты в JavaScript сломаны. Кто их исправит?

27 марта 2022 г.

Даты Javascript странные. Известно, что Брендан Эйх написал первую версию Javascript за 10 дней — и сама функция Date не стала исключением. Он основан на коде, который [в конечном итоге устарел в Java] (https://twitter.com/brendaneich/status/845826909061623808).


Это означает, что Javascript унаследовал функцию Date, которая была признана ошибочной и проблематичной в Java, оставив в ней множество проблем. Возможно, вы даже сами столкнулись с некоторыми проблемами. Вы можете задаться вопросом: «Что в этом такого странного?». Давайте рассмотрим все причуды и распространенные ловушки с конструктором Date Javascript, чтобы вы могли их избежать.


Javascript на самом деле не поддерживает даты


Это звучит нелогично, учитывая, что основной конструктор даты Javascript называется Date, но на самом деле Javascript не поддерживает даты. Javascript поддерживает только дату и время. Все даты Javascript внизу представляют собой временные метки Unix. Это означает, что если мы попытаемся создать дату, мы на самом деле создадим дату и время. Все даты Javascript без указания времени по умолчанию соответствуют полуночи данного дня.


```javascript


пусть дата = новая дата (2011, 1, 22);


// Обратите внимание, что к произведенной дате добавлено время:


// Вт, 22 февраля 2011 г., 00:00:00 GMT+0000 (время по Гринвичу)


Даты разбора


Парсинг дат, как мы сделали выше, работает нормально, если вы знаете, что месяцы начинаются с 0, но строки парсинга даты значительно различаются в разных браузерах. Настоятельно рекомендуется не анализировать строки даты. До спецификации ECMAScript 5 никогда не определялось, как Date анализирует строковые даты, и у разных браузеров есть много исторических причуд, которые делают его очень ненадежным.


В соответствии с текущей спецификацией только строки, соответствующие стандарту ISO-8601, должны анализироваться Javascript, а любые другие даты должны возвращать NaN, т.е.:


```javascript


пусть parseMyDate = Date.parse('2022-03-21T11:00:01+00:00');


Однако это не так. Многие браузеры позволяют анализировать дату за пределами этого формата. Здесь есть вероятность запутаться. Допустим, вы хотите проанализировать формат даты в стандартном формате dd/mm/yyyy. Вы берете стандартную дату и передаете ее в функцию parse():


```javascript


пусть myDate = новая дата («01.05.2020»);


console.log(мояДата);


Во всех современных браузерах используется формат даты в США, то есть мм/дд/гггг, что означает, что возвращается 1 мая, а не 5 января, что приводит к неожиданным результатам.


Даты синтаксического анализа по умолчанию в формате UTC


Предположим, у вас есть дата, с которой не связано ни время, ни часовой пояс:


```javascript


пусть myDate = Date.parse('01 января 1999');


console.log(мояДата);


Вы можете подумать, что в этом нет ничего сбивающего с толку — это представляет собой фиксированную дату во времени. Однако:


  • Если ваш часовой пояс UTC, это вернет 915148800000.

  • Если ваш часовой пояс UTC+3:00, будет возвращено 915138000000, то есть на 3 часа больше.

Если ваш часовой пояс UTC-5:00, это вернет 915166800000, то есть на 5 часов меньше.


Поэтому, если ваш часовой пояс западнее UTC, например, -5:00, Javascript вычитает 5 часов из временной метки Unix. Поскольку дни начинаются в полночь.


Это означает, что если мы попытаемся использовать эту метку времени с другим часовым поясом, например, в бэкэнд-системе, мы получим не 1 января 1999 года, а 31 декабря 1998 года! Все это потому, что Javascript не реализует даты — с каждой датой связано время — в данном случае полночь.


Месяцы начинаются с 0 в датах Javascript


Если мы хотим создать дату в Javascript, мы можем проанализировать числа, представляющие год, месяц и день. Например, если мы хотим создать дату 22 февраля 2011 года, мы напишем это, верно?


```javascript


пусть дата = новая дата (2011, 2, 22);


Только это дает нам «Вторник, 22 марта 2011 г., 00:00:00 по Гринвичу + 0000 (время по Гринвичу)». Это потому, что месяцы в Javascript начинают отсчет с 0, поэтому февраль равен 1, а не 2:


```javascript


пусть дата = новая дата (2011, 1, 22);


Неверные даты пропуска вперед


Допустим, вы случайно создали неправильную дату, скажем, 31 февраля 2022 года. Вы по ошибке передаете ее в свою функцию даты из базы данных или API:


```javascript


пусть дата = новая дата (2022, 1, 31);


Вы можете подумать, что это просто вернет «Invalid Date» или «NaN», но вы ошибаетесь. Javascript пропускается до 3 марта! Так как в феврале 2011 года всего 28 дней, и есть 3 дополнительных дня, эти дни прибавляются к концу месяца. Другими словами, вы не можете доверять дате, чтобы возвращать ошибки для всех неправильных дат.


Строки не преобразуются в числа


Самое странное поведение — это когда мы не передаем целые строки Javascript при синтаксическом анализе. Например:


```javascript


пусть myDate = новая дата («0»);


console.log(мояДата);


Вы можете подумать, что это вернет 0 год или, возможно, эпоху Unix, но на самом деле он возвращает 2000 год - «Сб, 01 января 2000 г., 00:00:00 по Гринвичу + 0000 (время по Гринвичу)».


Однако, что еще более странно, если мы попытаемся увеличить это число, оно начнет считаться месяцами:


```javascript


console.log(новая дата("5")); // Вт, 01 мая 2001 г., 00:00:00 GMT+0100 (британское летнее время)


console.log(новая дата("11")); // Чт, 01 ноября 2001 г., 00:00:00 GMT+0000 (время по Гринвичу)


console.log(новая дата("4")); // Воскресенье, 01 апреля 2001 г., 00:00:00 GMT+0100 (британское летнее время)


В довершение всего, если вы попытаетесь выполнить new Date("13"), в результате мы получим Invalid Date, поскольку 13-го месяца нет.


Число раз зависит от часового пояса


Если мы передаем только одно число в new Date(), оно будет рассматриваться как временная метка Unix, однако оно не корректируется для часового пояса. Например, в формате UTC следующий код возвращает «Чт, 01 января 1970 г., 00:00:00 по Гринвичу + 0000 (время по Гринвичу)»:


```javascript


console.log (новая дата (0));


Это имеет смысл, так как это эпоха Unix — однако, если мы находимся в UTC-5:00, этот код возвращает «31 декабря 1969 года, 19:00:00 по Гринвичу-0500 (восточное стандартное время)» — то есть 5 часов раньше. Это означает, что по умолчанию часовые пояса могут привести к большой путанице — если мы ожидаем, что дата будет 1 января 1970 года, у нас сразу же возникнет проблема при использовании такого метода, как Date().toLocaleString(). В конце концов, мы можем решить эту проблему, используя метод .toUTCString(), но это усложнение приводит к большой путанице.


Годы действительно непоследовательны


Вы могли подумать, что мы легко отделались, и нарушены только временные метки и часовые пояса, но даже годы непоследовательны. Если бы мы хотели создать дату 1 января в году 0, вы могли бы подумать, что мы напишем это:


```javascript


console.log (новая дата (0, 0, 0));


Поскольку месяцы начинаются с 0, это выглядит правильно, но на самом деле, если год меньше 100, 0 означает 1900 год. Хорошо, вы можете подумать, что это должно вернуть 1 января 1900 года.


вместо этого - но на самом деле это тоже неправильно - так как дни индексируются с 1, а не с 0. Вышеприведенный код возвращает "Sun 31 Dec 1899 00:00:00 GMT+0000 (Greenwich Mean Time)" - так как 0-й день месяца считается последним днем ​​предыдущего месяца. Вот еще несколько примеров:


```javascript


console.log (новая дата (0, 0, 0)); // Воскресенье, 31 декабря 1899 г., 00:00:00 GMT+0000 (время по Гринвичу)


console.log (новая дата (50, 0, 0)); // Сб, 31 декабря 1949 г., 00:00:00 GMT+0000 (время по Гринвичу)


console.log (новая дата (30, 0, 0)); // Вт, 31 декабря 1929 г., 00:00:00 GMT+0000 (время по Гринвичу)


console.log (новая дата (24, 0, 0)); // Пн, 31 декабря 1923 г., 00:00:00 GMT+0000 (время по Гринвичу)


Как только вы превысите 100 лет, он вернется к обычному подсчету лет. Таким образом, приведенный ниже код на самом деле дает нам 101 год, а не 2001 год:


```javascript


console.log (новая дата (101, 0, 0)); // Пт, 31 декабря 01:00 00:00:00 GMT-0001 (время по Гринвичу)


Это может быть полезно, если вы используете годы после 1900 года, но это невероятно нелогично для всего, что было раньше.


Почему никто не исправляет даты Javascript?


Функция Date в Javascript принципиально не работает во многих отношениях, поэтому большинство людей используют такие инструменты, как Moment.js, но почему это не было исправлено?


Основная причина в том, что большая часть Интернета была построена на коде, учитывающем недостатки Date. Таким образом, изменение сейчас приведет к тому, что многие веб-сайты просто сломаются.


Чтобы исправить эту ситуацию, Javascript представляет совершенно новый набор стандартов под названием Temporal, который будет занимать другое пространство имен, чем Date, и решит большинство проблем, описанных в этой статье. До тех пор мы застряли с причудами, которые производят Javascript Dates.


Также опубликовано здесь



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE