Antra

Standardbibliothek

Antra::Time

import Antra::Time

Small UTC time helpers for logger timestamps, deadlines and comparisons.

Antra::Time is a deliberately small time surface for absolute timestamps, lightweight formatting and simple parsing.

Practical defaults:

  • %Time{...} is a plain copyable data value
  • stored values stay in UTC with unix_ms precision
  • local time is a display or parse option, not a second stored representation
  • to_iso8601(...) and format(...) cover the most common output paths
  • strftime(...) / strptime(...) expose the thinner host-backed Perl path
  • format_locale(...) and parse(..., [locale: ...]) cover small :de and :us enduser forms
  • monotonic_us(), monotonic_resolution_us(), measure_ms(...) and measure_avg_ms(...) cover runtime measurement without wall-clock jumps

Core %Time{...} shape:

  • unix_ms: absolute Unix timestamp in milliseconds
  • zone: currently always :utc inside the stored value

Common output choices:

  • machine/API timestamp: Time.to_iso8601(time)
  • small readable date: Time.format(time, :date)
  • custom display: Time.format(time, "MMM dd, yyyy hh:mm:ss a")
  • existing Perl/Unix format: Time.strftime(time, "%Y-%m-%d %H:%M:%S")
  • German default: Time.format_locale(time, :de)
  • US default: Time.format_locale(time, :us)
  • direct calendar/time parts: Time.year(time), Time.month(time), Time.day(time), Time.hour(time)

Locale preset defaults:

  • :de, :datetime -> 31.12.2026 23:59:59
  • :de, :date -> 31.12.2026
  • :de, :time -> 23:59:59
  • :us, :datetime -> 12/31/2026 11:59:59 PM
  • :us, :date -> 12/31/2026
  • :us, :time -> 11:59:59 PM

Parse paths stay intentionally small:

  • Time.parse("2026-12-31T23:59:59.123Z") for ISO-8601
  • Time.parse("31.12.2026 23:59:59", [locale: :de]) for German presets
  • Time.parse("12/31/2026 11:59:59 PM", [locale: :us]) for US presets
  • Time.strptime("2026-12-31 23:59:59", "%Y-%m-%d %H:%M:%S") for explicit host formats

Time.format(...) template symbols:

  • yyyy year with century, yy short year
  • MMMM full month name, MMM short month name, MM month number
  • dddd full weekday name, ddd short weekday name
  • dd day of month
  • HH 24-hour, hh 12-hour
  • mm minute, ss second, SSS millisecond
  • a lowercase am/pm
  • Z UTC Z or local numeric offset like +02:00

Literal text stays literal. For text that would otherwise look like a token, quote it with single quotes, for example:

  • "yyyy-MM-dd 'at' HH:mm:ss"

For host-native %Y, %m, %d, ... directives use Time.strftime(...) or Time.strptime(...) instead of the template parser.

If an input text has no explicit offset, [zone: :utc | :local] decides how that wall-clock time is interpreted.

Field helpers stay numeric and follow the same projection rules:

  • Time.year/month/day(...)
  • Time.hour/minute/second/millisecond(...)
  • Time.day_of_year(...)

They default to UTC and accept the same [zone: :utc | :local] option when you want local calendar fields instead of UTC ones.

Example:

import Antra::Duration as Duration
import Antra::Time as Time

my start = Time.from_unix_ms(0)
my deadline = Time.add(start, 90sec)
my [result, elapsed_ms] = Time.measure_ms(|| -> 42)
my [_last, average_ms] = Time.measure_avg_ms(5, || -> 42)
Time.sleep(Duration.parse(dur"PT0.01S"))

println(Time.to_iso8601(deadline))
println(Time.format(deadline, "yyyy-MM-dd HH:mm:ss.SSS Z"))
println(Time.format_locale(deadline, :de))
println(Time.year(deadline))
println(Time.hour(deadline, [zone: :local]))
println(Time.diff_as(deadline, start, :min))
println(elapsed_ms)
println(average_ms)

Example:

import Antra::Time as Time

my de = Time.parse("31.12.2026 23:59:59", [locale: :de])
my us = Time.parse("12/31/2026 11:59:59 PM", [locale: :us])
my raw = Time.strptime("2026-12-31 23:59:59", "%Y-%m-%d %H:%M:%S")

println(Time.to_iso8601(de))
println(Time.to_iso8601(us))
println(Time.to_iso8601(raw))

Überblick#

Fuer echte Zeitpunkte gibt es jetzt ein kleines UTC-orientiertes Paket Antra::Time.

Der erste Wertkern ist bewusst schlicht:

%Time{
    unix_ms: 1719150000123,
    zone: :utc
}

Damit laesst sich ein Zeitpunkt als normaler Datenwert herumreichen, matchen, loggen und mit Duration kombinieren, ohne sofort eine groessere DateTime-/Zeitzonen-Surface einzufuehren.

Ein typischer Einstieg ist:

import Antra::Duration as Duration
import Antra::Time as Time

my epoch = Time.from_unix_ms(0)
my deadline = Time.add(epoch, 90sec)

println(Time.to_iso8601(epoch))
println(Time.to_iso8601(deadline))
println(Time.compare(epoch, deadline))
println(Duration.to_sec(Time.diff(deadline, epoch)))
println(Time.to_iso8601(deadline, [zone: :local]))
println(Time.diff_as(deadline, epoch, :min))

Die erste kleine API ist bewusst knapp:

  • Time.now_utc()
  • Time.monotonic_us() fuer monotone Laufzeitmessung
  • Time.monotonic_resolution_us() fuer die gemeldete Host-Clock-Aufloesung
  • Time.from_unix_ms(...), Time.to_unix_ms(...)
  • Time.from_unix(...), Time.to_unix(...)
  • Time.to_iso8601(...), optional mit [zone: :utc | :local]
  • Time.format(..., :iso8601 | :date | :time), optional mit [zone: :utc | :local]
  • Time.format(..., "MM dd"), Time.format(..., "MMM dd, yyyy hh:mm:ss a") oder Time.format(..., "yyyy-MM-dd HH:mm:ss.SSS Z") fuer kleine eigene Layouts
  • Time.strftime(..., "%Y-%m-%d %H:%M:%S"), optional mit [zone: :utc | :local]
  • Time.format_locale(..., :de | :us), optional mit [kind: :date | :time | :datetime, zone: :utc | :local]
  • Time.year/month/day/hour/minute/second/millisecond/day_of_year(...), optional mit [zone: :utc | :local]
  • Time.parse(...) fuer ISO-8601, Time.parse(..., [locale: :de | :us, ...]) fuer Locale-Presets
  • Time.strptime(..., "%Y-%m-%d %H:%M:%S"), optional mit [zone: :utc | :local]
  • Time.add(time, duration)
  • Time.measure_ms(fn) fuer [result, elapsed_ms] ohne Wall-Clock-Spruenge
  • Time.diff(later, earlier)
  • Time.diff_as(later, earlier, :sec | :min | :hour | :day | :week)
  • Time.before?(a, b), Time.after?(a, b)
  • Time.compare(a, b) mit :lt, :eq, :gt wie bei <=>
  • Time.time?(...)

Der MVP bleibt absichtlich eng:

  • der gespeicherte %Time{...}-Wert bleibt aktuell immer bei zone: :utc
  • lokale Zeit gibt es bisher nur als Ausgabe-Projektion ueber [zone: :local]
  • Parse ist bewusst klein und deckt nur ISO-8601, die eingebauten :de/:us-Presets und explizite strptime-Formate ab
  • Time ist im Moment eher ein kleiner UTC-Zeitpunkt als ein grosses DateTime-Framework

Bei Time.format(...) gibt es neben den benannten Styles auch einen kleinen Template-Modus. Aktuell unterstuetzt er bewusst nur einen sicheren Kern an gut lesbaren Tokens:

  • yyyy, yy
  • MMMM, MMM, MM
  • dddd, ddd, dd
  • HH, hh, mm, ss, a
  • SSS
  • Z

Damit sind Formen wie "MM dd", "MMM dd, yyyy hh:mm:ss a", "dddd, MMMM dd 'at' hh:mm:ss a", "yyyy-MM-dd" oder "yyyy-MM-dd HH:mm:ss.SSS Z" moeglich, ohne gleich ein grosses Kalender- Framework einzufuehren. Literaler Text innerhalb des Templates kann mit einfachen Quotes markiert werden.

Wenn du stattdessen direkt das aus Perl und vielen Unix-Tools bekannte strftime-Format nutzen willst, gibt es zusaetzlich Time.strftime(...). Das ist der duennere Pfad fuer bestehende %Y-%m-%d-Formate, waehrend Time.format(...) eher die lesbarere Antra-Oberflaeche fuer neue Skripte ist.

Fuer haeufige Enduser-Formen gibt es zusaetzlich kleine Locale-Presets:

  • Time.format_locale(time, :de) -> 31.12.2026 23:59:59
  • Time.format_locale(time, :de, [kind: :date]) -> 31.12.2026
  • Time.format_locale(time, :us) -> 12/31/2026 11:59:59 PM
  • Time.format_locale(time, :us, [kind: :time]) -> 11:59:59 PM

Das ist bewusst kein allgemeines i18n-System, sondern nur ein kleiner fester Preset-Satz fuer Deutschland und US-Formen.

Wenn du statt formatierter Strings lieber direkt Zahlen brauchst, gibt es zusätzlich kleine Feld-Getter:

  • Time.year(time)
  • Time.month(time)
  • Time.day(time)
  • Time.hour(time)
  • Time.minute(time)
  • Time.second(time)
  • Time.millisecond(time)
  • Time.day_of_year(time)

Diese Helper sind bewusst nur Projektionen auf denselben absoluten Zeitpunkt. Sie aendern also keinen %Time{...}-Wert, sondern lesen ihn nur als UTC oder mit [zone: :local] als lokale Kalender-/Uhrzeit-Sicht aus.

Zum Einlesen gibt es den passenden Gegenpfad:

  • Time.parse("2026-12-31T23:59:59.123Z") fuer ISO-8601
  • Time.parse("31.12.2026 23:59:59", [locale: :de]) fuer deutsche Presets
  • Time.parse("12/31/2026 11:59:59 PM", [locale: :us]) fuer US-Presets
  • Time.strptime("2026-12-31 23:59:59", "%Y-%m-%d %H:%M:%S") fuer freie Host-Formate

Wenn die Eingabe selbst keinen Offset traegt, bestimmt [zone: :utc | :local], wie die gelesene Uhrzeit interpretiert wird.

Typen#

typedef Zone = :utc | :local

Display zone of a %Time{...} value.

typedef Locale = :de | :us

Locale presets of format_locale and parse.

typedef LocaleKind = :date | :time | :datetime

What a locale preset shows.

typedef FormatStyle = :iso8601 | :date | :time

Preset styles of format.

typedef DiffUnit = :sec | :min | :hour | :day | :week

Units of diff_as.

Funktionen#

fn add(time, duration)
fn add(_time, duration)

Returns a new %Time{...} shifted by the given Duration.

fn after?(a, b)

Returns true if a is later than b.

fn before?(a, b)

Returns true if a is earlier than b.

fn compare(a, b)

Compares two %Time{...} values and returns :lt, :eq or :gt.

fn day(time)
fn day(time, [zone :- Zone = :utc, ..])
fn day(_time, [zone: = :utc, ..])

Returns the projected day of month in the range 1..31.

fn day_of_year(time)
fn day_of_year(time, [zone :- Zone = :utc, ..])
fn day_of_year(_time, [zone: = :utc, ..])

Returns the projected day of year in the range 1..366.

fn diff(later, earlier)

Returns the signed Duration difference later - earlier as %sec[...].

fn diff_as(later, earlier)
fn diff_as(later, earlier, unit :- DiffUnit)

Returns the signed time difference projected into a fixed unit.

fn format(time)
fn format(time, [zone: = :utc, ..])
fn format(time, template :- String | FormatStyle | [zone :- Zone?, ..])
fn format(time, template :- String | FormatStyle, [zone :- Zone = :utc, ..])
fn format(time, style)
# … 3 weitere Klauseln

Formats a %Time{...} value using the default :iso8601 style.

Named styles stay available via :iso8601, :date and :time. For custom layouts you can also pass a template string such as "MM dd", "MMM dd, yyyy hh:mm:ss a" or "yyyy-MM-dd HH:mm:ss.SSS Z".

fn format_locale(time, locale :- Locale)
fn format_locale(time, locale :- Locale, [kind :- LocaleKind = :datetime, zone :- Zone = :utc, ..])

Formats a %Time{...} value using locale presets for Germany or the US.

fn from_unix(sec)

Builds a %Time{...} value from Unix seconds.

fn from_unix_ms(ms)

Builds a %Time{...} value from Unix milliseconds.

fn hour(time)
fn hour(time, [zone :- Zone = :utc, ..])
fn hour(_time, [zone: = :utc, ..])

Returns the projected hour in the range 0..23.

fn measure_avg_ms(n, fn)
fn measure_avg_ms(n, _fn)

Runs fn n times and returns [last_result, average_elapsed_ms].

This is intentionally a tiny helper for focused microbenchmarks where the caller still wants control over output formatting and setup.

fn measure_ms(fn)

Runs fn and returns [result, elapsed_ms] based on the monotonic clock.

The elapsed value is a plain millisecond number and may be fractional for very short calls.

fn millisecond(time)
fn millisecond(time, [zone :- Zone = :utc, ..])
fn millisecond(_time, [zone: = :utc, ..])

Returns the millisecond fraction in the range 0..999.

fn minute(time)
fn minute(time, [zone :- Zone = :utc, ..])
fn minute(_time, [zone: = :utc, ..])

Returns the projected minute in the range 0..59.

fn monotonic_resolution_us()

Returns the reported monotonic clock resolution in microseconds.

This is the host clock granularity that monotonic_us() and measure_ms(...) build on, not a promise about end-to-end benchmark noise or scheduler jitter.

fn monotonic_us()

Reads a monotonic host clock in microseconds.

This is intended for runtime measurement and timeout bookkeeping, not for wall-clock timestamps or user-visible calendar output.

fn month(time)
fn month(time, [zone :- Zone = :utc, ..])
fn month(_time, [zone: = :utc, ..])

Returns the projected month number in the range 1..12.

fn now_utc()

Reads the current host time as a %Time{...} value in UTC.

fn parse(text)
fn parse(text, [locale :- Locale, kind :- LocaleKind = :datetime, zone :- Zone = :utc, ..])
fn parse(text, [zone :- Zone = :utc, ..])
fn parse(text, format)
fn parse(text, format, [zone :- Zone = :utc, ..])
fn parse(_text, format)

Parses ISO-8601 text, locale preset text, or explicit strptime text into a %Time{...} value.

fn second(time)
fn second(time, [zone :- Zone = :utc, ..])
fn second(_time, [zone: = :utc, ..])

Returns the projected second in the range 0..59.

fn sleep(duration)

Blocks for approximately the given Duration and returns undef.

fn strftime(time, format)
fn strftime(time, format, [zone :- Zone = :utc, ..])
fn strftime(_time, format)

Formats a %Time{...} value using Perl-style strftime directives such as "%Y-%m-%d %H:%M:%S".

fn strptime(text, format)
fn strptime(text, format, [zone :- Zone = :utc, ..])
fn strptime(_text, format)

Parses text using Perl-style strptime directives such as "%Y-%m-%d %H:%M:%S".

fn time?(value)

Returns true if value is a valid %Time{unix_ms: ..., zone: :utc} value.

fn to_iso8601(time)
fn to_iso8601(time, [zone :- Zone = :utc, ..])
fn to_iso8601(_time, [zone: = :utc, ..])

Formats a %Time{...} value as RFC-3339 / ISO-8601.

fn to_unix(time)

Returns the Unix second timestamp from a %Time{...} value.

fn to_unix_ms(time)

Returns the Unix millisecond timestamp from a %Time{...} value.

fn year(time)
fn year(time, [zone :- Zone = :utc, ..])
fn year(_time, [zone: = :utc, ..])

Returns the projected calendar year.

Quelle dieser Seite