Двузначный год — это запись года в формате из двух цифр вместо четырёх. Например:
"99"
"05"
"21"
Подобный формат часто встречается:
dd/MM/yy.Главная проблема двузначных годов заключается в неоднозначности.
Значение "25" может означать:
Для корректной интернационализации необходимо правильно интерпретировать такие значения в зависимости от локали, календаря и правил приложения.
Библиотека Globalize использует данные CLDR и предоставляет механизмы форматирования и парсинга дат.
Для работы с двузначными годами используется:
Globalize.dateParser()
или:
Globalize.parseDate()
с шаблонами формата, содержащими:
yy
yyВ шаблонах дат:
| Формат | Значение |
|---|---|
y |
год без фиксированной длины |
yy |
двузначный год |
yyyy |
четырёхзначный год |
Пример:
const parser = Globalize.dateParser({
skeleton: "yyMd"
});
или:
const parser = Globalize.dateParser({
raw: "dd/MM/yy"
});
const parser = Globalize.dateParser({
raw: "dd.MM.yy"
});
const date = parser("15.08.24");
console.log(date);
Результат:
Thu Aug 15 2024
При парсинге двузначного года библиотека должна определить век.
Обычно используется правило «скользящего окна» — pivot year.
Например:
| Ввод | Интерпретация |
|---|---|
00 |
2000 |
24 |
2024 |
68 |
2068 |
69 |
1969 |
99 |
1999 |
Точное поведение зависит от реализации underlying parser и ICU-логики.
Исторически двузначные года привели к одной из крупнейших проблем программирования — Y2K.
Старые системы хранили:
98
99
00
01
вместо:
1998
1999
2000
2001
После наступления 2000 года многие программы интерпретировали:
00
как:
1900
Это вызывало:
parseDateconst date = Globalize.parseDate(
"03/11/25",
{
raw: "dd/MM/yy"
}
);
console.log(date);
dateParserМетод dateParser создаёт переиспользуемую функцию.
const parser = Globalize.dateParser({
raw: "MM-dd-yy"
});
console.log(parser("12-31-99"));
console.log(parser("01-01-00"));
yy и
yyyyyy15/03/24
Компактный формат, но неоднозначный.
yyyy15/03/2024
Однозначный и безопасный.
yyyyДвузначные года создают проблемы:
"30"
Может означать:
Разные системы используют разные pivot year.
При переносе старых данных невозможно гарантировать правильность столетия.
Возраст, статистика и исторические данные могут вычисляться неправильно.
const formatter = Globalize.dateFormatter({
raw: "dd.MM.yy"
});
console.log(
formatter(new Date(2025, 4, 12))
);
Результат:
12.05.25
const formatter = Globalize.dateFormatter({
raw: "dd.MM.yyyy"
});
console.log(
formatter(new Date(2025, 4, 12))
);
Результат:
12.05.2025
Разные страны используют разные форматы дат.
MM/dd/yy
Пример:
04/25/24
dd.MM.yy
Пример:
25.04.24
yy/MM/dd
Пример:
24/04/25
Globalize поддерживает skeleton-форматы CLDR.
const formatter = Globalize.dateFormatter({
skeleton: "yyMd"
});
В зависимости от локали формат автоматически меняется.
Globalize.locale("en");
const enFormatter = Globalize.dateFormatter({
skeleton: "yyMd"
});
console.log(
enFormatter(new Date(2024, 0, 15))
);
Результат:
1/15/24
Globalize.locale("de");
const deFormatter = Globalize.dateFormatter({
skeleton: "yyMd"
});
console.log(
deFormatter(new Date(2024, 0, 15))
);
Результат:
15.1.24
Автоматическое определение столетия удобно, но может приводить к скрытым ошибкам.
const parser = Globalize.dateParser({
raw: "yy-MM-dd"
});
console.log(
parser("40-01-01")
);
Не всегда очевидно:
1940
или:
2040
Во многих проектах двузначные года предварительно преобразуются вручную.
function normalizeYear(year) {
return year < 50
? 2000 + year
: 1900 + year;
}
console.log(normalizeYear(24));
console.log(normalizeYear(89));
Результат:
2024
1989
function normalizeDate(input) {
const parts = input.split("/");
let year = Number(parts[2]);
year = year < 50
? 2000 + year
: 1900 + year;
return `${parts[0]}/${parts[1]}/${year}`;
}
const normalized = normalizeDate("15/08/24");
const parser = Globalize.dateParser({
raw: "dd/MM/yyyy"
});
console.log(
parser(normalized)
);
При миграции legacy-систем часто встречаются таблицы:
| id | date |
|---|---|
| 1 | 12/05/87 |
| 2 | 01/11/03 |
| 3 | 15/08/99 |
Такие данные требуют:
const regex = /^\d{2}$/;
console.log(regex.test("24"));
console.log(regex.test("2024"));
const regex = /^\d{2}\/\d{2}\/\d{2}$/;
console.log(regex.test("15/08/24"));
const parser = Globalize.dateParser({
raw: "dd/MM/yy"
});
console.log(
parser("99/99/99")
);
Результат:
null
const parser = Globalize.dateParser({
raw: "dd/MM/yy"
});
const result = parser("31/02/24");
if (result === null) {
console.log("Некорректная дата");
}
Наиболее безопасный подход:
YYYY-MM-DD
Пример:
2024-08-15
Такой формат:
const parser = Globalize.dateParser({
raw: "dd/MM/yy"
});
const formatter = Globalize.dateFormatter({
raw: "yyyy-MM-dd"
});
const date = parser("15/08/24");
console.log(
formatter(date)
);
Двузначный год сам по себе не связан с timezone, однако при преобразовании даты возможно смещение.
const date = new Date(24, 0, 1);
console.log(date);
Интерпретация конструктора Date отличается от строкового
парсинга.
new Date(99, 0, 1)
Результат:
1999
Globalize.parseDate("01/01/99", {
raw: "dd/MM/yy"
});
Использует локализованный парсер и CLDR-форматы.
<input placeholder="dd/mm/yy">
Удобен для быстрого ввода, но опасен неоднозначностью.
15.08.24
2024-08-15
Date
Во многих enterprise-приложениях двузначные года:
Создание parser-функции один раз эффективнее многократного вызова
parseDate.
Globalize.parseDate(value, {
raw: "dd/MM/yy"
});
внутри цикла.
const parser = Globalize.dateParser({
raw: "dd/MM/yy"
});
for (const value of values) {
parser(value);
}
Двузначные года плохо подходят для:
Пример:
12/04/15
Может означать:
Особое внимание уделяется граничным значениям.
[
"00",
"01",
"49",
"50",
"68",
"69",
"99"
]
describe("two-digit years", () => {
const parser = Globalize.dateParser({
raw: "dd/MM/yy"
});
test("parses 24 correctly", () => {
const date = parser("15/08/24");
expect(date.getFullYear())
.toBe(2024);
});
});
yyyy
для хранения2024
вместо:
24
yy только для отображенияconst formatter = Globalize.dateFormatter({
raw: "dd.MM.yy"
});
Перед импортом:
87 → 1987
03 → 2003
Лучше явно задавать правила преобразования.
if (year < 1900 || year > 2100) {
throw new Error("Invalid year");
}
yy для хранения данныхПлохо:
24
Хорошо:
2024
03/04/24
Может означать:
Date.parseDate.parse("01/02/24")
Результат зависит от среды выполнения.
"24"
без определения века приводит к нестабильному поведению системы.