Сравнение библиотек для работы с датами в JavaScript
Предыстория
После очередного изменения в нашем сервисе расчёта доставки, увеличившего количество обрабатываемых ПВЗ, его время ответа значительно вышло за пределы допустимого. В ходе профилирования было установлено, что от 50 до 84% времени уходит на работу с датами.
Проблема
Больше всего времени уходило на приведение в ISO(400ms), прибавление дней к дате(180ms) и работу с таймзонами(200ms). В качестве библиотеки для работы с датами мы использовали Luxon.
Возможные решения
В процессе изучения вопроса, мы изучили Luxon, Date-fns, Dayjs и нативную дату:
🏎️ Производительность (на 1 000 000 операций)
💡 Luxon
- Добавление одного дня: 7400ms
- Приведение к ISO: 5250ms
☀️ Dayjs
- Добавление одного дня: 1800ms | x4
- Приведение к ISO: 550ms | x9.5
📈 Date-fns
- Добавление одного дня: 360ms | x20
- Приведение к ISO: 1350ms | x4
🤖 Native Date
- Добавление одного дня: 250ms | x29.6
- Приведение к ISO: 550ms | x9.5/li>
🏖️ Удобство использования
💡 Luxon
- Таймзоны: ✅
- Fluent interface: ✅
- Иммутабельность: ✅
- Парсинг из произвольного формата: ✅
☀️ Dayjs
- Таймзоны: ✅
- Fluent interface: ✅
- Иммутабельность: ✅
- Парсинг из произвольного формата: ✅
📈 Date-fns
- Таймзоны: ❌
- Fluent interface: ❌
- Иммутабельность: ✅
- Парсинг из произвольного формата: ✅
🤖 Native Date
- Таймзоны: ❌
- Fluent interface: ❌
- Иммутабельность: ❌
- Парсинг из произвольного формата: ❌
📋 Дополнительно
В документации к Moment.js, предшественнику Luxon, присутствует статья со сравнением производительности разных библиотек:
Результат
Было принято решение использовать Dayjs в пользу наиболее простой миграции.
🕓 Конечное время расчёта доставки
💡 Luxon
- Первый запрос продукта: 1100ms
- Дальнейшие запросы продукта: 860ms
- Три последовательных запроса: 2100ms
☀️ Dayjs
- Первый запрос продукта: 200ms | x5.5
- Дальнейшие запросы продукта: 110ms | x7.8
- Три последовательных запроса: 220ms | x9.5
📈 Date-fns
- ⚠️ Данные могут быть недостоверны, т.к. замена Luxon на Date-fns частично сломала логику с таймзонами
- Первый запрос продукта: 260ms | x4.2
- Дальнейшие запросы продукта: 100ms | x8.6
- Несколько последовательных запросов: 250ms | x8.4
В заключение
Если вам нужна производительность — наилучшим решением будет написать свою обёртку над нативной датой. Если же вы не уверены — создайте абстракцию над используемой библиотекой, чтобы сделать миграцию наиболее простой и безболезненной.