Переход на Day.js в существующем проекте редко бывает одномоментным процессом: в реальных кодовых базах дата-время обычно распределено между форматированием, парсингом, бизнес-логикой, сериализацией API и утилитами. Поэтому наиболее устойчивой стратегией становится постепенная миграция с контролем совместимости и минимизацией регрессионных рисков.
Первый этап — полное картирование источников работы с датами. В типичном проекте встречаются:
DateКритически важно разделить использование по категориям:
Такое разделение позволяет внедрять Day.js точечно, не затрагивая всю систему сразу.
Одним из ключевых инструментов миграции становится создание промежуточного слоя работы с датами.
Пример базового интерфейса:
export const dateAdapter = {
now: () => Date.now(),
parse: (value) => new Date(value),
format: (date, fmt) => {
return formatDateLegacy(date, fmt);
},
addDays: (date, days) => {
const d = new Date(date);
d.setDate(d.getDate() + days);
return d;
}
};
После внедрения Day.js этот слой постепенно переписывается:
import dayjs from "dayjs";
export const dateAdapter = {
now: () => dayjs(),
parse: (value) => dayjs(value),
format: (date, fmt) => dayjs(date).format(fmt),
addDays: (date, days) => dayjs(date).add(days, "day")
};
Такой подход позволяет изолировать изменения от остального кода.
На промежуточном этапе часто требуется поддерживать двойную реализацию — старую и новую.
import dayjs from "dayjs";
export function formatDate(date, fmt, useDayjs = false) {
if (useDayjs) {
return dayjs(date).format(fmt);
}
return legacyFormat(date, fmt);
}
Цель этого этапа — обеспечить идентичность результатов.
Особое внимание уделяется:
Одна из самых частых проблем миграции — различие в парсинге строк.
Нативный Date и Day.js по-разному трактуют некорректные
или неполные строки.
Поэтому вводится слой нормализации:
import dayjs from "dayjs";
const normalize = (input) => {
if (!input) return null;
const d = dayjs(input);
return d.isValid() ? d : null;
};
В рамках миграции важно унифицировать:
Миграция должна идти по функциональным зонам.
// было
formatDateLegacy(date)
// стало
dayjs(date).format("DD.MM.YYYY")
// было
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
// стало
const nextWeek = dayjs().add(7, "day");
// было
if (a > b)
// стало
if (dayjs(a).isAfter(dayjs(b)))
Day.js использует модульную систему расширений. На этапе миграции важно централизовать подключение плагинов:
import dayjs from "dayjs";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
import utc from "dayjs/plugin/utc";
dayjs.extend(isSameOrAfter);
dayjs.extend(utc);
Рекомендуется формировать единый модуль и запрещать локальные расширения, чтобы избежать расхождений поведения.
Если проект затрагивает несколько регионов, миграция должна учитывать отсутствие встроенной полноценной timezone-модели в Day.js без плагинов.
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
const msk = dayjs().tz("Europe/Moscow");
На этапе миграции важно зафиксировать:
Ключевая часть миграции — сравнение старой и новой реализации.
test("format consistency", () => {
const date = new Date("2024-01-01T10:00:00Z");
expect(dayjs(date).format("YYYY-MM-DD"))
.toBe(legacyFormat(date, "YYYY-MM-DD"));
});
Особенно важно тестировать:
Для предотвращения регрессий вводятся архитектурные ограничения:
Date в бизнес-логикеПример правила:
{
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "dayjs",
"message": "Использовать dateAdapter"
}
]
}
]
}
Для крупных проектов используется автоматическая замена:
// jscodeshift transform
module.exports = function transformer(file, api) {
const j = api.jscodeshift;
return j(file.source)
.find(j.CallExpression)
.replaceWith(path => {
// преобразование moment() -> dayjs()
return path.node;
})
.toSource();
};
Codemod снижает количество ручных изменений и обеспечивает единообразие.
Одним из преимуществ Day.js является малый размер, но при неправильном использовании он может увеличиваться из-за плагинов.
Контрольные меры:
import *npx webpack-bundle-analyzer dist/stats.json
В системах с высокой критичностью применяется временная параллельная запись:
const legacyValue = legacyFormat(date);
const newValue = dayjs(date).format("YYYY-MM-DD");
logCompare(legacyValue, newValue);
Это позволяет выявить скрытые расхождения до полного отключения старой логики.
Финальный этап — удаление старых утилит:
При этом сохраняется только адаптер, работающий на Day.js, как единственная точка взаимодействия с датами.
После завершения миграции критично закрепить правила:
Date считается техническим долгомТакой подход стабилизирует поведение системы и предотвращает повторное расслоение логики работы с датами.