Работа с датами в историческом контексте значительно сложнее стандартных операций с современным временем. Большинство библиотек для JavaScript ориентированы на григорианский календарь, который официально используется сегодня. Однако при обработке архивных документов, исторических событий, генеалогических данных или астрономических вычислений возникает необходимость учитывать:
Библиотека Luxon не реализует полноценную поддержку альтернативных календарных систем, но предоставляет инструменты для работы с временными зонами, ISO-форматами, локалями и вычислениями, позволяющими моделировать поведение старых календарей.
Юлианский календарь был введён Юлием Цезарем в 45 году до н.э. Основная особенность — високосный год каждые 4 года без исключений.
Средняя длина года:
365.25 суток
Из-за накопления ошибки календарь постепенно начал расходиться с астрономическим временем.
В 1582 году папа Григорий XIII провёл реформу:
Средняя длина года:
365.2425 суток
После реформы произошло удаление нескольких дней из календаря.
После перехода на григорианский календарь часть дат была пропущена.
Пример для Италии:
4 октября 1582 → 15 октября 1582
Между этими датами не существовало дней:
5–14 октября 1582
Разные страны переходили на новый календарь в разные годы.
| Страна | Переход |
|---|---|
| Италия | 1582 |
| Франция | 1582 |
| Германия | 1700 |
| Британия | 1752 |
| Россия | 1918 |
Luxon использует:
Date;Это расширение современного григорианского календаря в прошлое, даже в эпохи, когда он ещё не существовал.
Следовательно:
DateTime.fromISO("1400-01-01")
не создаёт «реальную» историческую дату 1400 года, а создаёт дату по современным правилам григорианского календаря.
import { DateTime } from "luxon";
const date = DateTime.fromObject({
year: 1453,
month: 5,
day: 29
});
console.log(date.toISODate());
Результат:
1453-05-29
Luxon спокойно работает с древними датами.
ISO-8601 поддерживает годы до нашей эры.
const caesar = DateTime.fromObject({
year: -44,
month: 3,
day: 15
});
console.log(caesar.toISODate());
Результат:
-000044-03-15
Это соответствует 44 году до н.э.
Историческая система:
1 год до н.э. → 1 год н.э.
ISO-система:
0 = 1 год до н.э.
-1 = 2 год до н.э.
Поэтому необходимо учитывать смещение.
Luxon не умеет переключаться на юлианский календарь автоматически, поэтому приходится использовать ручные вычисления.
После 1582 года разница постепенно увеличивалась.
| Период | Разница |
|---|---|
| 1582 | 10 дней |
| 1700 | 11 дней |
| 1800 | 12 дней |
| 1900 | 13 дней |
function julianToGregorian(date) {
return date.plus({ days: 10 });
}
Использование:
const oldDate = DateTime.fromISO("1582-10-04");
const converted = julianToGregorian(oldDate);
console.log(converted.toISODate());
Для исторических вычислений требуется учитывать вековые поправки.
function getJulianDifference(year) {
if (year >= 2100) return 14;
if (year >= 1900) return 13;
if (year >= 1800) return 12;
if (year >= 1700) return 11;
return 10;
}
function julianToGregorian(date) {
const diff = getJulianDifference(date.year);
return date.plus({ days: diff });
}
Luxon хорошо подходит для вычисления интервалов.
const start = DateTime.fromISO("1200-01-01");
const end = DateTime.fromISO("1500-01-01");
const diff = end.diff(start, "years");
console.log(diff.years);
При работе с древними датами возникают сложности:
Luxon считает интервалы математически, а не исторически.
Рекомендуется сохранять:
YYYY-MM-DD
Пример:
const archiveDate = DateTime.fromObject({
year: 1812,
month: 9,
day: 14
});
console.log(archiveDate.toISODate());
Поскольку Luxon не хранит информацию о календаре, полезно сохранять её отдельно.
const event = {
calendar: "julian",
date: "1700-02-10"
};
Во многих документах встречаются пометки:
O.S. — Old Style
N.S. — New Style
Пример:
const record = {
style: "OS",
date: "1752-09-02"
};
В Британии переход произошёл в 1752 году.
После:
2 сентября 1752
сразу наступило:
14 сентября 1752
Можно реализовать собственный валидатор.
function isMissingBritishDate(date) {
return (
date.year === 1752 &&
date.month === 9 &&
date.day >= 3 &&
date.day <= 13
);
}
Использование:
const date = DateTime.fromISO("1752-09-10");
console.log(isMissingBritishDate(date));
До появления стандартизированных часовых поясов использовалось локальное солнечное время.
Luxon опирается на базу IANA timezone database, которая:
Пример:
DateTime.fromObject(
{
year: 1500,
month: 1,
day: 1
},
{
zone: "Europe/Moscow"
}
);
Результат технически существует, но не отражает реальную систему времени XV века.
Для астрономических расчётов часто используется Julian Day Number.
Luxon напрямую его не поддерживает, но позволяет выполнять преобразования.
const date = DateTime.fromISO("1900-01-01");
console.log(date.toMillis());
Unix timestamp начинается с:
1970-01-01
Для древних дат появляются:
const ancient = DateTime.fromObject({
year: -5000,
month: 1,
day: 1
});
console.log(ancient.isValid);
Внутренний объект Date имеет ограничения:
±8.64e15 миллисекунд
Это примерно:
±275000 лет
Luxon поддерживает локали через Intl API.
const date = DateTime.fromISO("1812-09-14")
.setLocale("ru");
console.log(date.toLocaleString(DateTime.DATE_FULL));
Некоторые старые документы используют:
Luxon работает только с современными локалями.
const date = DateTime.fromFormat(
"14 Sep 1752",
"dd LLL yyyy"
);
console.log(date.toISODate());
Luxon не поддерживает:
Требуется отдельный парсер.
Практичный подход — создание обёртки.
class HistoricalDate {
constructor(date, calendar = "gregorian") {
this.date = date;
this.calendar = calendar;
}
toGregorian() {
if (this.calendar === "julian") {
return this.date.plus({ days: 13 });
}
return this.date;
}
}
Luxon использует неизменяемые объекты.
const original = DateTime.fromISO("1700-01-01");
const modified = original.plus({ days: 11 });
console.log(original.toISODate());
console.log(modified.toISODate());
Это особенно важно для исторических вычислений, где исходные данные должны оставаться неизменными.
При проектировании архивных систем полезно хранить:
| Поле | Назначение |
|---|---|
| original_date | Исходная запись |
| normalized_date | Нормализованная дата |
| calendar | Тип календаря |
| precision | Точность |
| source | Источник |
Во многих документах известен только:
Пример структуры:
const dateInfo = {
year: 1348,
month: null,
day: null
};
const plaguePeriod = Interval.fromDateTimes(
DateTime.fromISO("1347-01-01"),
DateTime.fromISO("1353-12-31")
);
import { Interval } from "luxon";
const medieval = Interval.fromDateTimes(
DateTime.fromISO("1000-01-01"),
DateTime.fromISO("1500-01-01")
);
console.log(medieval.length("years"));
function isMiddleAges(date) {
return date.year >= 476 && date.year <= 1492;
}
Luxon упрощает сортировку дат.
const events = [
DateTime.fromISO("1917-11-07"),
DateTime.fromISO("1812-09-14"),
DateTime.fromISO("1945-05-09")
];
events.sort((a, b) => a - b);
const event = {
title: "Battle",
date: DateTime.fromISO("1415-10-25").toISO()
};
console.log(JSON.stringify(event));
После сериализации теряется информация:
Поэтому необходимо хранить метаданные отдельно.
Для полноценной исторической точности Luxon часто комбинируют с:
Luxon в таких системах отвечает за:
При работе с миллионами исторических записей полезно:
fromFormat;const cache = new Map();
function convert(date) {
if (cache.has(date)) {
return cache.get(date);
}
const result = DateTime.fromISO(date)
.plus({ days: 13 });
cache.set(date, result);
return result;
}
Особое внимание уделяется:
describe("Historical dates", () => {
test("british missing dates", () => {
const date = DateTime.fromISO("1752-09-10");
expect(isMissingBritishDate(date))
.toBe(true);
});
test("julian conversion", () => {
const date = DateTime.fromISO("1700-01-01");
expect(
julianToGregorian(date).day
).toBe(12);
});
});