Работа с датами в JavaScript: практическое руководство и решения

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики на начальном и среднем уровне, стремящиеся улучшить навыки работы с датами в JavaScript
  • Студенты и обучающиеся веб-разработке, интересующиеся практическими аспектами программирования
  • Полный стек разработчики, ищущие решения для сложных задач, связанных с датами и временем в приложениях

    Работа с датами в JavaScript — это настоящее минное поле для разработчиков всех уровней. Один неверный шаг, и ваше приложение будет отображать пользователям в Токио вчерашнюю дату, а клиентам в Нью-Йорке — время, которое наступит только через три часа! Я прошёл через все эти баги, потерял десятки часов отладки и готов поделиться картой, которая проведёт вас через это датовое болото с минимальными потерями. 🗓️

Хотите не просто понять, как работать с датами в JavaScript, а стать экспертом во всех аспектах веб-разработки? Обучение веб-разработке от Skypro — это не просто курс, а полноценная программа, где вы освоите не только нюансы работы с временем, но и научитесь создавать полнофункциональные веб-приложения с использованием современных технологий. Наши студенты не просто знают теорию — они умеют применять её на практике с первого дня обучения.

Основы объекта Date в JavaScript: создание и форматирование

Объект Date в JavaScript — это встроенный инструмент для работы с датами и временем. На первый взгляд он кажется простым, но имеет множество нюансов, которые важно понимать для корректной разработки.

Начнём с создания объектов Date. Существует четыре способа инициализации:

  • Без аргументов (текущая дата и время): new Date()
  • С временной меткой (миллисекунды с 1 января 1970): new Date(1627824000000)
  • Строкой с датой: new Date('2021-08-01')
  • С отдельными компонентами (год, месяц, день...): new Date(2021, 7, 1)

Обратите внимание на особенность при создании даты с компонентами: месяцы в JavaScript индексируются с нуля! То есть январь — это 0, а декабрь — 11. Этот момент становится источником множества ошибок.

JS
Скопировать код
// Создаём дату "1 августа 2021"
const date = new Date(2021, 7, 1); // Обратите внимание: 7 = август!

Для форматирования даты JavaScript предлагает несколько встроенных методов:

Метод Описание Пример вывода
toString() Полное представление даты и времени Sun Aug 01 2021 00:00:00 GMT+0300
toDateString() Только дата без времени Sun Aug 01 2021
toTimeString() Только время без даты 00:00:00 GMT+0300
toISOString() Формат ISO 8601 2021-08-01T00:00:00.000Z
toLocaleString() Локализованный формат 01.08.2021, 00:00:00

Метод toLocaleString() особенно полезен, так как позволяет настраивать формат вывода с учётом локальных правил:

JS
Скопировать код
// Форматируем дату по-русски
date.toLocaleString('ru-RU', {
day: '2-digit',
month: 'long',
year: 'numeric'
}); // "01 августа 2021 г."

Для извлечения отдельных компонентов даты используйте специальные методы:

  • getFullYear() — получить год (например, 2021)
  • getMonth() — получить месяц (0-11)
  • getDate() — получить день месяца (1-31)
  • getHours(), getMinutes(), getSeconds() — для времени
  • getDay() — день недели (0-6, где 0 — воскресенье)

Максим, senior frontend-разработчик

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

Мы использовали код вида new Date(year, month, day), где month получали напрямую из номера месяца, выбранного пользователем (1-12). Поскольку в JavaScript месяцы индексируются с 0, все даты смещались на месяц вперед! Февраль становился мартом, и так далее.

Исправление было простым: new Date(year, month-1, day), но этот опыт научил меня всегда быть предельно внимательным при работе с датами в JavaScript.

Пошаговый план для смены профессии

Манипуляции с датами: расчёт разницы и преобразования

Одна из самых частых операций с датами — это модификация значений и расчёт временных интервалов. JavaScript предоставляет набор инструментов для решения этих задач. 🔄

Для изменения компонентов даты можно использовать соответствующие сеттеры:

JS
Скопировать код
const date = new Date();
date.setFullYear(2022); // Устанавливаем год
date.setMonth(0); // Устанавливаем январь (не забываем про индексацию с 0)
date.setDate(15); // Устанавливаем 15-й день месяца

Важная особенность этих методов — они автоматически корректируют дату при выходе за допустимые пределы:

JS
Скопировать код
const date = new Date(2022, 0, 31); // 31 января 2022
date.setMonth(1); // Устанавливаем февраль
console.log(date); // 28 февраля 2022 (февраль 2022 имеет 28 дней)

date.setDate(32); // Устанавливаем 32-й день
console.log(date); // 4 апреля 2022 (31 день марта + 1 день)

Эта особенность чрезвычайно полезна для добавления интервалов:

JS
Скопировать код
// Добавляем 45 дней к текущей дате
const date = new Date();
date.setDate(date.getDate() + 45);

Для вычисления разницы между датами в JavaScript есть несколько подходов:

  1. Вычитание объектов Date (даёт разницу в миллисекундах)
  2. Ручной расчёт разницы по компонентам
  3. Использование сторонних библиотек

Давайте рассмотрим первый подход подробнее:

JS
Скопировать код
const date1 = new Date('2022-01-01');
const date2 = new Date('2022-02-15');

const diffInMs = date2 – date1; // 3888000000 миллисекунд
const diffInDays = diffInMs / (1000 * 60 * 60 * 24); // 45 дней

При работе с датами часто требуется преобразование между различными форматами. Вот таблица с основными преобразованиями:

Из формата В формат Метод/подход
Date объект Timestamp (мс) date.getTime() или +date
Timestamp (мс) Date объект new Date(timestamp)
Date объект ISO строка date.toISOString()
ISO строка Date объект new Date(isoString)
Date объект Компоненты (год, месяц, ...) date.getFullYear(), date.getMonth(), ...

Один из самых мощных приёмов — использование временных меток (timestamp). Это позволяет выполнять арифметические операции с датами:

JS
Скопировать код
// Получаем дату через 7 дней от текущей
const now = new Date();
const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);

// Или то же самое более кратко
const nextWeek = new Date(+now + 7 * 24 * 60 * 60 * 1000);

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

Работа с часовыми поясами и локализация времени

Часовые пояса — одна из самых сложных частей работы с датами в JavaScript. Объект Date всегда хранит время в UTC (Coordinated Universal Time), но методы отображения и получения компонентов времени работают в локальном часовом поясе пользователя, что часто вызывает путаницу. ⏰

Существует два типа методов для работы с датой:

  • Локальные методы: getHours(), getDate(), getMonth(), и т.д.
  • UTC методы: getUTCHours(), getUTCDate(), getUTCMonth(), и т.д.

Локальные методы возвращают значения в часовом поясе пользователя, а UTC методы — в универсальном времени:

JS
Скопировать код
const date = new Date('2022-01-15T12:00:00Z'); // 'Z' означает UTC

console.log(date.getHours()); // Может быть 15 (если вы в UTC+3)
console.log(date.getUTCHours()); // Всегда 12

Для работы с часовыми поясами в JavaScript есть несколько подходов:

  1. Хранить все даты в UTC и преобразовывать их в локальное время только для отображения
  2. Использовать метод toLocaleString() с опцией timeZone
  3. Применять специализированные библиотеки (Luxon, date-fns-tz)

Метод toLocaleString() позволяет форматировать дату с учетом определенного часового пояса:

JS
Скопировать код
const date = new Date('2022-01-15T12:00:00Z');

// Отображение в разных часовых поясах
console.log(date.toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' }));
console.log(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));
console.log(date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }));

Для локализации дат важно учитывать культурные особенности форматирования. Разные страны используют разные порядки компонентов даты, разделители и форматы времени:

JS
Скопировать код
const date = new Date('2022-01-15T12:30:45Z');

// Локализация для разных стран
console.log(date.toLocaleString('ru-RU')); // 15.01.2022, 15:30:45
console.log(date.toLocaleString('en-US')); // 1/15/2022, 12:30:45 PM
console.log(date.toLocaleString('de-DE')); // 15.1.2022, 13:30:45

Метод toLocaleString() принимает дополнительные параметры для тонкой настройки форматирования:

JS
Скопировать код
const options = {
weekday: 'long', // 'long', 'short', 'narrow'
year: 'numeric', // 'numeric', '2-digit'
month: 'long', // 'numeric', '2-digit', 'long', 'short', 'narrow'
day: 'numeric', // 'numeric', '2-digit'
hour: '2-digit', // 'numeric', '2-digit'
minute: '2-digit', // 'numeric', '2-digit'
second: '2-digit', // 'numeric', '2-digit'
timeZoneName: 'long' // 'long', 'short'
};

console.log(date.toLocaleString('ru-RU', options));
// "суббота, 15 января 2022 г., 15:30:45 Москва, стандартное время"

Когда дело касается работы с разницей во времени между часовыми поясами, чистый JavaScript предлагает ограниченные возможности. Для сложных сценариев стоит обратить внимание на специализированные библиотеки, о которых мы поговорим в следующем разделе.

Анна, full-stack разработчик

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

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

Решение оказалось многоэтапным:

  1. Мы перешли на хранение всех времен в UTC
  2. Добавили в профиль пользователя выбор часового пояса
  3. Внедрили библиотеку Luxon для правильного преобразования времени при отображении

Теперь каждый пользователь видит корректное локальное время начала мероприятий, независимо от его географического положения.

Современные библиотеки для работы с датами в JavaScript

Нативный API для работы с датами в JavaScript имеет множество ограничений и непоследовательностей. Именно поэтому сообщество разработало несколько библиотек, значительно упрощающих работу с датами и временем. 📚

Рассмотрим три самые популярные библиотеки и их особенности:

Библиотека Преимущества Недостатки Размер (min+gzip)
date-fns – Модульная структура<br>- Неизменяемый API<br>- Поддержка функционального программирования<br>- Хорошая производительность – Требует импорта множества функций<br>- Отдельный пакет для работы с часовыми поясами ~4 KB (импорт отдельных функций)
Luxon – Современный API<br>- Хорошая работа с часовыми поясами<br>- Расширенная локализация<br>- Создан автором moment.js – Меньше дополнительных функций по сравнению с moment.js<br>- Не такая обширная экосистема ~13 KB
Day.js – Очень маленький размер<br>- API, совместимый с moment.js<br>- Плагинная архитектура<br>- Проще мигрировать с moment.js – Меньше функций в базовой версии<br>- Требует загрузки плагинов для расширенного функционала ~2 KB (базовый) + плагины

Выбор библиотеки зависит от ваших конкретных задач:

  • Если размер бандла критичен — Day.js
  • Если важна модульность и функциональный подход — date-fns
  • Если нужна мощная работа с часовыми поясами — Luxon

Давайте посмотрим на примеры использования каждой из этих библиотек для решения одной и той же задачи — добавление 1 месяца и форматирование даты:

date-fns:

JS
Скопировать код
import { format, addMonths } from 'date-fns';
import { ru } from 'date-fns/locale';

const date = new Date();
const newDate = addMonths(date, 1);
const formatted = format(newDate, 'dd MMMM yyyy', { locale: ru });
console.log(formatted); // например, "15 февраля 2022"

Luxon:

JS
Скопировать код
import { DateTime } from 'luxon';

const date = DateTime.now();
const newDate = date.plus({ months: 1 });
const formatted = newDate.setLocale('ru').toFormat('dd MMMM yyyy');
console.log(formatted); // например, "15 февраля 2022"

Day.js:

JS
Скопировать код
import dayjs from 'dayjs';
import 'dayjs/locale/ru';
import localizedFormat from 'dayjs/plugin/localizedFormat';

dayjs.extend(localizedFormat);
dayjs.locale('ru');

const date = dayjs();
const newDate = date.add(1, 'month');
const formatted = newDate.format('DD MMMM YYYY');
console.log(formatted); // например, "15 февраля 2022"

Большинство современных проектов предпочитают date-fns из-за модульности и удобства использования с современными подходами к импорту. Day.js выбирают, когда важен размер, а Luxon — для сложных сценариев с часовыми поясами.

Важно отметить, что популярная ранее библиотека moment.js с 2020 года находится в режиме поддержки и не рекомендуется для новых проектов из-за своего размера и мутабельного API.

Практические решения типичных задач с датами и временем

В этом разделе мы рассмотрим конкретные решения для распространённых задач при работе с датами в JavaScript. Эти примеры помогут вам справиться с большинством сценариев, возникающих в реальных проектах. 💡

1. Проверка корректности даты

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

JS
Скопировать код
function isValidDate(dateString) {
const date = new Date(dateString);
return !isNaN(date) && date.toString() !== 'Invalid Date';
}

console.log(isValidDate('2022-02-31')); // false (31 февраля не существует)
console.log(isValidDate('2022-02-28')); // true

2. Вычисление возраста по дате рождения

JS
Скопировать код
function calculateAge(birthDate) {
const today = new Date();
const birth = new Date(birthDate);

let age = today.getFullYear() – birth.getFullYear();
const monthDiff = today.getMonth() – birth.getMonth();

// Уменьшаем возраст, если день рождения в этом году ещё не наступил
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}

return age;
}

console.log(calculateAge('1990-05-15')); // Выведет возраст в годах

3. Получение первого и последнего дня месяца

JS
Скопировать код
function getFirstDayOfMonth(year, month) {
return new Date(year, month, 1);
}

function getLastDayOfMonth(year, month) {
return new Date(year, month + 1, 0);
}

console.log(getFirstDayOfMonth(2022, 1)); // 1 февраля 2022
console.log(getLastDayOfMonth(2022, 1)); // 28 февраля 2022

4. Добавление рабочих дней (без выходных)

JS
Скопировать код
function addBusinessDays(date, days) {
const result = new Date(date);
let addedDays = 0;

while (addedDays < days) {
result.setDate(result.getDate() + 1);
const dayOfWeek = result.getDay();

// Пропускаем выходные (0 – воскресенье, 6 – суббота)
if (dayOfWeek !== 0 && dayOfWeek !== 6) {
addedDays++;
}
}

return result;
}

const nextBusinessDay = addBusinessDays(new Date('2022-02-18'), 3);
// Если 18 февраля – пятница, то результат будет 23 февраля (среда)

5. Форматирование относительного времени

Для отображения времени в формате "5 минут назад", "вчера" и т.д.:

JS
Скопировать код
function formatRelativeTime(date) {
const now = new Date();
const diff = now – date; // разница в миллисекундах

// Преобразуем в секунды
const seconds = Math.floor(diff / 1000);

if (seconds < 60) return 'только что';

// Преобразуем в минуты
const minutes = Math.floor(seconds / 60);

if (minutes < 60) return `${minutes} ${declOfNum(minutes, ['минуту', 'минуты', 'минут'])} назад`;

// Преобразуем в часы
const hours = Math.floor(minutes / 60);

if (hours < 24) return `${hours} ${declOfNum(hours, ['час', 'часа', 'часов'])} назад`;

// Для более старых дат используем обычное форматирование
return date.toLocaleDateString();
}

// Вспомогательная функция для склонения существительных
function declOfNum(n, titles) {
return titles[
n % 10 === 1 && n % 100 !== 11 
? 0 
: n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) 
? 1 
: 2
];
}

console.log(formatRelativeTime(new Date(Date.now() – 5 * 60 * 1000))); // "5 минут назад"

6. Работа с датами в UTC

Если ваше приложение должно работать с единым временем независимо от часового пояса пользователя:

JS
Скопировать код
function getUTCDate() {
const now = new Date();
return new Date(Date.UTC(
now.getUTCFullYear(),
now.getUTCMonth(),
now.getUTCDate(),
now.getUTCHours(),
now.getUTCMinutes(),
now.getUTCSeconds()
));
}

// Текущая дата в UTC
console.log(getUTCDate().toISOString());

При работе со сложными сценариями, такими как повторяющиеся события, учёт праздников или специфические бизнес-правила для дат, рекомендую использовать специализированные библиотеки, которые мы обсудили в предыдущем разделе. Они предоставляют готовые решения для большинства подобных задач и позволяют избежать множества ошибок.

Работа с датами и временем в JavaScript может быть запутанной, но, освоив основные принципы и инструменты, вы сможете уверенно решать даже сложные задачи. Помните главные правила: всегда учитывайте часовые пояса, используйте современные библиотеки для сложных случаев и тестируйте ваш код на разных локалях и часовых поясах. Эти знания значительно упростят вашу работу и помогут создавать более надёжные и интернациональные приложения. Следуя рекомендациям из этого руководства, вы избежите большинства распространенных ошибок и сэкономите множество часов отладки.

Загрузка...