Date & Time
All timestamps in Sema are UTC Unix timestamps — the number of seconds since January 1, 1970 00:00:00 UTC. Timestamps are floating-point numbers with millisecond fractional precision.
TIP
All time/ functions operate in UTC. There is no timezone conversion support — if you need local time handling, compute the offset manually with time/add.
Current Time
time/now
Return the current time as a UTC Unix timestamp in seconds, with fractional milliseconds.
(time/now) ; => 1707955200.123The integer part is seconds since the Unix epoch; the fractional part provides millisecond precision.
(define now (time/now))
(println "Current timestamp: " now)
;; Extract just the seconds (truncate fractional part)
(define whole-seconds (floor now))time-ms
Return the current time as Unix milliseconds (integer). Defined in the system module but useful alongside datetime operations.
(time-ms) ; => 1707955200123Formatting
time/format
Format a UTC Unix timestamp using a strftime-style format string.
(time/format timestamp format-string) ; => string(define ts 1736943000.0) ; 2025-01-15 12:10:00 UTC
(time/format ts "%Y-%m-%d") ; => "2025-01-15"
(time/format ts "%H:%M:%S") ; => "12:10:00"
(time/format ts "%Y-%m-%d %H:%M:%S") ; => "2025-01-15 12:10:00"
(time/format ts "%A, %B %d, %Y") ; => "Wednesday, January 15, 2025"
(time/format ts "%F") ; => "2025-01-15" (shorthand for %Y-%m-%d)
(time/format ts "%T") ; => "12:10:00" (shorthand for %H:%M:%S)Parsing
time/parse
Parse a date string into a UTC Unix timestamp using a strftime-style format string. The input is treated as a UTC naive datetime — no timezone information is expected or applied.
(time/parse date-string format-string) ; => float (UTC timestamp)(time/parse "2025-01-15 12:10:00" "%Y-%m-%d %H:%M:%S") ; => 1736943000.0
(time/parse "2025-01-15 00:00:00" "%Y-%m-%d %H:%M:%S") ; => 1736899200.0
(time/parse "15/01/2025 14:30:00" "%d/%m/%Y %H:%M:%S") ; => 1736951400.0INFO
The format string must provide enough directives to fully specify a date and time. Parsing a date-only string like "%Y-%m-%d" without time components will fail — always include time directives (e.g., %H:%M:%S).
Roundtrip — formatting a timestamp and parsing it back yields the original value:
(define ts 1700000000.0)
(define formatted (time/format ts "%Y-%m-%d %H:%M:%S"))
(define parsed (time/parse formatted "%Y-%m-%d %H:%M:%S"))
(= parsed ts) ; => #tWARNING
time/parse returns whole seconds — sub-second precision from the original timestamp is lost when roundtripping through format/parse.
Date Decomposition
time/date-parts
Decompose a UTC Unix timestamp into a map of date/time components.
(time/date-parts timestamp) ; => map(define ts 1736943000.0) ; 2025-01-15 12:10:00 UTC
(define parts (time/date-parts ts))
(get parts :year) ; => 2025
(get parts :month) ; => 1
(get parts :day) ; => 15
(get parts :hour) ; => 12
(get parts :minute) ; => 10
(get parts :second) ; => 0
(get parts :weekday) ; => "Wednesday"The returned map contains these keys:
| Key | Type | Description | Example |
|---|---|---|---|
:year | integer | Four-digit year | 2025 |
:month | integer | Month (1–12) | 1 |
:day | integer | Day of month (1–31) | 15 |
:hour | integer | Hour (0–23) | 12 |
:minute | integer | Minute (0–59) | 10 |
:second | integer | Second (0–59) | 0 |
:weekday | string | Full weekday name | "Wednesday" |
The :weekday value is the full English weekday name: "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday".
Arithmetic
time/add
Add seconds to a timestamp. Returns a new timestamp. Use negative values to subtract.
(time/add timestamp seconds) ; => float (timestamp)(define ts 1736943000.0) ; 2025-01-15 12:10:00 UTC
(time/add ts 3600) ; one hour later => 1736946600.0
(time/add ts 86400) ; one day later => 1737029400.0
(time/add ts -3600) ; one hour earlier => 1736939400.0
(time/add ts (* 7 86400)) ; one week laterCommon durations in seconds:
| Duration | Seconds |
|---|---|
| 1 minute | 60 |
| 1 hour | 3600 |
| 1 day | 86400 |
| 1 week | 604800 |
| 30 days | 2592000 |
time/diff
Compute the difference between two timestamps in seconds. Returns t1 - t2 (the first argument minus the second). The result can be negative.
(time/diff t1 t2) ; => float (seconds)(define morning 1736935800.0) ; 2025-01-15 10:10:00 UTC
(define afternoon 1736943000.0) ; 2025-01-15 12:10:00 UTC
(time/diff afternoon morning) ; => 7200.0 (2 hours)
(time/diff morning afternoon) ; => -7200.0 (negative — morning is earlier)
(time/diff morning morning) ; => 0.0TIP
time/diff returns a signed value: positive when t1 > t2, negative when t1 < t2. Use abs if you need the absolute elapsed time regardless of order.
Delay
sleep
Pause execution for a given number of milliseconds. Returns nil.
(sleep milliseconds) ; => nil(sleep 1000) ; sleep for 1 second
(sleep 500) ; sleep for 500ms
(sleep 0) ; yield (no-op pause)Note that sleep takes milliseconds (not seconds), unlike the time/ functions which work in seconds.
strftime Format Directives
The time/format and time/parse functions use chrono strftime format directives. Here are the most common ones:
Date
| Directive | Description | Example |
|---|---|---|
%Y | Four-digit year | 2025 |
%m | Month (zero-padded, 01–12) | 01 |
%d | Day of month (zero-padded, 01–31) | 15 |
%e | Day of month (space-padded) | 15 |
%B | Full month name | January |
%b | Abbreviated month name | Jan |
%A | Full weekday name | Wednesday |
%a | Abbreviated weekday name | Wed |
%u | Day of week (1=Monday, 7=Sunday) | 3 |
%j | Day of year (001–366) | 015 |
%F | ISO 8601 date (%Y-%m-%d) | 2025-01-15 |
Time
| Directive | Description | Example |
|---|---|---|
%H | Hour, 24-hour (zero-padded, 00–23) | 12 |
%I | Hour, 12-hour (zero-padded, 01–12) | 12 |
%M | Minute (zero-padded, 00–59) | 10 |
%S | Second (zero-padded, 00–59) | 00 |
%p | AM/PM | PM |
%T | Time (%H:%M:%S) | 12:10:00 |
%R | Short time (%H:%M) | 12:10 |
Combined & Special
| Directive | Description | Example |
|---|---|---|
%c | Locale date and time | Wed Jan 15 12:10:00 2025 |
%s | Unix timestamp (seconds) | 1736943000 |
%Z | Timezone abbreviation | UTC |
%% | Literal % | % |
Common Patterns
Measuring elapsed time
(define start (time/now))
;; ... do some work ...
(define end (time/now))
(define elapsed (time/diff end start))
(println (format "Took ~a seconds" elapsed))ISO 8601 formatting
(define ts (time/now))
(time/format ts "%Y-%m-%dT%H:%M:%SZ") ; => "2025-01-15T12:10:00Z"
(time/format ts "%F") ; => "2025-01-15" (date only)Calculating "N days ago"
(define now (time/now))
(define one-week-ago (time/add now (* -7 86400)))
(define thirty-days-ago (time/add now (* -30 86400)))
(println "One week ago: " (time/format one-week-ago "%Y-%m-%d"))Formatting for display
(define ts (time/now))
(time/format ts "%A, %B %d, %Y") ; => "Wednesday, January 15, 2025"
(time/format ts "%I:%M %p") ; => "12:10 PM"
(time/format ts "%b %d at %H:%M") ; => "Jan 15 at 12:10"Checking the day of the week
(define parts (time/date-parts (time/now)))
(define day (get parts :weekday))
(if (or (= day "Saturday") (= day "Sunday"))
(println "It's the weekend!")
(println "It's a weekday."))Computing duration between dates
(define start (time/parse "2025-01-01 00:00:00" "%Y-%m-%d %H:%M:%S"))
(define end (time/parse "2025-03-15 00:00:00" "%Y-%m-%d %H:%M:%S"))
(define diff-seconds (time/diff end start))
(define diff-days (/ diff-seconds 86400))
(println (format "~a days between dates" diff-days))Edge Cases
Unix epoch
(time/format 0.0 "%Y-%m-%d %H:%M:%S") ; => "1970-01-01 00:00:00"
(time/date-parts 0.0)
; => {:day 1 :hour 0 :minute 0 :month 1 :second 0 :weekday "Thursday" :year 1970}Negative timestamps (dates before 1970)
(time/format -86400.0 "%Y-%m-%d") ; => "1969-12-31"
(time/format -31536000.0 "%Y-%m-%d") ; => "1969-01-01"Sub-second precision
time/now returns millisecond fractional precision. time/add and time/diff preserve fractional seconds. However, time/parse returns whole seconds only.
(define ts (time/add 1736943000.0 0.5)) ; add 500ms
(time/diff ts 1736943000.0) ; => 0.5