Библиотека timeago.js не реализует собственный парсер дат. Вместо этого
она делегирует преобразование строк нативному конструктору new
Date(). Это решение упрощает библиотеку и уменьшает её размер, но
переносит ответственность за правильность строк на вызывающий код.
Внутри функции format входное значение приводится примерно
так:
function toDate(input) {
if (input instanceof Date) return input;
return new Date(input); // для строк и чисел
}
Это означает: любой аргумент, который new Date()
преобразует в валидную дату, будет работать. Если new
Date(input) возвращает Invalid Date, результат
форматирования будет непредсказуемым.
Перед передачей строки в format можно проверить её
валидность:
const str = '2025-05-26T10:00:00Z';
const date = new Date(str);
console.log(isNaN(date.getTime())); // false — валидная дата
Если результат true — строка невалидна и не должна
передаваться в format.
Единственный формат, гарантированно стандартизированный в ECMAScript — ISO 8601:
YYYY-MM-DDTHH:mm:ss.sssZ
Примеры:
new Date('2025-05-26T10:00:00Z'); // ок
new Date('2025-05-26T10:00:00.000Z'); // ок
new Date('2025-05-26T10:00:00+03:00'); // ок
Все остальные строковые форматы оставлены на усмотрение реализации JavaScript-движка.
ECMAScript определяет, что строки формата YYYY-MM-DD без
времени интерпретируются как UTC-полночь:
new Date('2025-05-26').toISOString();
// → "2025-05-26T00:00:00.000Z"
Если пользователь находится в часовом поясе UTC+3, то
2025-05-26 будет ощущаться как “вчера в 03:00 по местному
времени” — не как “сегодня”.
Строки формата YYYY-MM-DDTHH:mm:ss без
суффикса в большинстве современных браузеров интерпретируются
как локальное время:
new Date('2025-05-26T00:00:00').getTime();
// разное значение в разных часовых поясах
| Формат строки | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
2025-05-26T10:00:00Z
|
UTC | UTC | UTC | UTC |
2025-05-26T10:00:00
|
Local | Local | Может отличаться | Local |
2025-05-26
|
UTC | UTC | UTC | UTC |
2025/05/26
|
Local | Local | Ошибка в старых версиях | Local |
May 26 2025
|
Local | Local | Local | Local |
Safari исторически имеет проблемы с нестандартными форматами. Для кросс-браузерной совместимости следует использовать только ISO 8601 с явным UTC.
Node.js и браузеры используют разные движки JavaScript. Поведение парсинга нестандартных строк может отличаться:
// В браузере Chrome
new Date('2025.05.26'); // Valid Date
// В Node.js
new Date('2025.05.26'); // Invalid Date
Для кода, который выполняется и на сервере, и в браузере (SSR), это критически важно.
// UTC: 2025-05-26T10:00:00.000Z
// Если клиент в UTC+3, локальное время: 13:00:00
const utcDate = new Date('2025-05-26T10:00:00Z');
const diff = Date.now() - utcDate.getTime();
// Разница корректна — оба значения в UTC
Опасный вариант — передача строки без часового пояса и последующее несоответствие:
// Сервер сохранил "2025-05-26 10:00:00" как LOCAL время
// Клиент интерпретирует её как LOCAL time клиента
// Если у сервера и клиента разные часовые пояса — ошибка
Для строк с нестандартным форматом рекомендуется нормализация:
function normalizeDate(str) {
// DD.MM.YYYY → YYYY-MM-DDTHH:mm:ssZ
const match = str.match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
if (match) {
return `${match[3]}-${match[2]}-${match[1]}T00:00:00Z`;
}
return str;
}
import { format } from 'timeago.js';
format(normalizeDate('26.05.2025'), 'ru');
import { format } from 'timeago.js';
function safeParse(input, locale = 'ru') {
let date;
if (input instanceof Date) {
date = input;
} else if (typeof input === 'number') {
date = new Date(input);
} else if (typeof input === 'string') {
date = new Date(input);
} else {
return '';
}
if (isNaN(date.getTime())) {
console.warn('[timeago] Invalid date:', input);
return '';
}
return format(date, locale);
}
Если строки всегда в UTC, но без суффикса Z:
const str = '2025-05-26T10:00:00'; // предположительно UTC
// Принудительно добавить Z
const normalized = str.endsWith('Z') ? str : str + 'Z';
format(normalized, 'ru');
Если формат строк нестандартен или приходит из пользовательского ввода — рекомендуется использовать специализированные парсеры:
date-fns/parseISO — строгий парсинг ISO
date-fns/parse — парсинг по шаблону
dayjs(str) — гибкий парсинг
luxon.DateTime.fromFormat(str, fmt) — строгий шаблонный
парсинг
import { parse } from 'date-fns';
import { format as timeagoFormat } from 'timeago.js';
const date = parse('26.05.2025', 'dd.MM.yyyy', new Date());
timeagoFormat(date, 'ru');
Такой подход разделяет ответственность: date-fns отвечает
за парсинг, timeago.js — за форматирование.