Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

gobj-ui: Time and periods

Time and periods

A period is a bucket with an alignment: the hour, the day, the week, the quarter. A rolling window is not a bucket: it ends at now and reaches back. The two live next to each other, because a live log is read with a window and a report is read with a bucket.

C_YUI_PERIOD draws these, and every function here works without it.

Source code: src/yui_time.js


The catalog

YUI_PERIODS

The periods that carry a name: minute, 5min, 15min, hour, day, week, fortnight, month, bimester, quarter, semester, year and decade.

Each one is a pair of a unit and a count. The algebra knows nothing about the identifiers, and it works with any pair. These are the ones with a name, and a bucket with a name gives a better label than a range: "Q3 2026" reads better than "jul – sep 2026".

YUI_PERIODS_DEFAULT

The set that a navigator takes by default: ["hour", "day", "week", "month", "year"]. The rest of the catalog is one line of configuration away.

YUI_ROLLING

The rolling windows: 1h, 6h, 24h, 7d and 30d.


The algebra

period_spec(period)

Reads an identifier of the catalog, or a specification of its own, and gives {id, unit, count}. Returns null for anything that is not a bucket, such as the custom mode of a navigator.

period_start(period, anchor_ms)

Gives the start of the bucket that holds a time, as a local date.

The alignment is what makes a bucket a bucket. Each unit goes down to the natural origin of the unit above it, so the edges are the edges that a human holds already.

UnitIt aligns to
minuteThe hour. A bucket of 15 minutes starts at :00, :15, :30 and :45.
hourThe local midnight. A bucket of 6 hours starts at 00, 06, 12 and 18.
day1970-01-01. A count of one gives the plain day. A bucket of 10 days has no origin in the calendar, so it aligns to the epoch.
weekThe week of the epoch, and the week begins on Monday.
monthJanuary. That is the reason why a count of 2, 3, 4, 6 or 12 gives the bimesters, the quarters and the semesters of the calendar.
yearThe year 0. A decade begins in 2020, and not in 2021.

period_shift(period, anchor_ms, delta)

Gives the start of the bucket that is delta buckets away. A negative delta goes back.

The arithmetic is of the calendar, and never of the milliseconds. A step of one month carries the year by itself, and a step of one day across a change of summer time lands on the midnight again.

period_bounds(period, anchor_ms)

Gives the bucket as {from, to} in milliseconds.

period_bounds_epoch(period, anchor_ms, ms)

The same bucket, in the unit that the consumer speaks. A topic of timeranger keeps its times in seconds, and its system_flag says when it keeps them in milliseconds. This is the function that a builder of a query calls.

rolling_bounds(rolling, ms, now_ms)

Gives a rolling window as {from, to}.

The end to stays open, at 0. An iterator with no upper end keeps taking the records that arrive while the card is on the screen. An end that is pinned to now freezes the window at the instant of the click.

is_current_period(period, anchor_ms, now_ms)

Tells if this is the last bucket, which is the one that holds now. It is what makes the arrow of “next” grey, and what tells the navigator that it is at home.

infer_period(from, to, candidates, ms)

Finds the bucket that a pair of ends describes, and gives {period, anchor}. Returns null when no candidate matches.

Only an exact match counts: both ends must land on the edges of the bucket, in the unit that the consumer writes. candidates are tried in their order.


Labels

period_name(period, t)

Gives the name of a granularity, which is what a segmented control shows. It takes the identifier of the specification as the key of the translation, so an application that declares {id: "quarter"} gets its own word as soon as it adds the key.

period_label(period, anchor_ms, t, locale)

Gives the name of the bucket that a navigator sits on, which is the text between the two arrows.

PeriodExample
day"Today", "Yesterday" or "13 jul 2026"
week"Week 27", with the year when it is not this one
month"July", with the year when it is not this one
quarter"Q3 2026"
semester"H2 2026"
year"2026"
decade"2020 – 2029"
any otherThe edges of the bucket: "1 jul – 31 aug 2026"

The ones with a name have a name because a range reads worse. Everything that an application invents takes the range, which is always true.

safe_locale(locale)

Gives a locale that Intl accepts.


Conversions

epoch_to_ms(value, ms)

Changes a time of the consumer into milliseconds. With ms set to true the value is in milliseconds already.

ms_to_epoch(value_ms, ms)

Changes milliseconds into the unit of the consumer.

epoch_to_local_input(value, ms)

Changes a time into the form that an input of date and time takes.

local_input_to_epoch(v, ms)

Changes the value of an input of date and time into a time.

fmt_epoch(value, ms)

Writes a time for a human.

iso_week(d)

Gives the number of the week of a date, in the form of ISO 8601, with Monday as the first day.