# Time _Path: en/lua/core/time_ ## Table of Contents - Time & Duration ## Content # Time & Duration Work with time values, durations, timezones, and scheduling. Create timers, sleep for specified periods, parse and format timestamps. In workflows, `time.now()` returns a recorded time reference for deterministic replay. ## Loading ```lua local time = require("time") ``` ### now Returns the current time. In workflows, returns the recorded time from the workflow's time reference for deterministic replay. ```lua local t = time.now() print(t:format_rfc3339()) -- "2024-12-29T15:04:05Z" -- Measure elapsed time local start = time.now() do_work() local elapsed = time.now():sub(start) print("Took " .. elapsed:milliseconds() .. "ms") ``` **Returns:** `Time` ### From Components ```lua -- Create specific date/time in UTC local t = time.date(2024, time.DECEMBER, 25, 10, 30, 0, 0, time.utc) print(t:format_rfc3339()) -- "2024-12-25T10:30:00Z" -- Create in specific timezone local ny, _ = time.load_location("America/New_York") local meeting = time.date(2024, time.JANUARY, 15, 14, 0, 0, 0, ny) -- Defaults to local timezone if not specified local t = time.date(2024, 1, 15, 12, 0, 0, 0) ``` | Parameter | Type | Description | |-----------|------|-------------| | `year` | number | Year | | `month` | number | Month (1-12 or `time.JANUARY` etc) | | `day` | number | Day of month | | `hour` | number | Hour (0-23) | | `minute` | number | Minute (0-59) | | `second` | number | Second (0-59) | | `nanosecond` | number | Nanosecond (0-999999999) | | `location` | Location | Timezone (optional, defaults to local) | **Returns:** `Time` ### From Unix Timestamp ```lua -- From seconds since epoch local t = time.unix(1703862245, 0) print(t:utc():format_rfc3339()) -- "2023-12-29T15:04:05Z" -- With nanoseconds local t = time.unix(1703862245, 500000000) -- +500ms -- Convert JavaScript timestamp (milliseconds) local js_timestamp = 1703862245000 local t = time.unix(js_timestamp // 1000, (js_timestamp % 1000) * 1000000) ``` | Parameter | Type | Description | |-----------|------|-------------| | `sec` | number | Unix seconds | | `nsec` | number | Nanoseconds offset | **Returns:** `Time` ### From String Parse time strings using Go's reference time format: `Mon Jan 2 15:04:05 MST 2006`. ```lua -- Parse RFC3339 local t, err = time.parse(time.RFC3339, "2024-12-29T15:04:05Z") if err then return nil, err end -- Parse custom format local t, err = time.parse("2006-01-02", "2024-12-29") local t, err = time.parse("15:04:05", "14:30:00") local t, err = time.parse("2006-01-02 15:04:05 MST", "2024-12-29 14:30:00 EST") -- Parse in specific timezone local ny, _ = time.load_location("America/New_York") local t, err = time.parse("2006-01-02 15:04", "2024-12-29 14:30", ny) ``` | Parameter | Type | Description | |-----------|------|-------------| | `layout` | string | Go time format layout | | `value` | string | String to parse | | `location` | Location | Default timezone (optional) | **Returns:** `Time, error` ### Arithmetic ```lua local t = time.now() -- Add duration (accepts number, string, or Duration) local tomorrow = t:add("24h") local later = t:add(5 * time.MINUTE) local d, _ = time.parse_duration("1h30m") local future = t:add(d) -- Subtract time to get duration local diff = tomorrow:sub(t) -- returns Duration print(diff:hours()) -- 24 -- Add calendar units (handles month boundaries correctly) local next_month = t:add_date(0, 1, 0) -- add 1 month local next_year = t:add_date(1, 0, 0) -- add 1 year local last_week = t:add_date(0, 0, -7) -- subtract 7 days ``` | Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | `add(duration)` | number/string/Duration | Time | Add duration | | `sub(time)` | Time | Duration | Difference between times | | `add_date(years, months, days)` | numbers | Time | Add calendar units | ### Comparison ```lua local t1 = time.date(2024, 1, 1, 0, 0, 0, 0, time.utc) local t2 = time.date(2024, 1, 2, 0, 0, 0, 0, time.utc) t1:before(t2) -- true t2:after(t1) -- true t1:equal(t1) -- true ``` | Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | `before(time)` | Time | boolean | Is this time before other? | | `after(time)` | Time | boolean | Is this time after other? | | `equal(time)` | Time | boolean | Are times equal? | ### Formatting ```lua local t = time.now() t:format_rfc3339() -- "2024-12-29T15:04:05Z" t:format(time.DATE_ONLY) -- "2024-12-29" t:format(time.TIME_ONLY) -- "15:04:05" t:format("Mon Jan 2, 2006") -- "Sun Dec 29, 2024" ``` | Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | `format(layout)` | string | string | Format using Go layout | | `format_rfc3339()` | - | string | Format as RFC3339 | ### Unix Timestamps ```lua local t = time.now() t:unix() -- seconds since epoch t:unix_nano() -- nanoseconds since epoch ``` ### Components ```lua local t = time.now() -- Get date parts local year, month, day = t:date() -- Get time parts local hour, min, sec = t:clock() -- Individual accessors t:year() -- e.g., 2024 t:month() -- 1-12 t:day() -- 1-31 t:hour() -- 0-23 t:minute() -- 0-59 t:second() -- 0-59 t:nanosecond() -- 0-999999999 t:weekday() -- 0=Sunday .. 6=Saturday t:year_day() -- 1-366 t:is_zero() -- true if zero value ``` ### Timezone Conversion ```lua local t = time.now() t:utc() -- convert to UTC t:in_local() -- convert to local timezone t:in_location(ny) -- convert to specific timezone t:location() -- get current Location t:location():string() -- get timezone name ``` | Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | `utc()` | - | Time | Convert to UTC | | `in_local()` | - | Time | Convert to local timezone | | `in_location(loc)` | Location | Time | Convert to timezone | | `location()` | - | Location | Get current timezone | ### Rounding Round or truncate to duration boundaries. **Requires Duration userdata** (not number or string). ```lua local t = time.now() local hour_duration, _ = time.parse_duration("1h") local minute_duration, _ = time.parse_duration("15m") t:round(hour_duration) -- round to nearest hour t:truncate(minute_duration) -- truncate to 15-minute boundary ``` | Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | `round(duration)` | Duration | Time | Round to nearest multiple | | `truncate(duration)` | Duration | Time | Truncate to multiple | ### Creating Durations ```lua -- Parse from string local d, err = time.parse_duration("1h30m45s") local d, err = time.parse_duration("500ms") local d, err = time.parse_duration("2h30m45s500ms") -- From number (nanoseconds) local d, err = time.parse_duration(time.SECOND) local d, err = time.parse_duration(5 * time.MINUTE) -- Valid units: ns, us, ms, s, m, h ``` | Parameter | Type | Description | |-----------|------|-------------| | `value` | number/string/Duration | Duration to parse | **Returns:** `Duration, error` ### Duration Methods ```lua local d, _ = time.parse_duration("1h30m45s500ms") d:hours() -- 1.5125... d:minutes() -- 90.75... d:seconds() -- 5445.5 d:milliseconds() -- 5445500 d:microseconds() -- 5445500000 d:nanoseconds() -- 5445500000000 ``` ### Load by Name Load timezone by IANA name (e.g., "America/New_York", "Europe/London", "Asia/Tokyo"). ```lua local ny, err = time.load_location("America/New_York") if err then return nil, err end local tokyo, _ = time.load_location("Asia/Tokyo") local london, _ = time.load_location("Europe/London") -- Convert between timezones local t = time.now():utc() print("UTC:", t:format(time.TIME_ONLY)) print("New York:", t:in_location(ny):format(time.TIME_ONLY)) print("Tokyo:", t:in_location(tokyo):format(time.TIME_ONLY)) ``` | Parameter | Type | Description | |-----------|------|-------------| | `name` | string | IANA timezone name | **Returns:** `Location, error` ### Fixed Offset Create timezone with fixed UTC offset. ```lua -- UTC+5:30 (India Standard Time) local ist = time.fixed_zone("IST", 5*3600 + 30*60) -- UTC-8 (Pacific Standard Time) local pst = time.fixed_zone("PST", -8*3600) local t = time.date(2024, 1, 15, 12, 0, 0, 0, ist) ``` | Parameter | Type | Description | |-----------|------|-------------| | `name` | string | Zone name | | `offset` | number | UTC offset in seconds | **Returns:** `Location` ### Built-in Locations ```lua time.utc -- UTC timezone time.localtz -- Local system timezone ``` ### sleep Pause execution for specified duration. In workflows, recorded and replayed correctly. ```lua time.sleep("5s") time.sleep(500 * time.MILLISECOND) -- Backoff pattern for attempt = 1, 3 do local ok = try_operation() if ok then break end time.sleep(tostring(attempt) .. "s") end ``` | Parameter | Type | Description | |-----------|------|-------------| | `duration` | number/string/Duration | Sleep time | ### after Returns a channel that receives once after the duration. Works with `channel.select`. ```lua -- Simple timeout local timeout = time.after("5s") timeout:receive() -- blocks for 5 seconds -- Timeout with select local response_ch = make_request() local timeout_ch = time.after("30s") local result = channel.select{ response_ch:case_receive(), timeout_ch:case_receive() } if result.channel == timeout_ch then return nil, errors.new("TIMEOUT", "Request timed out") end ``` | Parameter | Type | Description | |-----------|------|-------------| | `duration` | number/string/Duration | Time to wait | **Returns:** `Channel` ### timer One-shot timer that fires after duration. Can be stopped or reset. ```lua local timer = time.timer("5s") -- Wait for timer timer:response():receive() send_reminder() -- Reset on activity local idle_timer = time.timer("5m") while true do local r = channel.select{ user_activity:case_receive(), idle_timer:response():case_receive() } if r.channel == idle_timer:response() then logout_user() break end idle_timer:reset("5m") end -- Stop timer timer:stop() ``` | Parameter | Type | Description | |-----------|------|-------------| | `duration` | number/string/Duration | Time until fire | **Returns:** `Timer, error` | Timer Method | Parameters | Returns | Description | |--------------|------------|---------|-------------| | `response()` | - | Channel | Get timer channel | | `channel()` | - | Channel | Alias for response() | | `stop()` | - | boolean | Cancel timer | | `reset(duration)` | number/string/Duration | boolean | Reset with new duration | ### ticker Repeating timer that fires at regular intervals. ```lua -- Periodic task local ticker = time.ticker("30s") local ch = ticker:response() while true do local tick_time = ch:receive() check_health() end -- Rate limiting local ticker = time.ticker("100ms") for _, item in ipairs(items) do ticker:response():receive() process(item) end ticker:stop() ``` | Parameter | Type | Description | |-----------|------|-------------| | `duration` | number/string/Duration | Interval between ticks | **Returns:** `Ticker, error` | Ticker Method | Parameters | Returns | Description | |---------------|------------|---------|-------------| | `response()` | - | Channel | Get ticker channel | | `channel()` | - | Channel | Alias for response() | | `stop()` | - | boolean | Stop ticker | ### Duration Units Duration constants are in nanoseconds. Use with arithmetic. ```lua time.NANOSECOND -- 1 time.MICROSECOND -- 1,000 time.MILLISECOND -- 1,000,000 time.SECOND -- 1,000,000,000 time.MINUTE -- 60 * SECOND time.HOUR -- 60 * MINUTE -- Example usage time.sleep(5 * time.SECOND) local timeout = time.after(30 * time.SECOND) ``` ### Format Layouts ```lua time.RFC3339 -- "2006-01-02T15:04:05Z07:00" time.RFC3339NANO -- "2006-01-02T15:04:05.999999999Z07:00" time.RFC822 -- "02 Jan 06 15:04 MST" time.RFC822Z -- "02 Jan 06 15:04 -0700" time.RFC850 -- "Monday, 02-Jan-06 15:04:05 MST" time.RFC1123 -- "Mon, 02 Jan 2006 15:04:05 MST" time.RFC1123Z -- "Mon, 02 Jan 2006 15:04:05 -0700" time.DATE_TIME -- "2006-01-02 15:04:05" time.DATE_ONLY -- "2006-01-02" time.TIME_ONLY -- "15:04:05" time.KITCHEN -- "3:04PM" time.STAMP -- "Jan _2 15:04:05" time.STAMP_MILLI -- "Jan _2 15:04:05.000" time.STAMP_MICRO -- "Jan _2 15:04:05.000000" time.STAMP_NANO -- "Jan _2 15:04:05.000000000" ``` ### Months ```lua time.JANUARY -- 1 time.FEBRUARY -- 2 time.MARCH -- 3 time.APRIL -- 4 time.MAY -- 5 time.JUNE -- 6 time.JULY -- 7 time.AUGUST -- 8 time.SEPTEMBER -- 9 time.OCTOBER -- 10 time.NOVEMBER -- 11 time.DECEMBER -- 12 ``` ### Weekdays ```lua time.SUNDAY -- 0 time.MONDAY -- 1 time.TUESDAY -- 2 time.WEDNESDAY -- 3 time.THURSDAY -- 4 time.FRIDAY -- 5 time.SATURDAY -- 6 ``` ## Errors | Condition | Kind | Retryable | |-----------|------|-----------| | Invalid duration format | `errors.INVALID` | no | | Parse failed | `errors.INVALID` | no | | Empty location name | `errors.INVALID` | no | | Location not found | `errors.NOT_FOUND` | no | | Duration <= 0 (timer/ticker) | `errors.INVALID` | no | ```lua local t, err = time.parse(time.RFC3339, "invalid") if err then if errors.is(err, errors.INVALID) then print("Invalid format:", err:message()) end return nil, err end local loc, err = time.load_location("Unknown/Zone") if err then if errors.is(err, errors.NOT_FOUND) then print("Location not found:", err:message()) end return nil, err end ``` See [Error Handling](lua/core/errors.md) for working with errors. ## Navigation Previous: Errors (lua/core/errors) Next: Channels (lua/core/channel)