CODEGURU

Работаем с датой в javascript

Работа с датой в javascript, на первый взгляд кажется странной и запутанной. Настолько странной и запутанной, что появились даже отдельные библиотеки исключительно для работы с датой (moment.js, date-fns). Ничего не имею против них, но достаточно потратить пять минут, чтобы разобраться как устроен объект Data в javascript, чтобы понять, что в 90% случаев никакие дополнительные библиотеки вам не нужны. И для начала давайте разберёмся с часовыми поясами.

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

Поверхность земли разделена на 24 часовых пояса. В javascript же существует всего два часовых пояса:

  • Local Time — часовой пояс, в котором находится ваш компьютер
  • UTC (Coordinated Universal Time) — то же самое, что время по Гринвичу (GMT)

По умолчанию практически всегда объект Data в javascript вернёт вам локальное время в вашем часовом поясе. Для получения времени UTC необходимо явно это указать. Давайте же создадим новый объект Data.

Создаём дату

Для создания нового объекта даты достаточно команды new Date(). В неё обычно передают параметры в одном из четырёх видов:

  • Дата как один параметр-строка
  • Дата как набор параметров
  • Дата как timestamp
  • Без параметров

Дата как один параметр-строка

При таком способе мы просто передаём строку в конструктор new Date():

new Date('1976-10-23');

Это привычный нам способ записи даты. Когда мы видим запись 23-10-1976, мы сразу понимаем, что речь идёт о 23 октября 1976 года. Но если мы передадим такую строку в конструктор new Date('23-10-1976'), то мы получим ошибку Invalid Date. И это совершенно логично. Дело в том, что в разных странах формат записи данных отличается. Например, мы в России прочтём запись 11-06-2019 как 11 июня 2019 года. А в некоторых странах эту же запись прочтут как 6 ноября 2019 года. «Ну должен же быть общий для всех формат записи даты», — скажете вы. И будете совершенно правы. Такой стандарт есть, и он называется ISO 8601 Extended Format. Именно в таком формате нужно передавать строку даты в конструктор New Date(). Выглядит этот формат так:

`YYYY-MM-DDTHH:mm:ss:sssZ`

Разберём что тут происходит:

  • YYYY - 4 цифры года
  • MM - 2 цифры месяца (где 01 это январь, а 12 — декабрь)
  • DD - 2 цифры дня (от 0 до 31)
  • - - разделитель
  • T - индикатор, после которого указывается время
  • HH - часы (от 0 до 24)
  • mm - минуты (от до 59)
  • ss - секунды (от 0 до 59)
  • sss - миллисекунды (от 0 до 999)
  • : - разделитель времени
  • Z - если указать её в конце, то дата будет в формате UTC. Если Z отсутствует, то дата будет в локальном часовом поясе(это работает только если указано время).

Время указывать необязательно, это опциональный параметр.

Вроде всё понятно и достаточно просто. Но у этого способа есть один очень серьёзный недостаток. Конструкция new Date('1976-10-23'); возвращает разный результат. Если вы находитесь ниже Гринвича, то вы получите 22 октября 1976 года. А если вы выше Гринвича, то результат будет 23 октября 1976. Это происходит потому, что если строка передаётся без времени, то результат считается в UTC. Если вы хотите получить результат в локальном часовом поясе, то необходимо передать время, как минимум HH и mm. Незнание этой особенности часто приводит к ошибкам, которые крайне сложно отследить. В общем такой способ создания даты не очень надёжен и лучше его не использовать.

Дата как набор параметров

В конструктор создания даты можно передать до 7 параметров.

  1. Год — 4 цифры
  2. Месяц — номер месяца (следует помнить, что в javascript отсчёт месяцев начинается с 0)
  3. День — день месяца (от 1 до 31)
  4. Часы — часы (от 0 до 24)
  5. Минуты — минуты (от 0 до 59)
  6. Секунды — секунды (от 0 до 59)
  7. Миллисекунды — миллисекунды ( от 0 до 999)

Довольно части разработчики избегают такого способа создания даты, потому что он кажется сложным. Но на самом деле это не так. Всё очень просто. Посмотрим пример:

new Date(2019, 5, 11, 12, 36, 23)

Просто читаем параметры слева направо. Год 2019 — месяц июнь — число 11 — часы 12 — минуты 36 — секунды 23. Единственная проблема здесь — запомнить, что месяцы начинаются с 0. Январь === 0, Февраль === 1 и т.д. Но если просто принять это как данность, то никаких проблем не возникает. Зато этот способ лишён путаницы между часовыми поясами. Результат всегда будет в локальном часовом поясе.

Дата как timestamp

В javascript понятие timestamp означает количество миллисекунд, прошедших с 1 января 1970 года. Для создания даты такой подход практически не используется. Зато он очень удобен при сравнении дат.

Без параметров

Если вы не передаёте никаких параметров в конструктор новой даты, то вы получите текущую дату в локальном часовом формате.

Создание даты — итоги

  1. Для создания нового объекта даты используется new Date()
  2. Существует четыре возможных синтаксиса параметров создания нового объекта даты 2.1 Дата как параметр-строка 2.2 Дата как набор параметров 2.3 Дата как timestamp 2.4 Без параметров
  3. Не используйте вариант с параметром-строкой
  4. Самый оптимальный вариант создания нового объекта даты — с датой как набор параметров
  5. Запомните, что нумерация месяцев в javascript начинается с 0

Теперь давайте поговорим о том, как представить дату в виде понятной строки.

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

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

const date = new Date(2019, 5, 12, 12, 51, 42)

Теперь посмотрим, что javascript предлагает нам для форматирования даты:

  1. date.toString() выдаст нам Wed Jun 12 2019 12:51:42 GMT+0300 (Moscow Standard Time)
  2. date.toDateString() выдаст нам Wed Jun 12 2019
  3. date.toLocaleString() выдаст нам 12/06/2019, 12:51:42
  4. date.toLocaleDateString() выдаст нам 12/06/2019
  5. date.toGMTString выдаст нам Wed, 12 Jun 2019 09:51:42 ;GMT
  6. date.toUTCString выдаст нам Wed, 12 ;Jun 2019 09:51:42 ;GMT
  7. datetoISOString выдаст нам 2019-06-12T09:51:42.000Z

И это все доступные варианты. Если вам нужен какой-то иной формат вывода даты, то вам придётся создать его самому.

Создаём свой формат вывода даты

Возможно, это звучит страшно и сложно, но на практике всё не так. Javascript предлагает достаточный набор методов для работы с объектом даты, которые помогут нам решить эту проблему.

Допустим, нам нужно выводить дату в таком виде: Среда, 12 июня 2019.

Рассмотрим, какие методы работы с датой нам доступны:

  1. getFullYear - возвращает год, в нашем случае 2019
  2. getMonth - возвращает месяц, в нашем случае 5 (мы же помним, что отсчёт начинается с 0)
  3. getDate - возвращает день, в нашем случае 12
  4. getDay - возвращает день недели, в нашем случае 3 (отсчет дней также начинается с 0, первый день — воскресенье)

Этого более чем достаточно для достижения нашей цели. Получить число и год проще простого:

const d = new Date(2019, 5, 12);

const year = d.getFullYear(); // 2019

const date = d.getDate(); // 12

Чтобы вывести месяц придётся немного поработать:

cosnt months = [ "Января", "Февраля", "Марта", "Апреля", "Мая", "Июня", "Июля", "Августа", "Сентября", "Октября", "Ноября", "Декабря" ];

const month = months(d.getMonth()); // Июня

День недели определим похожим образом:

cosnt days = [ "Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота" ];

const day = days(d.getDay()); // Среда

Теперь, когда у нас есть все необходимые данные, осталось только собрать конечный результат:

const formattedDate = `${day}, ${date} ${month} ${year}`; // Среда, 12 июня 2019

Сравнение дат

Сравнить даты можно с помощью операторов сравнения >, <, >=, <=:

const firstDate = new Date(2019, 1, 23);
const secondDate = new Date(2019, 2, 8);
console.log(firstDate < secondDate); // true

Тут всё очень просто. Сложности начинаются когда необходимо сравнить являются ли даты полностью одинаковыми. Сделать это с помощью == или === не получится:

const a = newDate(2019, 5, 12);
const b = new Date(2019, 5, 12);

console.log(a == b); // false
console.log(a === b); // false

Для сравнения полного равенства дат необходимо сравнивать их timestamp, который можно получить с помощью метода getTime:

const isSameTime = (a, b) => {
    return a.getTime() === b.getTime();
}

const a = newDate(2019, 5, 12);
const b = new Date(2019, 5, 12);

console.log(isSameTime(a, b)); // true

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

const isSameDay = (a, b) => {
    return a.getFullYear() === b.getFullYear && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
}

const a = newDate(2019, 5, 12);
const b = new Date(2019, 5, 12);

console.log(isSameDay(a, b)); // true

Получение даты из другой даты

Возможны два случая, когда вам необходимо создать новую дату из имеющейся:

  1. Задать определённую дату на основе имеющейся даты
  2. Изменить дату на определённый интервал

Установка определённой даты из имеющейся

Ранее мы рассматривали методы, которые предлагает нам javascript для получения информации о дате. Но раз есть методы для получения (геттеры), то должны быть аналогичные методы для установки (сеттеры). И они есть:

  1. setFullYear - устанавливает год в локальном часовом поясе
  2. setMonth - устанавливает месяц в локальном часовом поясе
  3. setDate - устанавливает число в локальном часовом поясе
  4. setHours - устанавливает часы в локальном часовом поясе
  5. setMinutes - устанавливает минуты в локальном часовом поясе
  6. setSeconds - устанавливает секунды в локальном часовом поясе
  7. setMilliseconds - устанавливает миллисекунды в локальном часовом поясе

Например, если необходимо установить дату на 15 число, сделать это можно так:

const date = new Date(2019, 5, 12);
d.setDate(15);
console.log(d); // 15 июня

Если необходимо установить месяц на июль, то вот так:

const date = new Date(2019, 5, 12);
d.setMonth(6);
console.log(d); // 12 июля

Необходимо помнить, что описанные выше методы изменяют оригинальный объект даты. На практике мы не должны так поступать, мы должны для этих операций создавать новый, независимый от основного, объект даты.

Изменение даты на определённый интервал

Существует два самых популярных способа для выполнения этой задачи. Допустим, сегодня 12 июня и мы хотим получить дату, которая будет через три дня. Первый способ ( c использованием set):

const today = new Date(2019, 5, 12);
const finalDate = new Date(today); // создаём новый объект, чтобы не изменять изначальный
const currentDay = today.getDate(); // получаем день
finalDate.setDate(currentDay + 3);
console.log(finalDate); // 15 июня 2019

Воторой подход (с помощью New Date):

const today = new Date(2019, 5, 12);
const year = today.getFullYear();
const month = today.getMonth();
const day = today.getDate();
const finalDate = new Date(yar, month, day + 3);
console.log(finalDate); // 15 июня 2019

Оба способа выполняют свою задачу, выбирайте какой нравится.

Автоматическая коррекция даты

Если при создании нового объекта даты вы укажете параметры, выходящие за пределы допустимого диапазона, то javascript автоматически исправит вас. Допустим, мы попытаемся установить дату 33 июня. В таком случае javascript вернёт нам 3 июля. Благодаря этому вы можете не волноваться, например, при изменении даты на определённый интервал.

new Date(2019, 5, 12 + 20); //вернёт 2 июля

На этом всё. Этой информации достаточно для решения 90% задач, связанных с датой.