The sandbox has no os.time or os.date; this library replaces them. Every function is synchronous and needs no scope. Zones are IANA names ("Europe/Berlin", "America/New_York"), which is what a settings.timezone row stores.

perch.time.now
perch.time.now(zone: string?): civil_time_t
The current wall-clock time in zone, or in the machine’s zone when omitted. An unknown zone returns { error = "unknown zone" }.
local t = perch.time.now("Asia/Tokyo")
clock:push({ time = string.format("%02d:%02d", t.hour, t.min) })
perch.time.at
perch.time.at(epoch: number, zone: string?): instant_t
The same question about any instant: epoch is seconds since the Unix epoch. Returns the civil_time_t fields plus epoch echoed back.
local d = perch.time.at(news.items[1].date)
w:push({ when = string.format("%d/%d", d.mon, d.day) })
perch.time.parse
perch.time.parse(text: string): number?
Turns an ISO-8601 timestamp into epoch seconds, or nil when the text is not a date. Accepted: a plain date ("2026-08-30"), a date and time ("2026-08-30T12:34", optional ":56" seconds), a trailing "Z", and an offset as "+hh:mm", "+hhmm" or "+hh". Text with no offset is read as UTC.
local epoch = perch.time.parse(payload.published_at)
if epoch then
local d = perch.time.at(epoch)
w:push({ line = string.format("%02d:%02d", d.hour, d.min) })
end
perch.time.zone
perch.time.zone(): string
The machine’s own IANA zone name ("America/Chicago"), or "UTC" when it cannot be determined.
perch.time.format
perch.time.format(spec: string, zone: string?): string
Formats the current time with a C++ chrono spec without braces: "%H:%M", "%A %d %B", "%I:%M %p". Returns "?" for an invalid spec or zone. For any other instant, use perch.time.at and format the fields yourself.
date:push({ line = perch.time.format("%A, %d %B") })
Types
export type civil_time_t = { year: number, mon: number, day: number, hour: number, min: number, sec: number, dow: number, off: number, error: string? }
export type instant_t = civil_time_t & { epoch: number }
| Field | Meaning |
|---|---|
year, mon, day | the date; mon is 1..12 |
hour, min, sec | 0..23, 0..59, 0..59 |
dow | day of week, 0 is Sunday |
off | the zone’s current UTC offset in minutes, DST applied |
error | "unknown zone" when the zone was not recognised; the other fields are then absent |
epoch | on an instant_t: the seconds you passed to perch.time.at |