# Clock `clock/*` is the TCL-style date and time ensemble. Every value crossing the namespace boundary is a number: epoch seconds is the universal currency (all of `format`, `scan`, and `add` take or return it), and `clock/format` / `clock/scan` are strict duals driven by one strptime-style specifier table. `clock/clicks` is the exception — a monotonic nanosecond counter, meaningful only as a difference between two samples. All functions are registered in every host and need no import. ## Functions | Function | Purpose | |---|---| | `clock/seconds` | Current wall-clock time as integer seconds since the Unix epoch | | `clock/millis` | Current wall-clock time in epoch milliseconds | | `clock/micros` | Current wall-clock time in epoch microseconds | | `clock/clicks` | High-resolution monotonic timestamp in nanoseconds (not epoch-anchored) | | `clock/format time ?format? ?timezone?` | Format epoch seconds as a string; spec-map form `%{ :format :timezone :locale }` | | `clock/scan str %{ :format ... }` | Parse a string into epoch seconds; `:format` is required | | `clock/add time n :unit ...` | Calendar-aware date arithmetic; spec-map form `%{ :days n :months n :timezone ... }` | Wall clock vs monotonic clock: - `clock/seconds`, `clock/millis`, and `clock/micros` read the system wall clock. They can jump backwards when the clock is adjusted, so they are wrong for measuring durations. - `clock/clicks` reads a monotonic counter. Its origin is unspecified and may differ between runs and processes — only the difference between two readings is meaningful. Use it for elapsed-time and profiling measurements. Formatting and parsing: - `clock/format` defaults to `"%Y-%m-%dT%H:%M:%S%z"` (ISO-8601) and to the local timezone. Timezones are `"UTC"`, `"local"`, or a signed `+HHMM` / `+HHMMSS` offset. - Both functions share one specifier table — `%Y %m %d %H %M %S %j` %u %A %B %z` and the rest, plus the composites `%T %R %D %F %r %+`.` - `clock/scan` is deliberately strict: free-form parsing is not supported and `:format` is mandatory. Missing time-of-day fields resolve to midnight; missing date fields fall back to `:base` (default: now); a `%z` in the input overrides the `:timezone` option. Malformed input returns `%Err{:message}`. Calendar arithmetic: - Units are `:second :minute :hour :day :week :month :year`; positional deltas apply left to right. `:second`/`:minute`/`:hour` add fixed durations directly to the epoch value. - `:day` and `:week` are wall-clock-preserving: `clock/add` converts to broken-down time in `:timezone`, adds the days, and converts back. - `:month` and `:year` are calendar arithmetic and clamp the day to the target month's last day (Jan 31 + 1 month becomes Feb 28/29). - `clock/add` interprets calendar units in `:timezone` (default: `"local"`). Pass the timezone in the spec-map form so the arithmetic happens in the same zone as the timestamp you built with `clock/scan`. ## Examples ### Reading the wall clock
set now [clock/seconds]
echo [clock/format $now]                            # ISO-8601, local tz
echo [clock/format $now "%Y-%m-%d %H:%M:%S" "UTC"]
echo [clock/format 0 "%A, %B %d %Y" "UTC"]          # Thursday, January 01 1970
echo [clock/millis]                                 # epoch milliseconds
### Parsing and calendar arithmetic
set t [clock/scan "2024-01-31 14:30:00"
       %{ :format "%Y-%m-%d %H:%M:%S" :timezone "UTC" }]

# Month-end clamping, using the same timezone for the arithmetic:
echo [clock/format [clock/add $t %{ :months 1 :timezone "UTC" }] "%F" "UTC"]
# => "2024-02-29"

# Fixed-duration units add to the epoch value directly:
echo [clock/format [clock/add $t 26 :hours] "%F %T" "UTC"]
# => "2024-02-01 16:30:00"
### Elapsed time with the monotonic clock
set start [clock/clicks]
# ... work ...
set elapsed-ns ([clock/clicks] - $start)
echo "took" ($elapsed-ns / 1000000) "ms"
## See Also - Concurrency — `sleep`, `after`, and timer scheduling - Logging — log records carry millisecond epoch timestamps