Относительное время

Относительное время — способ представления даты и времени не в абсолютном формате (2026-05-27 14:00), а относительно текущего момента:

  • 5 минут назад
  • через 2 дня
  • вчера
  • 1 hour ago
  • in 3 weeks

Библиотека Globalize предоставляет механизм локализации подобных выражений на основе данных CLDR (Common Locale Data Repository). Форматирование автоматически учитывает:

  • язык;
  • правила склонения;
  • множественное число;
  • направление времени;
  • особенности локали.

Подключение модулей

Для работы с относительным временем необходимы:

const Globalize = require("globalize");

require("cldr-data").entireSupplemental();
require("cldr-data").entireMainFor("en", "ru");

Globalize.load(
  require("cldr-data/supplemental/likelySubtags.json"),
  require("cldr-data/supplemental/timeData.json"),
  require("cldr-data/supplemental/weekData.json"),
  require("cldr-data/main/en/dateFields.json"),
  require("cldr-data/main/ru/dateFields.json")
);

После загрузки данных создаётся экземпляр локали:

const ru = new Globalize("ru");
const en = new Globalize("en");

Метод formatRelativeTime

Основной инструмент библиотеки — метод:

globalize.formatRelativeTime(value, unit)

Параметры

Параметр Описание
value числовое значение
unit единица времени

Поддерживаемые единицы времени

Единица Значение
second секунды
minute минуты
hour часы
day дни
week недели
month месяцы
year годы

Базовые примеры

Секунды

ru.formatRelativeTime(-5, "second");

Результат:

5 секунд назад

Положительное значение:

ru.formatRelativeTime(10, "second");

Результат:

через 10 секунд

Минуты

ru.formatRelativeTime(-3, "minute");

Результат:

3 минуты назад
en.formatRelativeTime(-3, "minute");

Результат:

3 minutes ago

Часы

ru.formatRelativeTime(2, "hour");

Результат:

через 2 часа

Дни

ru.formatRelativeTime(-1, "day");

Результат:

1 день назад

Направление времени

Знак числа определяет направление времени.

Значение Интерпретация
отрицательное прошлое
положительное будущее
0 текущий момент

Пример:

ru.formatRelativeTime(-7, "day");
7 дней назад
ru.formatRelativeTime(7, "day");
через 7 дней

Работа со множественным числом

Одно из главных преимуществ Globalize — корректное склонение числительных.

Русский язык имеет сложные правила множественного числа:

ru.formatRelativeTime(-1, "minute");
ru.formatRelativeTime(-2, "minute");
ru.formatRelativeTime(-5, "minute");

Результаты:

1 минуту назад
2 минуты назад
5 минут назад

Для английского языка:

en.formatRelativeTime(-1, "minute");
en.formatRelativeTime(-2, "minute");

Результаты:

1 minute ago
2 minutes ago

Все правила берутся из CLDR автоматически.


Использование сокращённых форматов

CLDR содержит разные стили отображения времени:

  • длинный;
  • короткий;
  • узкий.

Пример сокращённых вариантов:

через 3 ч.
5 мин назад
2 нед.

В некоторых версиях Globalize стиль можно указать через опции форматтера.


Создание форматтера

Для повышения производительности рекомендуется создавать форматтер заранее.

Без кэширования

ru.formatRelativeTime(-5, "day");

С кэшированием

const formatter = ru.relativeTimeFormatter("day");

formatter(-5);
formatter(10);

Результаты:

5 дней назад
через 10 дней

Почему форматтеры быстрее

При каждом вызове библиотека:

  1. анализирует локаль;
  2. ищет шаблоны;
  3. подбирает plural rules;
  4. строит итоговую строку.

Предварительно созданный форматтер избавляет от повторной подготовки.

Особенно важно это:

  • в React-приложениях;
  • в чатах;
  • в системах уведомлений;
  • в realtime-интерфейсах.

Форматирование списка сообщений

Исходные данные

const messages = [
  { text: "Сообщение 1", minutes: -1 },
  { text: "Сообщение 2", minutes: -5 },
  { text: "Сообщение 3", minutes: -20 }
];

Форматирование

const formatter = ru.relativeTimeFormatter("minute");

messages.forEach(message => {
  console.log(
    message.text,
    formatter(message.minutes)
  );
});

Результат:

Сообщение 1 1 минуту назад
Сообщение 2 5 минут назад
Сообщение 3 20 минут назад

Автоматический выбор единицы времени

Часто требуется преобразовывать разницу дат автоматически:

  • секунды;
  • минуты;
  • часы;
  • дни;
  • месяцы.

Пример функции

function formatTimeAgo(date, globalize) {
  const diff = Date.now() - date.getTime();

  const seconds = Math.floor(diff / 1000);

  if (seconds < 60) {
    return globalize.formatRelativeTime(
      -seconds,
      "second"
    );
  }

  const minutes = Math.floor(seconds / 60);

  if (minutes < 60) {
    return globalize.formatRelativeTime(
      -minutes,
      "minute"
    );
  }

  const hours = Math.floor(minutes / 60);

  if (hours < 24) {
    return globalize.formatRelativeTime(
      -hours,
      "hour"
    );
  }

  const days = Math.floor(hours / 24);

  return globalize.formatRelativeTime(
    -days,
    "day"
  );
}

Использование:

const postDate = new Date(Date.now() - 3600 * 1000);

console.log(
  formatTimeAgo(postDate, ru)
);

Результат:

1 час назад

Форматирование будущего времени

Относительное время подходит не только для прошлого.

Таймер события

const daysLeft = 12;

ru.formatRelativeTime(daysLeft, "day");

Результат:

через 12 дней

До начала мероприятия

const hoursLeft = 3;

ru.formatRelativeTime(hoursLeft, "hour");

Результат:

через 3 часа

Интеграция с Date

Обычно вычисляется разница между двумя датами.

Пример

const now = new Date();
const createdAt = new Date(
  now.getTime() - 2 * 60 * 60 * 1000
);

const diffHours = Math.floor(
  (createdAt - now) / (1000 * 60 * 60)
);

ru.formatRelativeTime(diffHours, "hour");

Результат:

2 часа назад

Использование в интерфейсах

Относительное время применяется:

Сценарий Пример
мессенджеры 2 минуты назад
комментарии час назад
соцсети вчера
уведомления через 5 минут
таймеры через 2 дня

Обновление времени в реальном времени

Во многих интерфейсах надписи должны обновляться автоматически.

Пример

const formatter =
  ru.relativeTimeFormatter("minute");

function render() {
  const createdAt =
    Date.now() - 5 * 60 * 1000;

  const diffMinutes = Math.floor(
    (createdAt - Date.now()) / 60000
  );

  console.log(
    formatter(diffMinutes)
  );
}

setInterval(render, 60000);

Особенности локализации

Разные языки имеют разные правила.

Русский

через 1 день
через 2 дня
через 5 дней

Польский

za 1 dzień
za 2 dni
za 5 dni

Арабский

Используются отдельные формы для:

  • 0;
  • 1;
  • 2;
  • нескольких;
  • многих объектов.

CLDR хранит все эти правила.


Отличие от Intl.RelativeTimeFormat

Современный Javascript содержит встроенный API:

Intl.RelativeTimeFormat

Пример:

const rtf = new Intl.RelativeTimeFormat("ru");

rtf.format(-5, "minute");

Результат:

5 минут назад

Почему используют Globalize

Несмотря на наличие встроенного API, Globalize остаётся востребованным благодаря:

  • тесной интеграции с CLDR;
  • единой системе интернационализации;
  • поддержке старых окружений;
  • совместимости с другими форматтерами;
  • гибкой загрузке locale-данных.

Комбинирование с другими форматтерами

Globalize позволяет объединять:

  • числа;
  • валюты;
  • даты;
  • относительное время;
  • сообщения.

Пример интерфейса уведомления

const dateFormatter =
  ru.dateFormatter({
    datetime: "medium"
  });

const relativeFormatter =
  ru.relativeTimeFormatter("minute");

const createdAt =
  new Date(Date.now() - 300000);

console.log(
  "Создано:",
  dateFormatter(createdAt)
);

console.log(
  relativeFormatter(-5)
);

Результат:

Создано: 27 мая 2026 г., 14:10:00
5 минут назад

Ошибки при использовании

Не загружены данные CLDR

Ошибка:

E_MISSING_CLDR

Причина:

Globalize.load(...)

не был вызван.


Не подключён dateFields.json

Относительное время хранится именно там.

Необходимо:

require("cldr-data/main/ru/dateFields.json")

Неверная единица времени

Ошибка:

ru.formatRelativeTime(-5, "minutes");

Правильно:

ru.formatRelativeTime(-5, "minute");

Используются только значения в единственном числе.


Оптимизация размера бандла

Полный CLDR может занимать значительный объём.

Для уменьшения размера подключают только необходимые данные.

Минимальный набор

Globalize.load(
  require("cldr-data/main/ru/dateFields.json")
);

Поддержка нескольких языков

Загрузка нескольких локалей

Globalize.load(
  require("cldr-data/main/en/dateFields.json"),
  require("cldr-data/main/ru/dateFields.json"),
  require("cldr-data/main/de/dateFields.json")
);

Использование

const locales = {
  ru: new Globalize("ru"),
  en: new Globalize("en"),
  de: new Globalize("de")
};

locales.ru.formatRelativeTime(-1, "day");
locales.en.formatRelativeTime(-1, "day");
locales.de.formatRelativeTime(-1, "day");

Результаты:

1 день назад
1 day ago
vor 1 Tag

Практический пример: комментарии

Данные

const comments = [
  {
    user: "Alex",
    createdAt:
      Date.now() - 1000 * 60 * 3
  },
  {
    user: "Kate",
    createdAt:
      Date.now() - 1000 * 60 * 50
  }
];

Форматирование

const formatter =
  ru.relativeTimeFormatter("minute");

comments.forEach(comment => {
  const diffMinutes = Math.floor(
    (comment.createdAt - Date.now()) /
    60000
  );

  console.log(
    comment.user,
    formatter(diffMinutes)
  );
});

Результат:

Alex 3 минуты назад
Kate 50 минут назад

Практический пример: обратный отсчёт

function formatCountdown(targetDate) {
  const diff =
    targetDate.getTime() - Date.now();

  const days = Math.floor(
    diff / (1000 * 60 * 60 * 24)
  );

  return ru.formatRelativeTime(
    days,
    "day"
  );
}

const eventDate =
  new Date(Date.now() + 5 * 86400000);

console.log(
  formatCountdown(eventDate)
);

Результат:

через 5 дней

Особенности производительности

При большом количестве вызовов желательно:

  • переиспользовать форматтеры;
  • не создавать экземпляр Globalize повторно;
  • кэшировать locale-данные;
  • лениво загружать языки.

Архитектура относительного времени в Globalize

Механизм работы состоит из нескольких этапов:

  1. загрузка CLDR;
  2. определение локали;
  3. выбор plural rules;
  4. выбор шаблона времени;
  5. подстановка значения;
  6. возврат локализованной строки.

Структура CLDR для relative time

CLDR хранит шаблоны в dateFields.json.

Пример структуры:

{
  "day": {
    "relativeTime-type-future": {
      "relativeTimePattern-count-one":
        "через {0} день",
      "relativeTimePattern-count-few":
        "через {0} дня",
      "relativeTimePattern-count-many":
        "через {0} дней"
    }
  }
}

Именно эти данные используются движком форматирования.


Совместимость

Globalize поддерживает:

  • Node.js;
  • браузеры;
  • старые среды без полного Intl;
  • серверный рендеринг;
  • SPA-приложения.

Использование в React

Пример компонента

function TimeAgo({ createdAt, globalize }) {
  const diffMinutes = Math.floor(
    (createdAt - Date.now()) / 60000
  );

  return (
    <span>
      {
        globalize.formatRelativeTime(
          diffMinutes,
          "minute"
        )
      }
    </span>
  );
}

Использование в Vue

export default {
  props: ["createdAt"],

  computed: {
    relativeTime() {
      const diffMinutes = Math.floor(
        (this.createdAt - Date.now()) /
        60000
      );

      return this.$globalize
        .formatRelativeTime(
          diffMinutes,
          "minute"
        );
    }
  }
};

Использование в серверном рендеринге

На сервере относительное время часто формируется заранее.

Node.js

app.get("/", (req, res) => {
  const globalize = new Globalize("ru");

  const createdAt =
    Date.now() - 1000 * 60 * 15;

  const relative =
    globalize.formatRelativeTime(
      -15,
      "minute"
    );

  res.render("index", {
    relative
  });
});