Date and time
Lutra has separate types for calendar dates, clock times, and instants in time. That separation helps you write reporting code without mixing up local calendar logic and UTC instants.
Use these types for different jobs:
Datefor a calendar day such as@2025-11-14Timefor a duration or clock-like time value such as@16:07:44Timestampfor an instant in time such as@2025-12-29T16:07:44
Understand the three core types¶
Date stores a calendar day.
Internally, it is backed by days_epoch.
func main() -> {
date = @2025-11-14,
days_epoch = @2025-11-14.days_epoch,
same_value = @2025-11-14.0,
}
Time stores a signed number of microseconds.
You will often use it as a duration.
func main() -> {
short = @00:15:00,
micros = @00:15:00.microseconds,
}
Timestamp stores a UTC instant with microsecond precision.
It does not carry an embedded timezone.
func main() -> {
ts = @2025-12-29T16:07:44,
micros = @2025-12-29T16:07:44.microseconds,
}
Use literals when you need fixed values¶
Lutra has built-in literal syntax for all three types:
func main() -> {
date = @2025-11-14,
time = @16:07:44,
timestamp = @2025-12-29T16:07:44,
}
Convert a local date to a timestamp¶
Use date::to_timestamp(date, tz) when you want the UTC instant for midnight
at the start of a local day.
func start_of_day() -> date::to_timestamp(@2026-06-03, "Europe/Ljubljana")
This is a common reporting boundary. For example, you might turn a user-selected local date into the timestamp range that you pass to a filter.
Extract year, month, and day from a date¶
Use date::to_year_month_day when you need numeric calendar parts.
func main() -> date::to_year_month_day(@2025-12-29)
That returns:
{year = 2025, month = 12, day = 29}
This is useful when you need a stable calendar key or want to derive the first of a month.
Convert a timestamp to a local date¶
Use timestamp::to_date(ts, tz) when you need local calendar grouping.
func main() -> timestamp::to_date(@2025-12-31T23:30:00, "Europe/Ljubljana")
This program returns @2026-01-01, because Europe/Ljubljana timezone is an hour
ahead of UTC, which means that the local date already falls in the next year.
This conversion is the key rule for calendar reporting:
Timestamp is an instant, but reports are often grouped by local day, week, or
month.
Compute durations¶
Use timestamp::sub for elapsed time between two instants.
func main() -> timestamp::sub(
@2025-12-29T16:30:00,
@2025-12-29T16:07:44,
)
Use date::sub when you want the duration between two dates.
func main() -> date::sub(@2025-12-29, @2025-12-01)
Both results are Time values.
Check timezone offsets when you need them¶
Use timezone_offset(time_zone, date) when you need the UTC offset that
applies on a local date.
func main() -> timezone_offset("Europe/Ljubljana", @2025-07-01)
This is most useful when you are debugging timezone-sensitive logic.
Most reporting code can stay at the level of timestamp::to_date and
date::to_timestamp.
Group rows by local calendar month¶
When you group timestamps by month, convert them to a local Date first.
Then derive a stable month key.
type Event: {
ts: Timestamp,
amount: Int64,
}
const events: [Event] = [
{ts = @2025-01-01T00:30:00, amount = 10},
{ts = @2025-01-15T12:00:00, amount = 15},
{ts = @2025-02-01T09:00:00, amount = 7},
]
func totals_by_month(events: [Event], time_zone: Text): [{month: Int32, total: Int64}] -> (
events
| group_map(
e -> (
let d = e.ts | timestamp::to_date(time_zone);
let parts = d | date::to_year_month_day;
d.days_epoch - ((parts.day | to_int32) - 1)
),
func (month, rows) -> {
month = month,
total = rows | map(r -> r.amount) | sum(),
}
)
| sort(x -> x.month)
)
The key idea is not the arithmetic itself. The key idea is the order of operations:
- convert the instant to a local
Date - derive the calendar bucket from that date
- aggregate within that bucket