GrDuration
Machine-translated from the Russian original, not yet reviewed. Read the original
When to take it
- how long it took — the duration of a call, the time of a build, the downtime of equipment: “2 h 30 min” reads faster than “9000”;
- how long it has been running right now — pass the moment of the start as a single date, and the label becomes live: the timer is shared across the whole application and is removed on a hidden tab;
- the interval between two moments — a pair of dates is computed by itself, and there is no need to subtract milliseconds by hand;
- the value travels into the markup —
datetimeis filled with the exact length in ISO 8601, even when the text has been shortened to two units.
When to take something else
| Need | Take |
|---|---|
| Show how long ago it was | GrRelativeTime |
| Choose a moment rather than show a length | GrDatePicker / GrTimePicker |
| Enter a duration rather than show it | GrNumberInput |
| Show the share of what has elapsed out of the whole | GrProgressBar |
There are four units, and there will be nothing larger
Days, hours, minutes, seconds. Months and years are deliberately not here: they are calendar ones —
of different lengths in February and in July — and they cannot be derived from a number of seconds
without lying. A gap that really is measured by the calendar is shown by
GrRelativeTime, and computed by differenceInMonths.
A ceiling rather than a quota
maxUnits limits the number of units from above and adds nothing: exactly two hours is “2 h” rather
than “2 h 0 min”. The smaller part is discarded rather than rounded — “2 h 59 min 30 s” stays
“2 h 59 min”. Rounding would raise the display above the time that really has elapsed, and this value
is usually used to measure precisely that.
Zero is the only exception to the discarding: “0 s” is printed, because an empty string reads as “there is no data”, and a zero means something else.
A live label belongs only to the form with a single date
A number and a pair of dates set the length as a whole, and there is nothing in them to tick. The moment of the start is another matter: until “now” the time keeps running, and the label recomputes itself. The tick is chosen by the smallest unit shown, so a label in hours does not wake the tab every second.
The live display is derived from the clock, which means the server render and the client one will
diverge. The component marks that with data-allow-mismatch, and the mark is removed not by a flag
but by data: pass base and the render becomes deterministic.
Limits
There is no countdown here: “2 h 30 min left” is the same length, but a deadline, a zero and the transition into negative values belong to a scenario rather than to a label. Compute the remainder yourself and pass it as a number.
Install
npm i @feugene/granularity-chronoImport
import { GrDuration } from '@feugene/granularity-chrono/components/GrDuration'API
The API for this component has not been generated yet: the showcase generator only covers the core so far. Until it does, the reference lives in the package documentation.
Examples 1
Basic
<script setup lang="ts">
import { ref } from 'vue'
/**
* Три формы значения — три разных вопроса. Число это готовая длина, пара дат —
* промежуток между моментами, одна дата — время, которое идёт прямо сейчас.
*/
const started = ref(new Date())
const rows = [
{ title: 'Длительность звонка', value: 9000 },
{ title: 'Сборка заняла', value: 10_770 },
]
const meeting: [Date, Date] = [
new Date(2026, 7, 12, 9, 0),
new Date(2026, 7, 12, 11, 30),
]
</script>
<template>
<div class="grid gap-3">
<div v-for="row in rows" :key="row.title" class="flex items-baseline justify-between gap-6 text-sm">
<span class="opacity-70">{{ row.title }}</span>
<GrDuration :value="row.value" />
</div>
<div class="flex items-baseline justify-between gap-6 text-sm">
<span class="opacity-70">Совещание</span>
<GrDuration :value="meeting" />
</div>
<div class="flex items-baseline justify-between gap-6 text-sm">
<span class="opacity-70">Страница открыта</span>
<GrDuration :value="started" />
</div>
<p class="showcase-demo-text text-sm opacity-70">
«Сборка заняла» показывает <strong>2 ч 59 мин</strong>, а не «3 ч»: младшее
отбрасывается, а не округляется. Последняя строка тикает сама — такт
выбирается по младшей показанной единице, и таймер в приложении один на всех.
</p>
</div>
</template>