Проблемы с датами

Некорректная обработка дат — наиболее частая причина неверного вывода timeago.js. Проблемы возникают при парсинге строк, временных зонах, граничных значениях и типах входных данных.


Проблема 1: Строка без временной зоны

import { format } from 'timeago.js';

format('2025-06-01T12:00:00', 'ru');

Строка без Z или +HH:MM парсится браузером как локальное время. На машине с UTC+3 это означает:

'2025-06-01T12:00:00' → 2025-06-01T09:00:00Z

Если сервер отдаёт UTC, разница составит 3 часа. Пользователь видит неверное “относительное время”.

Решение:

// Вариант 1 — нормализация на клиенте
function normalizeDate(str) {
  if (str && str.includes('T') && !str.endsWith('Z') && !/[+\-]\d{2}:\d{2}$/.test(str)) {
    return str + 'Z';
  }
  return str;
}

format(normalizeDate(post.createdAt), 'ru');

// Вариант 2 — отдавать ISO с Z на сервере
// "2025-06-01T12:00:00.000Z"

Проблема 2: Строка в нестандартном формате

format('01.06.2025', 'ru');         // Неверно — формат ДД.ММ.ГГГГ
format('June 1, 2025', 'ru');       // Зависит от движка
format('2025/06/01', 'ru');         // Работает, но нестандартно

Надёжный формат — ISO 8601:

format('2025-06-01', 'ru');                // Только дата (время = 00:00:00 UTC или локально)
format('2025-06-01T12:00:00Z', 'ru');      // Дата и время в UTC
format('2025-06-01T12:00:00+03:00', 'ru'); // С явным offset

Проблема 3: Timestamp в секундах вместо миллисекунд

Большинство бэкендов на Python/PHP возвращают timestamp в секундах:

const unixSeconds = 1748779200; // June 1, 2025 в секундах
format(unixSeconds, 'ru'); // Ошибка! Дата будет в 1970 году

Решение:

function fromUnixSeconds(ts) {
  return new Date(ts * 1000);
}

format(fromUnixSeconds(post.createdAt), 'ru');

Проблема 4: Дата в будущем воспринимается как прошлое

// Если часы сервера и браузера расхождаться более чем на несколько секунд
const serverTime  = '2025-06-01T12:00:05Z'; // Сервер
const clientNow   = '2025-06-01T12:00:00Z'; // Клиент — отстаёт на 5 секунд
// format покажет "через 5 секунд" — правильно
// Но при рассинхронизации часов разница может быть неожиданной

Решение с relativeDate:

// Если сервер отдаёт текущее время — использовать его как точку отсчёта
const serverNow = response.headers['x-server-time'];

format(post.createdAt, 'ru', {
  relativeDate: new Date(serverNow),
});

Проблема 5: Invalid Date

const date = new Date('not a date');
format(date, 'ru'); // Не бросает исключение — возвращает "NaN years ago" или похожее

Проверка перед вызовом:

function safeFormat(date, locale = 'ru') {
  const d = new Date(date);
  return isNaN(d.getTime()) ? '' : format(d, locale);
}

Проблема 6: null и undefined

const timestamp = apiResponse.date; // Может быть null, если дата не задана

format(timestamp, 'ru'); // TypeError или неверный результат
format(timestamp ?? new Date(), 'ru'); // Fallback на текущее время
// Или
if (timestamp) format(timestamp, 'ru');

Проблема 7: Дата в прошлом, но очень давно

format(new Date('1970-01-01'), 'ru'); // "55 лет назад"

Для очень старых дат возвращается корректный результат, но число может быть неожиданно большим. Если нужно ограничить:

function boundedFormat(date, locale = 'ru', maxYears = 10) {
  const d      = new Date(date);
  const ageMs  = Date.now() - d.getTime();
  const ageYrs = ageMs / (365.25 * 86400 * 1000);

  if (ageYrs > maxYears) {
    return d.toLocaleDateString('ru-RU');
  }

  return format(d, locale);
}

Проблема 8: Date объект из другого контекста

При использовании iframe или Shadow DOM Date из другого контекста не является экземпляром текущего Date:

const iframeDate = iframe.contentWindow.eval('new Date()');
iframeDate instanceof Date; // false в некоторых браузерах

Решение:

// Преобразовать через timestamp
format(iframeDate.getTime(), 'ru');

Диагностика проблем с датами

function diagnoseDate(input) {
  const d = new Date(input);

  return {
    input,
    isValid:   !isNaN(d.getTime()),
    utcString: isNaN(d.getTime()) ? null : d.toISOString(),
    localString: isNaN(d.getTime()) ? null : d.toLocaleString('ru-RU'),
    timestamp: isNaN(d.getTime()) ? null : d.getTime(),
    type:      typeof input,
    isDate:    input instanceof Date,
  };
}

console.log(diagnoseDate('01.06.2025'));
console.log(diagnoseDate('2025-06-01T12:00:00'));
console.log(diagnoseDate('2025-06-01T12:00:00Z'));

Чеклист для диагностики

  • Строка даты содержит Z или явный offset?
  • Timestamp в миллисекундах, а не в секундах?
  • Проверяется ли на null перед вызовом format?
  • Проверяется ли isNaN(new Date(value).getTime())?
  • Часы клиента и сервера синхронизированы?