Пошук уроків, статей та іншого контенту
Розглянете Intl.RelativeTimeFormat для локалізованих фраз на кшталт «вчора», «за два тижні» та «3 години тому».
Intl.RelativeTimeFormatIntl.RelativeTimeFormat — це вбудований API JavaScript для форматування відносного часу з урахуванням локалі.
Замість ручного складання фраз:
"3 години тому"
"за два тижні"
"вчора"можна передати числове значення та одиницю часу:
const formatter = new Intl.RelativeTimeFormat("uk", {
numeric: "auto"
});
formatter.format(-3, "hour"); // "3 години тому"
formatter.format(2, "week"); // "через 2 тижні"
formatter.format(-1, "day"); // "вчора"API самостійно:
підставляє правильний порядок слів;
локалізує одиниці часу;
обирає граматичну форму;
формує фрази для минулого та майбутнього;
підтримує різні стилі відображення.
const formatter = new Intl.RelativeTimeFormat(locales, options);
formatter.format(value, unit);localesПерший параметр визначає локаль:
const ukrainian = new Intl.RelativeTimeFormat("uk");
const english = new Intl.RelativeTimeFormat("en");
console.log(ukrainian.format(-3, "hour"));
// "3 години тому"
console.log(english.format(-3, "hour"));
// "3 hours ago"Також можна передати масив локалей:
const formatter = new Intl.RelativeTimeFormat(["uk-UA", "uk", "en"]);Браузер або середовище JavaScript вибере першу підтримувану локаль.
valueЗнак числа визначає напрямок часу:
від’ємне число — минуле;
додатне число — майбутнє;
нуль — поточний момент.
const formatter = new Intl.RelativeTimeFormat("uk");
formatter.format(-2, "day"); // "2 дні тому"
formatter.format(2, "day"); // "через 2 дні"
formatter.format(0, "day"); // "через 0 днів"Саме число не потрібно робити від’ємним вручну для минулого часу. Від’ємне значення вже означає минуле.
unitПідтримуються такі одиниці:
"year"
"quarter"
"month"
"week"
"day"
"hour"
"minute"
"second"
Одиницю потрібно передавати в однині:
const formatter = new Intl.RelativeTimeFormat("uk");
console.log(formatter.format(-1, "month"));
// "місяць тому"
console.log(formatter.format(3, "year"));
// "через 3 роки"Форми "months", "дні" або "hours" як значення unit використовувати не потрібно.
numericОпція numeric визначає, чи потрібно використовувати спеціальні слова на кшталт «вчора», «сьогодні» та «завтра».
"always"Це стандартне значення. Форматтер завжди використовує число:
const formatter = new Intl.RelativeTimeFormat("uk", {
numeric: "always"
});
console.log(formatter.format(-1, "day"));
// "1 день тому"
console.log(formatter.format(0, "day"));
// "через 0 днів"
console.log(formatter.format(1, "day"));
// "через 1 день""auto"Значення "auto" дозволяє замінювати деякі числові фрази природнішими словами:
const formatter = new Intl.RelativeTimeFormat("uk", {
numeric: "auto"
});
console.log(formatter.format(-1, "day"));
// "вчора"
console.log(formatter.format(0, "day"));
// "сьогодні"
console.log(formatter.format(1, "day"));
// "завтра"Для тижнів, місяців і років також можуть використовуватися спеціальні локалізовані форми:
const formatter = new Intl.RelativeTimeFormat("uk", {
numeric: "auto"
});
console.log(formatter.format(-1, "week"));
// "минулого тижня"
console.log(formatter.format(1, "week"));
// "наступного тижня"Точний текст залежить від локалі та реалізації середовища.
styleОпція style визначає довжину одиниці часу.
"long"Це стандартний стиль із повними словами:
const formatter = new Intl.RelativeTimeFormat("uk", {
style: "long"
});
console.log(formatter.format(-3, "hour"));
// "3 години тому""short"Скорочений варіант:
const formatter = new Intl.RelativeTimeFormat("uk", {
style: "short"
});
console.log(formatter.format(-3, "hour"));
// Результат залежить від реалізації локалі"narrow"Найкомпактніший варіант, зручний для невеликих елементів інтерфейсу:
const formatter = new Intl.RelativeTimeFormat("uk", {
style: "narrow"
});
console.log(formatter.format(-3, "hour"));Для публічного тексту зазвичай варто використовувати "long". Стилі "short" і "narrow" краще підходять для карток, списків і компактних панелей.
Intl.RelativeTimeFormat не перетворює автоматично секунди на хвилини, години або дні. Якщо передати значення 90 з одиницею "second", API не змінить його на «1 хвилина 30 секунд»:
const formatter = new Intl.RelativeTimeFormat("uk");
console.log(formatter.format(-90, "second"));
// "90 секунд тому"Тому перед форматуванням потрібно самостійно визначити одиницю.
Наприклад, можна використовувати такі межі:
менше 60 секунд — секунди;
менше 60 хвилин — хвилини;
менше 24 годин — години;
менше 7 днів — дні;
менше 30 днів — тижні;
менше 12 місяців — місяці;
інакше — роки.
Це не єдина можлива стратегія. Межі залежать від дизайну та вимог продукту.
Нижче наведено функцію, яка отримує дату події та повертає локалізований відносний час.
function formatRelativeTime(date, locale = "uk-UA") {
const now = Date.now();
const timestamp = date instanceof Date ? date.getTime() : new Date(date).getTime();
if (Number.isNaN(timestamp)) {
throw new TypeError("Передано некоректну дату");
}
const differenceInSeconds = (timestamp - now) / 1000;
const absoluteSeconds = Math.abs(differenceInSeconds);
let value;
let unit;
if (absoluteSeconds < 60) {
value = Math.round(differenceInSeconds);
unit = "second";
} else if (absoluteSeconds < 60 * 60) {
value = Math.round(differenceInSeconds / 60);
unit = "minute";
} else if (absoluteSeconds < 60 * 60 * 24) {
value = Math.round(differenceInSeconds / (60 * 60));
unit = "hour";
} else if (absoluteSeconds < 60 * 60 * 24 * 7) {
value = Math.round(differenceInSeconds / (60 * 60 * 24));
unit = "day";
} else if (absoluteSeconds < 60 * 60 * 24 * 30) {
value = Math.round(differenceInSeconds / (60 * 60 * 24 * 7));
unit = "week";
} else if (absoluteSeconds < 60 * 60 * 24 * 365) {
value = Math.round(differenceInSeconds / (60 * 60 * 24 * 30));
unit = "month";
} else {
value = Math.round(differenceInSeconds / (60 * 60 * 24 * 365));
unit = "year";
}
const formatter = new Intl.RelativeTimeFormat(locale, {
numeric: "auto",
style: "long"
});
return formatter.format(value, unit);
}
console.log(formatRelativeTime(new Date(Date.now() - 3 * 60 * 60 * 1000)));
// Наприклад: "3 години тому"
console.log(formatRelativeTime(new Date(Date.now() + 2 * 24 * 60 * 60 * 1000)));
// Наприклад: "через 2 дні"У цій функції:
час події перетворюється на мілісекунди;
обчислюється різниця між подією та поточним часом;
за абсолютним значенням різниці обирається одиниця;
знак різниці зберігається;
Intl.RelativeTimeFormat створює локалізований текст.
Додатне значення означає подію в майбутньому, тому функція поверне фразу на кшталт «через 2 дні». Від’ємне значення означає подію в минулому.
Date зберігає момент часу, а не просто календарну дату. Порівняння через getTime() коректно працює для моментів часу, отриманих з ISO-рядків або числових timestamp.
const publishedAt = new Date("2026-07-25T12:00:00Z");
console.log(formatRelativeTime(publishedAt, "uk-UA"));Рядок із часовим поясом, наприклад із суфіксом Z або зміщенням +03:00, однозначно описує момент часу:
const date = new Date("2026-07-25T15:00:00+03:00");Натомість рядки без часового поясу можуть інтерпретуватися як локальний час середовища:
const date = new Date("2026-07-25T15:00:00");Для даних із сервера краще передавати повні ISO-рядки з часовим поясом або UTC.
formatToPartsМетод format() повертає один готовий рядок. Якщо потрібно окремо стилізувати число та текст, можна використати formatToParts().
const formatter = new Intl.RelativeTimeFormat("uk", {
numeric: "always"
});
const parts = formatter.formatToParts(-3, "hour");
console.log(parts);Результат містить частини з типами, наприклад:
[
{ type: "integer", value: "3", unit: "hour" },
{ type: "literal", value: " години тому" }
]Структура та конкретний текст залежать від локалі. Не варто покладатися на фіксовані індекси масиву. Краще перевіряти type:
function formatRelativeTimeParts(value, unit, locale = "uk") {
const formatter = new Intl.RelativeTimeFormat(locale, {
numeric: "always"
});
return formatter.formatToParts(value, unit).map((part) => {
if (part.type === "integer") {
return `<strong>${part.value}</strong>`;
}
return part.value;
}).join("");
}
console.log(formatRelativeTimeParts(-3, "hour"));
// Наприклад: "<strong>3</strong> години тому"Якщо результат вставляється в HTML, потрібно враховувати безпеку даних. Значення з Intl.RelativeTimeFormat не містять користувацького вводу, але загальна функція повинна екранувати зовнішні дані перед вставленням у innerHTML.
Створення Intl.RelativeTimeFormat може бути дорожчим за виклик format(). Якщо потрібно форматувати багато значень, наприклад список повідомлень, зручно створити форматтер один раз:
const relativeTimeFormatter = new Intl.RelativeTimeFormat("uk-UA", {
numeric: "auto"
});
const messages = [
{ value: -5, unit: "minute" },
{ value: -1, unit: "day" },
{ value: 2, unit: "week" }
];
for (const message of messages) {
console.log(
relativeTimeFormatter.format(message.value, message.unit)
);
}Для кількох локалей можна зберігати форматтери в Map:
const formatters = new Map();
function getRelativeTimeFormatter(locale) {
if (!formatters.has(locale)) {
formatters.set(
locale,
new Intl.RelativeTimeFormat(locale, {
numeric: "auto",
style: "long"
})
);
}
return formatters.get(locale);
}
console.log(getRelativeTimeFormatter("uk").format(-1, "day"));
console.log(getRelativeTimeFormatter("en").format(-1, "day"));Перед реалізацією варто визначити правила відображення:
чи потрібно показувати секунди;
після якого моменту використовувати хвилини;
коли замінювати «1 день тому» на «вчора»;
чи потрібні тижні;
як обробляти майбутні дати;
який стиль доречний для конкретного компонента.
Наприклад, для стрічки новин можна використати:
до хвилини — секунди;
до години — хвилини;
до доби — години;
до тижня — дні;
далі — повну календарну дату.
Для повідомлень про заплановану подію майбутній час може бути важливішим за минулий:
const formatter = new Intl.RelativeTimeFormat("uk", {
numeric: "always"
});
console.log(formatter.format(2, "hour"));
// "через 2 години"Не варто самостійно складати українські форми:
// Ненадійний підхід
const text = `${count} ${count === 1 ? "година" : "години"} тому`;Таке правило не охоплює всі числівники та одиниці. Крім того, воно не працює для інших локалей.
Краще передати значення до Intl.RelativeTimeFormat:
const formatter = new Intl.RelativeTimeFormat("uk");
console.log(formatter.format(-22, "hour"));Правильно:
formatter.format(-3, "day"); // три дні в минулому
formatter.format(3, "day"); // три дні в майбутньомуНе потрібно передавати абсолютне значення та окремо додавати текст "тому".
Форматтер не перетворює секунди на хвилини:
formatter.format(-90, "second");
// "90 секунд тому"Вибір одиниці потрібно реалізувати окремо.
Одиниці передаються англійськими ідентифікаторами в однині:
formatter.format(-2, "day");
formatter.format(-2, "month");
formatter.format(-2, "year");Варіанти "days" або "дні" не є правильними значеннями параметра unit.
У прикладі з автоматичним вибором одиниці місяць часто приблизно обчислюють як 30 днів, а рік — як 365 днів. Це зручно для простого відносного часу, але не завжди відповідає календарній арифметиці.
Наприклад, проміжок між 1 лютого та 1 березня може мати різну кількість днів у різні роки. Якщо бізнес-логіка вимагає точних календарних місяців або років, потрібно окремо реалізувати календарні обчислення, а Intl.RelativeTimeFormat використовувати лише для фінального форматування.
Необов’язково створювати новий форматтер для кожного елемента списку:
// Неоптимально для великої кількості елементів
for (const item of items) {
const formatter = new Intl.RelativeTimeFormat("uk");
console.log(formatter.format(item.value, item.unit));
}Краще створити його до циклу та повторно використовувати.
Intl.RelativeTimeFormat локалізує фрази про минулий і майбутній час.
format(value, unit) приймає число та одиницю часу.
Від’ємні значення означають минуле, додатні — майбутнє.
numeric: "auto" може використовувати слова «вчора», «сьогодні» та «завтра».
style підтримує стилі "long", "short" і "narrow".
API не вибирає одиницю автоматично — секунди, хвилини, години та дні потрібно визначати самостійно.
formatToParts() дає змогу окремо обробляти частини локалізованого результату.
Для великих списків форматтери варто кешувати.
Для точних календарних обчислень місяців і років потрібно відокремлювати обчислення дат від локалізації тексту.