The Machines · Core numerics

clock

Time and Timing — machines/clock.shoddy

the clock machine's icon

Summary

clock is the friendly layer over Shoddy's three timing built-ins: Ticks, Sleep, and Clock. It serves any console program that needs to measure how long something took, stamp a log line with the date and time, wait until a moment arrives, or turn a raw duration into readable words. It gives you Elapsed and TimeIt for timing, SleepUntil for pacing, Stamp and friends for ISO-8601 timestamps (the international year-first format, like 2026-07-20T14:03:07), and FormatDuration for turning milliseconds into "1m 04.2s". The built-ins underneath are always callable without including anything. This machine is simply the derived, window-independent convenience layer over them, in the same spirit that vt100 is a derived layer over Chr.

A Brief History of the Epoch

When Unix needed a way to remember a moment, its authors did not store a calendar date — they stored a count. Pick one instant, call it zero, and every other moment is just a number of seconds before or after it. The first edition of the manual, in 1971, counted sixtieths of a second from the start of that year. That counter overflowed a 32-bit word — the fixed-size number box computers of the day used — in under three years. So the count was recut to whole seconds, and the zero settled where it has been ever since: midnight at the start of the 1st of January 1970, UTC (the world's reference time, unshifted by any time zone). That arbitrary midnight became the most load-bearing instant in computing. Every timestamp in this repository, every file's modified-time, every certificate's expiry is a distance from it. The famous year-2038 problem is nothing more than the seconds count outgrowing a signed 32-bit box — which a Shoddy Number, being a double, sails past without noticing. Clock() hands you the civil pieces of "now", and julian turns them back into exactly this kind of count. Fifty years on, nobody has found a better way to remember a moment than a number.

Why It's Useful

Programs need to know about time in two quite different ways, and the whole design of this machine turns on keeping them apart. Sometimes you want to measure: how long did that computation take, how much time has passed since the last frame, is it time to draw again yet? And sometimes you want to stamp: what is the wall-clock date and time right now, so I can label this log line or name this file? Those sound similar, but they demand opposite things from a clock. Shoddy gives you two clocks precisely so you never have to choose the wrong one.

Ticks() is for measuring. It counts milliseconds since your program started. And — this is the key word — it is monotonic: it only ever goes forward, at a steady pace, never jumping. That makes it perfect for deltas, frame pacing, and benchmarks, where all you care about is how much time elapsed between two readings. It is also the only clock animation should ever use, because a clock that never jumps backward is a clock that never makes your animation stutter or leap.

Clock() is for stamping. It reports the wall-clock time — the actual calendar date and time on the wall — as a 7-element array: [year, month, day, hour, minute, second, ms]. That's exactly what you want on a log line or in a filename. But wall-clock time is not monotonic. Whenever the operating system adjusts the system clock, Clock() jumps with it — a daylight-saving change, or a network time sync nudging the clock a second or two either way. If you measure a duration by subtracting two Clock() readings, a time sync in the middle can hand you a negative answer, or a duration off by an hour. That's why the two jobs get two clocks: use Ticks() to measure, use Clock() to stamp, and never mix them.

User's Guide

For measuring, read Ticks() yourself and hand it to Elapsed, or let TimeIt time a function for you. For stamping, Stamp and its relatives read Clock() and hand you a ready-made string. For pacing, SleepUntil waits until a target tick arrives.

Include "clock.shoddy"

Def Work()
    Sleep(1200)                           ' pretend this is real work

Def Main()
    Let took = TimeIt(Work)               ' run Work, return ms it took
    Print(FormatDuration(took))           ' 1.2s

    Let start = Ticks()
    ' ... some loop body ...
    Print(Elapsed(start))                 ' ms since start, monotonic

    Print(Stamp())                        ' 2026-07-20T14:03:07.042
    Print(StampDate())                    ' 2026-07-20
    Print(StampTime())                    ' 14:03:07.042

    SleepUntil(Ticks() + 500)             ' wait out the rest of a 500ms beat

A few things worth remembering:

Builtins

The three timing words the runtime dispatches — not defined here, documented here

These are not clock's Defs. The engine dispatches them, and a Def whose name is a builtin is refused. That is why no word in this machine may be called Ticks, Sleep or Clock. They are listed on this page because clock is the machine whose domain they belong to, and they need no Include. The same three are documented in machines/clock.shoddy's own header block. All three are effectful.

WordDescription
Ticks()Monotonic milliseconds since the program started, fractional. The clock for measuring — frame pacing, deltas, benchmarks — and the only one animation may use, because it never jumps. Elapsed and TimeIt below are the ordinary ways to reach it.
Sleep(ms)Yield the thread for that many milliseconds; 0 or less waits not at all. This is what a poll loop puts between tries, so a non-blocking socket or a scribbler window does not spin a core. SleepUntil below is the deadline form.
Clock()Wall-clock now, as a 7-element Array: year, month, day, hour, minute, second, millisecond. The clock for stamping — logs, filenames — and not for measuring. It jumps whenever the operating system adjusts system time, which is the whole reason Ticks exists separately. It is a flat Array rather than a record because a builtin cannot reach a Type; Stamp and the words below lift it into readable shapes.

Word Reference

Every word, over the Ticks/Sleep/Clock builtins

Measuring and pacing

WordDescription
Elapsed(since)Milliseconds that have passed since an earlier Ticks() reading you pass in as since. Monotonic and drift-free — the right way to time things.
TimeIt(f)Runs the function f and hands back how many milliseconds it took. A one-line benchmark.
SleepUntil(t)Sleeps until Ticks() reaches the target t. If t is already in the past, returns immediately rather than waiting — good for keeping a loop on a steady beat.

Stamping the wall clock

WordDescription
Stamp()The current date and time as one ISO-8601 string, like "2026-07-20T14:03:07.042". Reads the wall clock exactly once, so the date and time halves are always from the same instant.
StampDate()Just the date part of now, like "2026-07-20".
StampTime()Just the time part of now, like "14:03:07.042".
DateOf(c)Formats the date from a Clock() array c ([year, month, day, ...]) as "YYYY-MM-DD". Use it when you've already taken a Clock() reading of your own.
TimeOf(c)Formats the time from a Clock() array c as "HH:MM:SS.mmm", milliseconds and all.
ClockDate()Today, as julian's JulDate — the one bridge between this capability and the pure calendar. One Clock() read, so it cannot straddle midnight. Everything a date can do (serials, Julian day numbers, weekdays, date arithmetic) happens over in julian, purely, after this word has run.

Formatting a duration

WordDescription
FormatDuration(ms)Turns a number of milliseconds into readable words: "4.2s" under a minute, "1m 04.2s" at or above one, seconds to one decimal place. Whole seconds stay bare, so 65 seconds is "1m 05s" and not "1m 05.0s".

Sharing a program with money? Include both bare — they get along. (They once collided over twin private Pad2 helpers. Both now lean on str's PadZero instead, each bringing it in through its own Include, and compiled include-once means one copy serves everybody.)

Who Uses It

UserHow
pac-vt100SleepUntil paces the loop to one tick per 100 ms frame without drift.

The Machines It Uses

MachineWhy
strPadZero keeps the stamp fields two and three digits wide — "07", never "7".
julianFor one word: ClockDate(), the bridge from “now” into the pure calendar. The direction is load-bearing. clock includes julian and never the other way round, so fin, geo and ephemeris compute with dates while staying out of the capability system entirely.