first_cycle : Cycle
Initial cycle sample used before any simulation time has elapsed.
Simulation-time helpers and wall-clock timestamps.
Use input.time for animation and simulation. The host samples one Cycle
per update!; elapsed_seconds is the simulation duration of that cycle.
now! reads the nondeterministic wall clock. It is legal in init!,
update!, and tasks, and refused in render!; it does not wait.
first_cycle : Cycle
Initial cycle sample used before any simulation time has elapsed.
to_seconds : U64 -> F32
Convert a nanosecond duration to seconds.
expect Time.to_seconds(500_000_000) == 0.5
delta_seconds : U64, U64 -> F32
Seconds elapsed between two clock samples. current must be at or after
previous. Derive a delta from consecutive timestamp samples:
dt = Time.delta_seconds(model.last_tick, timestamp_nanos)
now! : () => Timestamp
Read the world's clock.
Legal in init!, update!, and tasks; refused in render!. render! is
given the model rather than an input, and should draw the instant the
model already decided on.
Two calls in one update! can return different values, and an app that wants
one instant for a whole cycle should read it once and keep it in the model.
Nothing else about the platform changes with it: the calendar is explicitly
nondeterministic, it is not what a capture paces, and it is not what
animation should move on.
Timing samples attached to one host cycle.
Time.Timestamp :: # (opaque)
An instant on the world's clock, as seconds and nanoseconds since the Unix epoch.
Always normalized: nanosecond is less than 1,000,000,000, and seconds
are floor-based, so one nanosecond before the epoch is
{ seconds: -1, nanosecond: 999_999_999 } rather than a negative
fraction of second zero. That is what makes ordering and differences
work the same on both sides of 1970.
A timestamp has no time zone, no calendar policy, and no
leap-second model. Everything this module formats is UTC; local time and
calendar arithmetic belong to a pure Roc package, and a Timestamp is
the value to pass to it.
is_eq : _
Compare two of these values.
epoch : Timestamp
The Unix epoch, 1970-01-01T00:00:00Z.
A neutral value for a model field that has not read the clock yet, and the base every other instant is measured from.
from_parts : { seconds : I64, nanosecond : U32 } -> Try(Timestamp, [InvalidNanosecond])
Build a timestamp from its two parts.
InvalidNanosecond is a whole second or more of fractional part,
which is a value that does not describe an instant rather than a
clock that failed.
from_nanos_since_epoch : I128 -> Try(Timestamp, [OutOfRange])
Build a timestamp from signed nanoseconds since the epoch.
OutOfRange is an instant further from 1970 than a signed 64-bit
count of seconds can hold, which is about 292 billion years either
way.
seconds_since_epoch : Timestamp -> I64
Whole seconds since the epoch, rounded towards the epoch's past.
subsecond_nanoseconds : Timestamp -> U32
The fractional nanoseconds within this timestamp's own second.
nanos_since_epoch : Timestamp -> I128
Signed nanoseconds since the epoch.
difference_nanos : Timestamp, Timestamp -> I128
Nanoseconds from the first instant to the second, negative when the second is the earlier one.
to_iso_8601 : Timestamp -> Str
Format as an ISO 8601 instant in UTC, to the second.
expect Time.Timestamp.epoch.to_iso_8601() == "1970-01-01T00:00:00Z"
The Z is always literal: this platform formats UTC and nothing
else, so there is no offset to get wrong. Years are padded to four
digits, and a year before the common era is written with a leading
minus and as many digits as it needs.
to_iso_8601_nanos : Timestamp -> Str
Format as an ISO 8601 instant in UTC, to the nanosecond.
The fractional part is always nine digits, so timestamps sort as strings in the same order they sort as instants.
to_file_stamp : Timestamp -> Str
Format as an instant that is legal in a filename on every platform.
The same shape as to_iso_8601 with - where the colons would be,
because a colon cannot appear in a path on Windows:
expect Time.Timestamp.epoch.to_file_stamp() == "1970-01-01T00-00-00Z"