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
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
value | TValue | null | undefined | undefined | Момент, о котором речь. Пустое значение рисует пустую метку. |
valueAdapter |
| undefined | Как значение приходит от потребителя. Тот же контракт, что у пикеров. |
base | Date | undefined | undefined | С чем сравнивать вместо «сейчас». Задан — часы не читаются вовсе: ни таймера, ни расхождения серверного рендера с клиентским. Это же делает воспроизводимыми тесты и снимки. |
live | boolean | undefined | undefined | Обновляться живьём. Такт компонент выбирает сам по текущей единице: «3 месяца назад» незачем пересчитывать каждую секунду. |
cutoff | number | undefined | undefined | Начиная со скольких дней показывать обычную дату вместо относительной. `0` — никогда: «347 дней назад» останется как есть. |
format | Intl.DateTimeFormatOptions | undefined | undefined | Вид абсолютной даты — опциями `Intl`, а не строкой-паттерном. |
width | NameWidth | undefined | undefined | Длина относительной строки: «3 месяца назад» против «3 мес. назад». |
numeric | Intl.RelativeTimeFormatNumeric | undefined | undefined | `'auto'` даёт «вчера», `'always'` — «1 день назад». |
locale | string | undefined | undefined | Локаль показа. Не задана — из адаптера i18n приложения. |
Слоты
| Slot | Type | Описание |
|---|---|---|
default | { text: string; absolute: string; datetime: string; } | Своя разметка вместо текста: значения те же, что компонент рисует сам. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrRelativeTimeConfigurableProps, GrRelativeTimeProps } from '@feugene/granularity-chrono/components/GrRelativeTime'Файлdist/types/components/GrRelativeTime/config.d.ts
const grRelativeTimeConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrRelativeTime">;Файлdist/types/components/GrRelativeTime/defaults.d.ts
Пропы GrRelativeTime, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrRelativeTime: { … } }">.
Ровно то, что задают один раз на приложение: вид строки и порог перехода к
абсолютной дате. Само значение и то, с чем его сравнивают, — всегда на месте.
interface GrRelativeTimeConfigurableProps {
width: NameWidth;
numeric: Intl.RelativeTimeFormatNumeric;
cutoff: number;
live: boolean;
}Файлdist/types/components/GrRelativeTime/safelist.d.ts
const grRelativeTimeSafelist: string[];Файлdist/types/components/GrRelativeTime/GrRelativeTime.vue.d.ts
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 сравнивают с заданным моментом, и
допуска нет: две секунды вперёд — это будущее.
Такт зависит от единицы
Секунды пересчитываются раз в пять секунд, месяцы — раз в час. Одинаковый такт для всех означал бы либо секунду, дёргающую сотню меток впустую, либо минуту, из-за которой «только что» держится дольше, чем это правда.
Пропы этого примера