time - Time and Dates #
The time module provides comprehensive date and time handling for Quest, powered by the jiff library. It offers timezone-aware datetime operations, duration arithmetic, and formatting capabilities with nanosecond precision.
Module Import #
use "std/time" as time
Core Concepts #
Overview #
Quest's time module introduces five new data structures:
- Timestamp - An instant in time (UTC, nanosecond precision)
- Zoned - A timezone-aware datetime
- Date - A calendar date (year, month, day)
- Time - A time of day (hour, minute, second, nanosecond)
- Span - A duration or time span
All types implement Quest's QObj trait and have common methods like .str(), ._rep(), ._doc(), ._id(), and .cls().
Immutability #
All datetime objects are immutable - operations return new objects rather than modifying existing ones. This design:
- Prevents accidental mutations
- Enables safe sharing across contexts
- Simplifies reasoning about code
- Matches functional programming best practices
let dt = time.now()
let tomorrow = dt.add_days(1) # Returns NEW datetime, dt unchanged
puts(dt.str()) # Original datetime still intact
Timezone Handling #
The module uses the IANA Time Zone Database for accurate timezone and DST handling. Timezones are specified using IANA names like:
"America/New_York""Europe/London""Asia/Tokyo""UTC"
Current Time Functions #
time.now() #
Get the current instant as a UTC timestamp.
Returns: Timestamp
Example:
let now = time.now()
puts(now.str()) # "2025-10-01T14:30:45.123456789Z"
time.now_local() #
Get the current datetime in the system's local timezone.
Returns: Zoned
Example:
let local = time.now_local()
puts(local.str()) # "2025-10-01T10:30:45.123-04:00[America/New_York]"
time.today() #
Get today's date in the local timezone.
Returns: Date
Example:
let today = time.today()
puts(today.str()) # "2025-10-01"
time.time_now() #
Get the current time of day in the local timezone.
Returns: Time
Example:
let now = time.time_now()
puts(now.str()) # "14:30:45.123456789"
Construction Functions #
time.parse(string) #
Parse a datetime string in various formats (ISO 8601, RFC 3339, RFC 2822).
Parameters:
string- Datetime string to parse (Str)
Returns: Timestamp, Zoned, Date, or Time depending on input format
Supported formats:
- ISO 8601 / RFC 3339:
"2025-10-01T14:30:00Z"→ Timestamp - With timezone offset:
"2025-10-01T14:30:00-05:00"→ Timestamp - With timezone name:
"2025-10-01T14:30:00-05:00[America/New_York]"→ Zoned (if supported by jiff) - Date only:
"2025-10-01"→ Date - Time only:
"14:30:45"→ Time
Example:
let dt1 = time.parse("2025-10-01T14:30:00Z") # Timestamp
let dt2 = time.parse("2025-10-01T14:30:00-05:00") # Timestamp
let date = time.parse("2025-10-01") # Date
let time_val = time.parse("14:30:45") # Time
time.datetime(year, month, day, hour, minute, second, timezone?) #
Create a timezone-aware datetime from components.
Parameters:
year- Year (Num)month- Month 1-12 (Num)day- Day 1-31 (Num)hour- Hour 0-23 (Num)minute- Minute 0-59 (Num)second- Second 0-59 (Num)timezone- Optional timezone name (Str), defaults to "UTC"
Returns: Zoned
Example:
let dt1 = time.datetime(2025, 10, 1, 14, 30, 0) # UTC
let dt2 = time.datetime(2025, 10, 1, 14, 30, 0, "America/New_York")
time.date(year, month, day) #
Create a calendar date.
Parameters:
year- Year (Num)month- Month 1-12 (Num)day- Day 1-31 (Num)
Returns: Date
Example:
let date = time.date(2025, 10, 1)
puts(date.str()) # "2025-10-01"
time.time(hour, minute, second, nanosecond?) #
Create a time of day.
Parameters:
hour- Hour 0-23 (Num)minute- Minute 0-59 (Num)second- Second 0-59 (Num)nanosecond- Optional nanoseconds (Num), defaults to 0
Returns: Time
Example:
let t1 = time.time(14, 30, 45)
let t2 = time.time(14, 30, 45, 123456789)
time.from_timestamp(seconds) #
Create a Timestamp from Unix epoch seconds.
Parameters:
seconds- Unix timestamp in seconds (Num)
Returns: Timestamp
Example:
let ts = time.from_timestamp(1727794245)
puts(ts.str()) # "2024-10-01T14:50:45Z"
time.from_timestamp_ms(milliseconds) #
Create a Timestamp from Unix epoch milliseconds.
Parameters:
milliseconds- Unix timestamp in milliseconds (Num)
Returns: Timestamp
Example:
let ts = time.from_timestamp_ms(1727794245123)
puts(ts.str()) # "2024-10-01T14:50:45.123Z"
time.from_timestamp_us(microseconds) #
Create a Timestamp from Unix epoch microseconds.
Parameters:
microseconds- Unix timestamp in microseconds (Num)
Returns: Timestamp
Example:
let ts = time.from_timestamp_us(1727794245123456)
puts(ts.str()) # "2024-10-01T14:50:45.123456Z"
Timestamp Methods #
Methods available on Timestamp objects.
timestamp.to_zoned(timezone) #
Convert timestamp to a timezone-aware datetime.
Parameters:
timezone- Timezone name (Str)
Returns: Zoned
Example:
let ts = time.now()
let ny = ts.to_zoned("America/New_York")
let tokyo = ts.to_zoned("Asia/Tokyo")
timestamp.as_seconds() #
Get Unix timestamp in seconds.
Returns: Num
Example:
let ts = time.now()
puts(ts.as_seconds()) # 1727794245
timestamp.as_millis() #
Get Unix timestamp in milliseconds.
Returns: Num
Example:
let ts = time.now()
puts(ts.as_millis()) # 1727794245123
timestamp.as_micros() #
Get Unix timestamp in microseconds.
Returns: Num
Example:
let ts = time.now()
puts(ts.as_micros()) # 1727794245123456
timestamp.as_nanos() #
Get Unix timestamp in nanoseconds.
Returns: Num
Example:
let ts = time.now()
puts(ts.as_nanos()) # 1727794245123456789
timestamp.since(other) #
Calculate the span (duration) between this timestamp and another.
Parameters:
other- Earlier timestamp (Timestamp)
Returns: Span
Example:
let now = time.now()
let then = time.from_timestamp(now.as_seconds() - 3600)
let diff = now.since(then)
puts(diff.as_hours()) # 1.0
puts(diff.humanize()) # "in 1 hour"
Zoned DateTime Methods #
Methods available on Zoned (timezone-aware datetime) objects.
Component Getters #
zoned.year() #
Get the year component.
Returns: Num
zoned.month() #
Get the month component (1-12).
Returns: Num
zoned.day() #
Get the day of month (1-31).
Returns: Num
zoned.hour() #
Get the hour component (0-23).
Returns: Num
zoned.minute() #
Get the minute component (0-59).
Returns: Num
zoned.second() #
Get the second component (0-59).
Returns: Num
zoned.millisecond() #
Get the millisecond component (0-999).
Returns: Num
zoned.microsecond() #
Get the microsecond component (0-999999).
Returns: Num
zoned.nanosecond() #
Get the nanosecond component (0-999999999).
Returns: Num
zoned.day_of_week() #
Get the day of week (1=Monday, 7=Sunday).
Returns: Num
zoned.day_of_year() #
Get the day of year (1-366).
Returns: Num
zoned.timezone() #
Get the timezone name.
Returns: Str
zoned.quarter() #
Get the fiscal quarter (1-4) for this datetime.
Returns: Num (1-4)
Example:
let dt = time.parse("2025-10-01T14:30:45-05:00[America/New_York]")
puts(dt.year()) # 2025
puts(dt.month()) # 10
puts(dt.day()) # 1
puts(dt.hour()) # 14
puts(dt.minute()) # 30
puts(dt.second()) # 45
puts(dt.day_of_week()) # 3 (Wednesday)
puts(dt.quarter()) # 4 (Q4: Oct-Dec)
puts(dt.timezone()) # "America/New_York"
Formatting Methods #
zoned.format(pattern) #
Format datetime using strftime-style format codes.
Parameters:
pattern- Format string (Str)
Returns: Str
Common format codes:
%Y- Year (4 digits, e.g., 2025)%m- Month (01-12)%d- Day (01-31)%H- Hour 24h (00-23)%I- Hour 12h (01-12)%M- Minute (00-59)%S- Second (00-59)%p- AM/PM%A- Full weekday name (Monday, Tuesday, etc.)%a- Abbreviated weekday (Mon, Tue, etc.)%B- Full month name (January, February, etc.)%b- Abbreviated month (Jan, Feb, etc.)%Z- Timezone name%z- Timezone offset (+0000)
Example:
let dt = time.now_local()
puts(dt.format("%Y-%m-%d %H:%M:%S")) # "2025-10-01 14:30:45"
puts(dt.format("%B %d, %Y")) # "October 01, 2025"
puts(dt.format("%I:%M %p")) # "02:30 PM"
puts(dt.format("%A, %B %d, %Y")) # "Wednesday, October 01, 2025"
zoned.to_rfc3339() #
Format as RFC 3339 string.
Returns: Str
Example:
let dt = time.now_local()
puts(dt.to_rfc3339()) # "2025-10-01T14:30:45-04:00"
zoned.str() #
Default string representation (ISO 8601).
Returns: Str
Example:
let dt = time.now_local()
puts(dt.str()) # "2025-10-01T14:30:45-04:00[America/New_York]"
Timezone Conversion #
zoned.to_timezone(timezone) #
Convert to a different timezone.
Parameters:
timezone- Target timezone name (Str)
Returns: Zoned
Example:
let ny = time.parse("2025-10-01T14:30:00-05:00[America/New_York]")
let london = ny.to_timezone("Europe/London")
let tokyo = ny.to_timezone("Asia/Tokyo")
puts(ny.str()) # "2025-10-01T14:30:00-05:00[America/New_York]"
puts(london.str()) # "2025-10-01T20:30:00+01:00[Europe/London]"
puts(tokyo.str()) # "2025-10-02T04:30:00+09:00[Asia/Tokyo]"
zoned.to_utc() #
Convert to UTC timezone.
Returns: Zoned
Example:
let local = time.now_local()
let utc = local.to_utc()
Arithmetic Methods #
zoned.add_years(years) #
Add years to the datetime.
Parameters:
years- Number of years to add (Num)
Returns: Zoned
zoned.add_months(months) #
Add months to the datetime.
Parameters:
months- Number of months to add (Num)
Returns: Zoned
zoned.add_days(days) #
Add days to the datetime.
Parameters:
days- Number of days to add (Num)
Returns: Zoned
zoned.add_hours(hours) #
Add hours to the datetime.
Parameters:
hours- Number of hours to add (Num)
Returns: Zoned
zoned.add_minutes(minutes) #
Add minutes to the datetime.
Parameters:
minutes- Number of minutes to add (Num)
Returns: Zoned
zoned.add_seconds(seconds) #
Add seconds to the datetime.
Parameters:
seconds- Number of seconds to add (Num)
Returns: Zoned
Example:
let dt = time.parse("2025-10-01T14:30:00Z")
let tomorrow = dt.add_days(1)
let next_week = dt.add_days(7)
let next_month = dt.add_months(1)
let next_year = dt.add_years(1)
let later = dt.add_hours(3).add_minutes(30)
zoned.subtract_years(years) #
Subtract years from the datetime.
Parameters:
years- Number of years to subtract (Num)
Returns: Zoned
zoned.subtract_months(months) #
Subtract months from the datetime.
Parameters:
months- Number of months to subtract (Num)
Returns: Zoned
zoned.subtract_days(days) #
Subtract days from the datetime.
Parameters:
days- Number of days to subtract (Num)
Returns: Zoned
zoned.subtract_hours(hours) #
Subtract hours from the datetime.
Parameters:
hours- Number of hours to subtract (Num)
Returns: Zoned
zoned.subtract_minutes(minutes) #
Subtract minutes from the datetime.
Parameters:
minutes- Number of minutes to subtract (Num)
Returns: Zoned
zoned.subtract_seconds(seconds) #
Subtract seconds from the datetime.
Parameters:
seconds- Number of seconds to subtract (Num)
Returns: Zoned
Example:
let dt = time.now()
let yesterday = dt.subtract_days(1)
let last_week = dt.subtract_days(7)
let hour_ago = dt.subtract_hours(1)
zoned.add(span) #
Add a span/duration to the datetime.
Parameters:
span- Span object to add (Span)
Returns: Zoned
Example:
let dt = time.now()
let span = time.span(days: 2, hours: 3, minutes: 30)
let future = dt.add(span)
zoned.subtract(span) #
Subtract a span/duration from the datetime.
Parameters:
span- Span object to subtract (Span)
Returns: Zoned
zoned.since(other) #
Calculate the duration between this datetime and another.
Parameters:
other- Earlier datetime (Zoned or Timestamp)
Returns: Span
Example:
let start = time.parse("2025-10-01T10:00:00Z")
let end = time.parse("2025-10-01T15:30:45Z")
let duration = end.since(start)
puts(duration.hours()) # 5
puts(duration.minutes()) # 330
puts(duration.seconds()) # 19845
Comparison Methods #
zoned.equals(other) #
Check if two datetimes represent the same instant.
Parameters:
other- Datetime to compare (Zoned or Timestamp)
Returns: Bool
zoned.before(other) #
Check if this datetime is before another.
Parameters:
other- Datetime to compare (Zoned or Timestamp)
Returns: Bool
zoned.after(other) #
Check if this datetime is after another.
Parameters:
other- Datetime to compare (Zoned or Timestamp)
Returns: Bool
Example:
let dt1 = time.parse("2025-10-01T10:00:00Z")
let dt2 = time.parse("2025-10-01T15:00:00Z")
puts(dt1.equals(dt2)) # false
puts(dt1.before(dt2)) # true
puts(dt1.after(dt2)) # false
Rounding Methods #
zoned.round_to_hour() #
Round datetime to the nearest hour.
Returns: Zoned
zoned.round_to_minute() #
Round datetime to the nearest minute.
Returns: Zoned
zoned.start_of_day() #
Get the start of the day (00:00:00).
Returns: Zoned
zoned.end_of_day() #
Get the end of the day (23:59:59.999999999).
Returns: Zoned
zoned.start_of_month() #
Get the first day of the month at 00:00:00.
Returns: Zoned
zoned.end_of_month() #
Get the last day of the month at 23:59:59.999999999.
Returns: Zoned
zoned.start_of_quarter() #
Get the first day of the quarter at 00:00:00.
Returns: Zoned
zoned.end_of_quarter() #
Get the last day of the quarter at 23:59:59.999999999.
Returns: Zoned
Example:
let dt = time.parse("2025-10-15T14:30:45Z")
puts(dt.start_of_day().str()) # "2025-10-15T00:00:00Z"
puts(dt.start_of_month().str()) # "2025-10-01T00:00:00Z"
puts(dt.end_of_month().str()) # "2025-10-31T23:59:59.999999999Z"
# Quarter operations (Q4: Oct-Dec)
let q2_date = time.datetime(2025, 5, 15, 10, 0, 0)
puts(q2_date.quarter()) # 2
puts(q2_date.start_of_quarter().str()) # "2025-04-01T00:00:00Z"
puts(q2_date.end_of_quarter().str()) # "2025-06-30T23:59:59.999999999Z"
Date Methods #
Methods available on Date objects.
Component Getters #
date.year() #
Get the year.
Returns: Num
date.month() #
Get the month (1-12).
Returns: Num
date.day() #
Get the day of month (1-31).
Returns: Num
date.day_of_week() #
Get the day of week (1=Monday, 7=Sunday).
Returns: Num
date.day_of_year() #
Get the day of year (1-366).
Returns: Num
date.quarter() #
Get the fiscal quarter (1-4) for this date.
Returns: Num (1-4)
Quarter mapping:
- Q1: January-March (1-3)
- Q2: April-June (4-6)
- Q3: July-September (7-9)
- Q4: October-December (10-12)
Example:
let jan = time.date(2025, 1, 15)
puts(jan.quarter()) # 1
let jul = time.date(2025, 7, 15)
puts(jul.quarter()) # 3
Arithmetic #
date.add_days(days) #
Add days to the date.
Parameters:
days- Number of days (Num)
Returns: Date
date.add_months(months) #
Add months to the date.
Parameters:
months- Number of months (Num)
Returns: Date
date.add_years(years) #
Add years to the date.
Parameters:
years- Number of years (Num)
Returns: Date
Example:
let date = time.date(2025, 10, 1)
let tomorrow = date.add_days(1)
let next_month = date.add_months(1)
let next_year = date.add_years(1)
Duration Calculation #
date.since(other) #
Calculate the span (duration) between this date and another date.
Parameters:
other- Earlier date (Date)
Returns: Span
Example:
let date1 = time.date(2025, 10, 1)
let date2 = time.date(2025, 9, 1)
let span = date1.since(date2)
puts(span.days()) # 30
Comparison #
date.equals(other) #
Check if two dates are equal.
Parameters:
other- Date to compare (Date)
Returns: Bool
date.before(other) #
Check if this date is before another.
Parameters:
other- Date to compare (Date)
Returns: Bool
date.after(other) #
Check if this date is after another.
Parameters:
other- Date to compare (Date)
Returns: Bool
Time Methods #
Methods available on Time objects.
Component Getters #
time.hour() #
Get the hour (0-23).
Returns: Num
time.minute() #
Get the minute (0-59).
Returns: Num
time.second() #
Get the second (0-59).
Returns: Num
time.nanosecond() #
Get the nanosecond (0-999999999).
Returns: Num
Span (Duration) Functions #
time.span(years?, months?, days?, hours?, minutes?, seconds?, millis?, micros?, nanos?) #
Create a span from components (all parameters are optional named parameters).
Parameters:
years- Number of years (Num)months- Number of months (Num)days- Number of days (Num)hours- Number of hours (Num)minutes- Number of minutes (Num)seconds- Number of seconds (Num)millis- Number of milliseconds (Num)micros- Number of microseconds (Num)nanos- Number of nanoseconds (Num)
Returns: Span
Example:
let span1 = time.span(days: 5, hours: 3)
let span2 = time.span(hours: 2, minutes: 30, seconds: 45)
let span3 = time.span(years: 1, months: 6, days: 15)
time.days(n) #
Create a span of n days.
Parameters:
n- Number of days (Num)
Returns: Span
time.hours(n) #
Create a span of n hours.
Parameters:
n- Number of hours (Num)
Returns: Span
time.minutes(n) #
Create a span of n minutes.
Parameters:
n- Number of minutes (Num)
Returns: Span
time.seconds(n) #
Create a span of n seconds.
Parameters:
n- Number of seconds (Num)
Returns: Span
Example:
let five_days = time.days(5)
let three_hours = time.hours(3)
let thirty_minutes = time.minutes(30)
Span Methods #
Methods available on Span objects.
Component Getters #
span.years() #
Get the years component.
Returns: Num
span.months() #
Get the months component.
Returns: Num
span.days() #
Get the days component.
Returns: Num
span.hours() #
Get the hours component.
Returns: Num
span.minutes() #
Get the minutes component.
Returns: Num
span.seconds() #
Get the seconds component.
Returns: Num
Conversion #
span.as_hours() #
Convert entire span to hours (fractional).
Returns: Num
span.as_minutes() #
Convert entire span to minutes (fractional).
Returns: Num
span.as_seconds() #
Convert entire span to seconds (fractional).
Returns: Num
span.as_millis() #
Convert entire span to milliseconds.
Returns: Num
Example:
let span = time.span(hours: 2, minutes: 30)
puts(span.as_hours()) # 2.5
puts(span.as_minutes()) # 150
puts(span.as_seconds()) # 9000
Arithmetic #
span.add(other) #
Add two spans.
Parameters:
other- Span to add (Span)
Returns: Span
span.subtract(other) #
Subtract a span from this one.
Parameters:
other- Span to subtract (Span)
Returns: Span
span.multiply(n) #
Multiply span by a scalar.
Parameters:
n- Multiplier (Num)
Returns: Span
span.divide(n) #
Divide span by a scalar.
Parameters:
n- Divisor (Num)
Returns: Span
Example:
let span1 = time.hours(5)
let span2 = time.minutes(30)
let total = span1.add(span2)
let doubled = span1.multiply(2)
let half = span1.divide(2)
Humanize #
span.humanize() #
Convert span to human-friendly relative time description.
Returns: Str
Behavior:
- Negative spans: Returns "X ago" (e.g., "2 hours ago")
- Positive spans: Returns "in X" (e.g., "in 3 days")
- Automatically chooses appropriate unit (seconds → minutes → hours → days → months → years)
- Proper singular/plural forms ("1 second" vs "30 seconds")
Example:
# Past times (negative spans)
let past = time.hours(-2)
puts(past.humanize()) # "2 hours ago"
let days_ago = time.days(-7)
puts(days_ago.humanize()) # "7 days ago"
# Future times (positive spans)
let future = time.minutes(30)
puts(future.humanize()) # "in 30 minutes"
# Real-world usage
let now = time.now()
let post_time = time.from_timestamp(now.as_seconds() - 7200)
let age = now.since(post_time)
puts("Posted ", age.humanize()) # "Posted in 2 hours"
# Social media style
let comment_age = time.now().since(comment.created_at)
ui.show(comment_age.humanize()) # "5 minutes ago"
Supported ranges:
- < 60 seconds: "X seconds ago" / "in X seconds"
- < 60 minutes: "X minutes ago" / "in X minutes"
- < 24 hours: "X hours ago" / "in X hours"
- < 30 days: "X days ago" / "in X days"
- < 365 days: "X months ago" / "in X months"
-
= 365 days: "X years ago" / "in X years"
Utility Functions #
time.sleep(seconds) #
Sleep for a specified duration (blocks execution).
Parameters:
seconds- Duration in seconds (Num, can be fractional)
Example:
time.sleep(2) # Sleep for 2 seconds
time.sleep(0.5) # Sleep for 500ms
time.sleep(0.001) # Sleep for 1ms
time.is_leap_year(year) #
Check if a year is a leap year.
Parameters:
year- Year to check (Num)
Returns: Bool
Example:
puts(time.is_leap_year(2024)) # true
puts(time.is_leap_year(2025)) # false
time.ticks_ms() #
Get milliseconds elapsed since the program started. Uses a monotonic clock that is not affected by system time changes, making it ideal for measuring elapsed time and performance.
Parameters: None
Returns: Num - milliseconds since program start
Example:
use "std/time" as time
let start = time.ticks_ms()
# Do some work
let sum = 0
let i = 0
while i < 100000
sum = sum + i
i = i + 1
end
let finish = time.ticks_ms()
puts("Operation took", finish - start, "ms")
# Output: Operation took 245ms
Use Cases:
- Performance measurement
- Timing operations
- Benchmarking
- Rate limiting
- Timeout detection
Notes:
- The clock starts when the Quest program begins execution
- Returns a monotonic time that only moves forward
- Not affected by system clock adjustments
- Suitable for measuring short durations with millisecond precision
- For calendar time and dates, use
time.now(),time.today(), etc.
Complete Examples #
Example 1: Age Calculator #
use "std/time" as time
let birthday = time.date(1990, 5, 15)
let today = time.today()
# Calculate age
let age = today.year() - birthday.year()
let this_year_birthday = time.date(today.year(), birthday.month(), birthday.day())
if today.before(this_year_birthday)
age = age - 1
end
puts("You are " .. age.str() .. " years old")
# Days until next birthday
let next_birthday = this_year_birthday
if today.after(next_birthday)
next_birthday = time.date(today.year() + 1, birthday.month(), birthday.day())
end
let days_until = next_birthday.since(today).days()
puts("Days until birthday: " .. days_until.str())
Example 2: Timezone Converter #
use "std/time" as time
# Meeting scheduled in New York
let meeting = time.datetime(2025, 10, 15, 14, 0, 0, "America/New_York")
puts("Meeting Times:")
puts(" New York: " .. meeting.format("%I:%M %p %Z"))
puts(" London: " .. meeting.to_timezone("Europe/London").format("%I:%M %p %Z"))
puts(" Tokyo: " .. meeting.to_timezone("Asia/Tokyo").format("%I:%M %p %Z"))
puts(" Sydney: " .. meeting.to_timezone("Australia/Sydney").format("%I:%M %p %Z"))
Example 3: Performance Timer #
use "std/time" as time
let start = time.now()
# Do some work
let sum = 0
for i in 1..1000000
sum = sum + i
end
let elapsed = time.now().since(start)
puts("Computation took " .. elapsed.as_millis().str() .. " ms")
Example 4: Date Range Iteration #
use "std/time" as time
let start_date = time.date(2025, 10, 1)
let end_date = time.date(2025, 10, 7)
let current = start_date
while current.before(end_date) or current.equals(end_date)
puts(current.format("%A, %B %d")) # "Wednesday, October 01"
current = current.add_days(1)
end
Example 5: Business Days Calculator #
use "std/time" as time
fun is_weekend(date)
let dow = date.day_of_week()
return dow == 6 or dow == 7 # Saturday or Sunday
end
fun add_business_days(start_date, count)
let current = start_date
let added = 0
while added < count
current = current.add_days(1)
if not is_weekend(current)
added = added + 1
end
end
return current
end
let today = time.today()
let deadline = add_business_days(today, 10)
puts("10 business days from now: " .. deadline.str())
Integration with Other Modules #
With Logging #
use "std/time" as time
use "std/log" as log
# The log module automatically uses time.now() for timestamps
log.info("Application started")
# You can also manually log with timestamps
let start_time = time.now()
# ... do work ...
let duration = time.now().since(start_time)
log.info("Task completed in " .. duration.as_seconds().str() .. " seconds")
Implementation Notes #
Jiff Integration #
The time module is implemented as a thin wrapper around the jiff Rust library, which provides:
- Temporal API compatibility (inspired by JavaScript's Temporal proposal)
- Full IANA timezone database support
- Automatic DST handling
- Nanosecond precision
- Comprehensive datetime arithmetic
Performance Considerations #
- Timezone lookups are cached by jiff
- All datetime objects are immutable (functional style)
- Arithmetic operations are optimized by jiff's internal representation
- String parsing is lazy where possible
Error Handling #
Invalid operations (like creating February 30th) will raise exceptions with descriptive error messages:
# This will raise an error
let invalid = time.date(2025, 2, 30) # Error: invalid date
Types Reference #
Quest's time module introduces five new data structures. While these are implemented in Rust, they can be conceptualized as Quest types:
# Conceptual Quest type definitions (actual implementation is in Rust)
type Timestamp
# An instant in time (UTC, nanosecond precision)
# Internal: i128 nanoseconds since Unix epoch
fun to_zoned(timezone: Str) -> Zoned
fun as_seconds() -> num
fun as_millis() -> num
fun as_nanos() -> num
end
type Zoned
# A timezone-aware datetime
# Internal: Timestamp + TimeZone + civil DateTime
# Component getters
fun year() -> num
fun month() -> num
fun day() -> num
fun hour() -> num
fun minute() -> num
fun second() -> num
fun nanosecond() -> num
fun day_of_week() -> num
fun day_of_year() -> num
fun timezone() -> str
# Formatting
fun format(pattern: Str) -> str
fun to_rfc3339() -> str
# Timezone conversion
fun to_timezone(tz: Str) -> Zoned
fun to_utc() -> Zoned
# Arithmetic
fun add_years(n: Num) -> Zoned
fun add_months(n: Num) -> Zoned
fun add_days(n: Num) -> Zoned
fun add_hours(n: Num) -> Zoned
fun add_minutes(n: Num) -> Zoned
fun add_seconds(n: Num) -> Zoned
fun add(span: Span) -> Zoned
fun subtract_days(n: Num) -> Zoned
fun subtract(span: Span) -> Zoned
fun since(other: Zoned) -> Span
# Comparison
fun equals(other: Zoned) -> bool
fun before(other: Zoned) -> bool
fun after(other: Zoned) -> bool
# Rounding
fun round_to_hour() -> Zoned
fun round_to_minute() -> Zoned
fun start_of_day() -> Zoned
fun end_of_day() -> Zoned
fun start_of_month() -> Zoned
fun end_of_month() -> Zoned
end
type Date
# A calendar date (year, month, day)
# Internal: i16 year, i8 month, i8 day
fun year() -> num
fun month() -> num
fun day() -> num
fun day_of_week() -> num
fun day_of_year() -> num
fun add_days(n: Num) -> Date
fun add_months(n: Num) -> Date
fun add_years(n: Num) -> Date
fun equals(other: Date) -> bool
fun before(other: Date) -> bool
fun after(other: Date) -> bool
end
type Time
# A time of day (hour, minute, second, nanosecond)
# Internal: i8 hour, i8 minute, i8 second, i32 nanosecond
fun hour() -> num
fun minute() -> num
fun second() -> num
fun nanosecond() -> num
end
type Span
# A duration mixing calendar and clock units
# Internal: years, months, days, hours, minutes, seconds, nanos
# Component getters
fun years() -> num
fun months() -> num
fun days() -> num
fun hours() -> num
fun minutes() -> num
fun seconds() -> num
# Conversion
fun as_hours() -> num
fun as_minutes() -> num
fun as_seconds() -> num
fun as_millis() -> num
# Arithmetic
fun add(other: Span) -> Span
fun subtract(other: Span) -> Span
fun multiply(n: Num) -> Span
fun divide(n: Num) -> Span
end
# Common trait: All types implement QObj
trait Temporal
fun str() -> str # String representation
fun _rep() -> str # REPL display format
fun _doc() -> str # Documentation
fun _id() -> num # Unique object ID
fun cls() -> str # Type name
end
# Timestamp, Zoned, Date, Time, Span all implement Temporal
Detailed Type Descriptions #
1. Timestamp #
An instant in time represented as UTC with nanosecond precision. Internally stores nanoseconds since Unix epoch (1970-01-01 00:00:00 UTC).
Creation:
time.now()- Current instanttime.parse("2025-10-01T14:30:00Z")- Parse from string
Key Properties:
- Always UTC
- Nanosecond precision
- Comparable and orderable
- Immutable
Example:
let ts = time.now()
puts(ts.str()) # "2025-10-01T14:30:45.123456789Z"
puts(ts.as_seconds()) # 1727794245
puts(ts.as_millis()) # 1727794245123
2. Zoned #
A timezone-aware datetime that combines a timestamp with timezone information and civil datetime (date + time). This is the primary type for working with human-readable dates and times.
Creation:
time.now_local()- Current datetime in system timezonetime.datetime(2025, 10, 1, 14, 30, 0, "America/New_York")- From componentstime.parse("2025-10-01T14:30:00-05:00[America/New_York]")- Parse from string
Key Properties:
- Timezone-aware (stores IANA timezone)
- Has both absolute (timestamp) and civil (date+time) representations
- Automatic DST handling
- Immutable
Example:
let dt = time.now_local()
puts(dt.str()) # "2025-10-01T10:30:45-04:00[America/New_York]"
puts(dt.year()) # 2025
puts(dt.month()) # 10
puts(dt.timezone()) # "America/New_York"
puts(dt.format("%B %d")) # "October 01"
3. Date #
A calendar date without time-of-day or timezone information. Represents year, month, and day.
Creation:
time.today()- Today's date in local timezonetime.date(2025, 10, 1)- From componentstime.parse("2025-10-01")- Parse from string
Key Properties:
- No time component
- No timezone information
- Useful for date arithmetic (birthdays, deadlines, etc.)
- Immutable
Example:
let date = time.date(2025, 10, 1)
puts(date.str()) # "2025-10-01"
puts(date.day_of_week()) # 3 (Wednesday)
let tomorrow = date.add_days(1)
4. Time #
A time-of-day without date or timezone information. Represents hour, minute, second, and nanosecond.
Creation:
time.time_now()- Current time in local timezonetime.time(14, 30, 45)- From componentstime.time(14, 30, 45, 123456789)- With nanoseconds
Key Properties:
- No date component
- No timezone information
- Nanosecond precision
- Immutable
Example:
let t = time.time(14, 30, 45)
puts(t.str()) # "14:30:45"
puts(t.hour()) # 14
puts(t.minute()) # 30
puts(t.second()) # 45
5. Span #
A duration or time span that can mix calendar units (years, months, days) with clock units (hours, minutes, seconds, nanoseconds). Used for datetime arithmetic and measuring elapsed time.
Creation:
time.span(days: 5, hours: 3)- From componentstime.days(7)- Convenience constructordt1.since(dt2)- Difference between two datetimes
Key Properties:
- Can represent both calendar and clock durations
- Aware of variable-length months and DST transitions
- Immutable
- Supports arithmetic operations
Example:
let span = time.span(days: 2, hours: 3, minutes: 30)
puts(span.as_hours()) # 51.5 (2 days + 3.5 hours)
let dur = time.hours(5).add(time.minutes(30))
puts(dur.as_minutes()) # 330
Type Hierarchy #
All five types implement Quest's QObj trait and have these common methods:
.str()- String representation._rep()- REPL display format._doc()- Documentation._id()- Unique object ID.cls()- Type name