GrRelativeTime

  • chrono
  • Дата и время
  • API · 9 props

Берут, когда свежесть важнее точности.

import { GrRelativeTime } from '@feugene/granularity-chrono/components/GrRelativeTime'

Обзор

Когда брать

  • свежесть важнее точности — лента, комментарии, уведомления, аудит-лог: «5 минут назад» читается быстрее, чем «12.08.2026, 14:32»;
  • метка обязана оставаться верной — live пересчитывает текст, и «только что» не висит час спустя;
  • меток на странице много — сотня меток стоит одного setInterval: такт общий и на скрытой вкладке снимается;
  • нужны обе формы — точная дата уезжает в title и в слот, поэтому наведение и скринридер дают абсолютный момент.

Когда взять другое

НужноБерите
Выбрать дату, а не показатьGrDatePicker
Выбрать момент со временемGrDateTimePicker
Показать последовательность событийGrTimeline
Показать обратный отсчёт до дедлайнасвоя обвязка над useChronoNow

Точные значения так не показывают. Договор, платёж, расписание рейса требуют даты: «через 2 дня» не отвечает на вопрос «какого числа». cutoff задаёт порог, после которого компонент сам переходит на абсолютную форму, — и для таких мест его выставляют, а не отключают живой пересчёт.

Примеры 3 у каждого — живое демо и его код

  • Активность рабочего пространства

    Свежесть важнее точности: «2 минуты назад» читается быстрее метки времени, а точный момент остаётся в title и в <time datetime> — для наведения и диктора. value-adapter="isoDateTime" берёт строки API как есть, numeric="auto" говорит «вчера», а base закрепляет «сейчас» для этой застывшей страницы — и сервер с браузером совпадают.

    Workspace activity
    • MOMina Ortiz approved invoice INV-0418
    • DKDaniel Kim added 3 seats to the Team plan
    • BABilling API retried the card of Contoso Ltd
    • ATAiko Tanaka changed the dunning schedule
    • MOMina Ortiz invited Ben Hartley as an accountant
  • Когда платить по счетам

    Срок оплаты близок к договору: «через 5 дней» годится, а «3 недели назад» не отвечает на вопрос «какого числа». cutoff с 14 дней переключает на абсолютную дату, оформленную опциями format, а не строкой-шаблоном. value-adapter="isoDate" помечает значение как день, и оно считается календарными днями — в 9 утра завтрашняя дата читается «завтра», а не «через 15 часов». Слот по умолчанию получает текст, и просроченные строки говорят об этом красным.

    Contoso Ltd · open invoicesNet 14 · reminders go out on the due date
    • INV-0431€1,284.50
    • INV-0427€432.00
    • INV-0418€3,210.00
    • INV-0402€96.00
  • Метка, которая остаётся верной

    Последний импорт — настоящий момент этой сессии, поэтому live держит метку верной без чьего-либо участия: «сейчас» становится «1 минуту назад» на общем таймере, который тикает раз в 5 секунд, пока важны секунды, раз в час для месяцев и останавливается на скрытой вкладке. width="short" укладывается в строку статуса.

    Deutsche Bank · business accountTransactions are matched to invoices on import
    Last import

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
valueTValue | null | undefinedundefinedМомент, о котором речь. Пустое значение рисует пустую метку.
valueAdapter
"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoA"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoAdapter<TValue> | undefined
undefinedКак значение приходит от потребителя. Тот же контракт, что у пикеров.
baseDate | undefinedundefinedС чем сравнивать вместо «сейчас». Задан — часы не читаются вовсе: ни таймера, ни расхождения серверного рендера с клиентским. Это же делает воспроизводимыми тесты и снимки.
liveboolean | undefinedundefinedОбновляться живьём. Такт компонент выбирает сам по текущей единице: «3 месяца назад» незачем пересчитывать каждую секунду.
cutoffnumber | undefinedundefinedНачиная со скольких дней показывать обычную дату вместо относительной. `0` — никогда: «347 дней назад» останется как есть.
formatIntl.DateTimeFormatOptions | undefinedundefinedВид абсолютной даты — опциями `Intl`, а не строкой-паттерном.
widthNameWidth | undefinedundefinedДлина относительной строки: «3 месяца назад» против «3 мес. назад».
numericIntl.RelativeTimeFormatNumeric | undefinedundefined`'auto'` даёт «вчера», `'always'` — «1 день назад».
localestring | undefinedundefinedЛокаль показа. Не задана — из адаптера i18n приложения.

Слоты

SlotTypeОписание
default{ text: string; absolute: string; datetime: string; }Своя разметка вместо текста: значения те же, что компонент рисует сам.

Типы

Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.

Импорт
import type { GrRelativeTimeConfigurableProps, GrRelativeTimeProps } from '@feugene/granularity-chrono/components/GrRelativeTime'

Файлdist/types/components/GrRelativeTime/config.d.ts

grRelativeTimeConfigconst
const grRelativeTimeConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrRelativeTime">;

Файлdist/types/components/GrRelativeTime/defaults.d.ts

GrRelativeTimeConfigurablePropsinterface

Пропы GrRelativeTime, настраиваемые глобально через <GrConfigProvider :component-defaults="{ GrRelativeTime: { … } }">. Ровно то, что задают один раз на приложение: вид строки и порог перехода к абсолютной дате. Само значение и то, с чем его сравнивают, — всегда на месте.

interface GrRelativeTimeConfigurableProps {
    width: NameWidth;
    numeric: Intl.RelativeTimeFormatNumeric;
    cutoff: number;
    live: boolean;
}

Файлdist/types/components/GrRelativeTime/safelist.d.ts

grRelativeTimeSafelistconst
const grRelativeTimeSafelist: string[];

Файлdist/types/components/GrRelativeTime/GrRelativeTime.vue.d.ts

GrRelativeTimePropsinterface

GrRelativeTime — «3 минуты назад», «через 2 дня». Строку строит Intl.RelativeTimeFormat: названия единиц, склонение числительного и «вчера» вместо «1 день назад» знает платформа. Своих строк у компонента нет ни одной — он говорит на всех языках, которые знает движок. Разметка — <time> с машинным datetime: относительный текст читается человеком, точный момент остаётся машине и остаётся в title для того, кто захочет узнать дату.

interface GrRelativeTimeProps<T = Date> {
    /** Момент, о котором речь. Пустое значение рисует пустую метку. */
    value?: T | null;
    /** Как значение приходит от потребителя. Тот же контракт, что у пикеров. */
    valueAdapter?: GrChronoAdapterName | GrChronoAdapter<T>;
    /**
     * С чем сравнивать вместо «сейчас».
     *
     * Задан — часы не читаются вовсе: ни таймера, ни расхождения серверного
     * рендера с клиентским. Это же делает воспроизводимыми тесты и снимки.
     */
    base?: Date;
    /**
     * Обновляться живьём. Такт компонент выбирает сам по текущей единице:
     * «3 месяца назад» незачем пересчитывать каждую секунду.
     */
    live?: boolean;
    /**
     * Начиная со скольких дней показывать обычную дату вместо относительной.
     * `0` — никогда: «347 дней назад» останется как есть.
     */
    cutoff?: number;
    /** Вид абсолютной даты — опциями `Intl`, а не строкой-паттерном. */
    format?: Intl.DateTimeFormatOptions;
    /** Длина относительной строки: «3 месяца назад» против «3 мес. назад». */
    width?: NameWidth;
    /** `'auto'` даёт «вчера», `'always'` — «1 день назад». */
    numeric?: Intl.RelativeTimeFormatNumeric;
    /** Локаль показа. Не задана — из адаптера i18n приложения. */
    locale?: string;
}

Использование из прозы библиотеки

Единица выбирается двумя способами, и это видно на границах

До суток разрыв меряется прошедшим временем — секунда есть секунда, календарь тут ни при чём. От суток и дальше — календарными кортежами: дни, недели, полные месяцы, годы.

Отсюда два следствия. Месяц остаётся месяцем и в феврале, и в июле, потому что считается календарём. А сутки, длившиеся из-за перевода часов 23 часа, называются часами — потому что столько времени и правда прошло; иначе двухчасовой разрыв через полночь каждую ночь назывался бы вчерашним.

Дата без времени — это день, а не полночь

Значение без времени (valueAdapter="isoDate" или свой адаптер с dateOnly: true) описывает день в календаре, а его полночь — условность разбора. Прошедшим временем дата завтрашнего дня утром оказалась бы «через 15 часов», вчерашняя — «9 часов назад». Такое значение меряется только календарём от сегодняшнего дня: «завтра», «сегодня», «вчера», дальше недели и месяцы. В datetime у <time> уходит сама дата (2026-10-07), а не момент в UTC: локальная полночь восточнее Гринвича в UTC — это вечер предыдущего дня.

«Сейчас» не бывает будущим

Общие часы обновляются тактом, и между тактами их «сейчас» отстаёт от настоящего. Поэтому компонент перечитывает часы при смене значения и при переезде на тикер другого такта, а не сравнивает с последним тиком: иначе только что случившееся читалось бы как «через 4 секунды», а метка, дозревшая до минут, на отставшем минутном тикере снова видела бы секунды и качалась между тикерами. Будущее ближе секундного такта (пять секунд) — тоже «сейчас»: источник значения нередко спешит на пару секунд. Нулевой разрыв печатается «now», а с numeric="always" — «0 sec. ago», но не «in 0 sec.». С base сравнивают с заданным моментом, и допуска нет: две секунды вперёд — это будущее.

Такт зависит от единицы

Секунды пересчитываются раз в пять секунд, месяцы — раз в час. Одинаковый такт для всех означал бы либо секунду, дёргающую сотню меток впустую, либо минуту, из-за которой «только что» держится дольше, чем это правда.