Temporal Data Types
LoraDB has six first-class temporal types. Each can be stored as a
node or relationship
property, compared,
ordered, and used in arithmetic with Duration.
| Type | Components | Timezone |
|---|---|---|
Date | year, month, day | — |
Time | hour, minute, second, nanosecond | UTC offset |
LocalTime | hour, minute, second, nanosecond | — |
DateTime | Date + Time fields | UTC offset |
LocalDateTime | Date + LocalTime fields | — |
Duration | months, days, seconds, nanoseconds | — |
See Temporal Functions for the full construction, truncation, and arithmetic reference. This page focuses on the types.
Which one do I use?
| Situation | Type |
|---|---|
| A calendar day (invoice date, birthday) | Date |
| An instant with offset (event timestamp, audit log) | DateTime |
| A wall-clock time with offset | Time |
| A naive local wall-clock time | LocalTime |
| A naive local moment (meeting at 10:00 "Amsterdam time") | LocalDateTime |
| A span (90 minutes, 30 days, 2 weeks) | Duration |
When in doubt, use DateTime for instants and Date for calendar days
— they cover most real-world needs.
Writing temporals
Literals via casts
There are no bare temporal literals — cast a string or component map to the target temporal type:
CREATE (e:Event {
title: 'Launch',
at: '2026-05-01T09:00:00Z'::DATETIME,
day: '2026-05-01'::DATE,
clock: '09:00:00'::LOCAL_TIME,
runs_for: 'PT90M'::DURATION
})Component maps
CREATE (d:Day {
on: {year: 2024, month: 1, day: 15}::DATE
})See more in Temporal Functions → Construction and current time.
From host language
Every binding ships a helper so you can pass typed values as parameters without writing query casts manually:
- Node/WASM — typed helpers
- Python — parameters
Comparison and ordering
Values of the same temporal type are totally ordered.
MATCH (e:Event)
WHERE e.at >= temporal.now() AND e.at < temporal.now() + 'P7D'::DURATION
RETURN e
ORDER BY e.atDifferent temporal types are not cross-comparable — convert first or compare in matching units.
Arithmetic
Date + Duration→DateDateTime + Duration→DateTimeDateTime - DateTime→Duration
RETURN '2024-01-15'::DATE + 'P30D'::DURATION
;// 2024-02-14
RETURN '2025-01-01T00:00:00Z'::DATETIME - '2024-01-01T00:00:00Z'::DATETIME
// P366D (a Duration — 2024 is a leap year)Duration is calendar-aware: 'P1M'::DURATION is "one month"
(variable length in days), not exactly 30 days. Use 'P30D'::DURATION
for a fixed 30-day window.
Component access
WITH '2024-01-15T10:30:45Z'::DATETIME AS dt
RETURN dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.secondFor the full list see Temporal Functions → component access.
Serialisation
Across host-language bindings, temporals are tagged:
| Type | Shape |
|---|---|
Date | {kind: "date", iso: "YYYY-MM-DD"} |
Time | {kind: "time", iso: "HH:MM:SS.nnnnn+ZZ:ZZ"} |
LocalTime | {kind: "localtime", iso: "HH:MM:SS.nnnnn"} |
DateTime | {kind: "datetime", iso: "YYYY-MM-DDTHH:MM:SS.nnnnn+ZZ:ZZ"} |
LocalDateTime | {kind: "localdatetime", iso: "YYYY-MM-DDTHH:MM:SS.nnnnn"} |
Duration | {kind: "duration", iso: "P…"} |
Use the host-language helpers (date(), datetime(), duration() in
each binding — see Node,
Python) to build these values
without touching the tagged shape manually.
Examples
Events in the next week
MATCH (e:Event)
WHERE e.at >= temporal.now() AND e.at < temporal.now() + 'P7D'::DURATION
RETURN e.title, e.at
ORDER BY e.atBucketed by month
MATCH (e:Event)
RETURN temporal.truncate('month', e.on) AS month, count(*) AS events
ORDER BY monthAge from birthday
MATCH (p:Person)
RETURN p.name,
temporal.in_days(p.born, temporal.today()) / 365 AS approx_age_yearsDuration arithmetic
CREATE (m:Meeting {
start: '2026-05-01T09:00:00Z'::DATETIME,
len: 'PT1H30M'::DURATION
});
MATCH (m:Meeting)
RETURN m.start, m.start + m.len AS endActive in last 30 days
MATCH (u:User)
WHERE u.last_seen >= temporal.now() - 'P30D'::DURATION
RETURN count(*) AS active_30dGroup by year of birth
MATCH (p:Person) WHERE p.born IS NOT NULL
RETURN p.born.year AS year, count(*) AS people
ORDER BY yearWindow query — past N days
MATCH (e:Event)
WHERE e.at >= temporal.now() - {days: $days}::DURATION
RETURN e
ORDER BY e.at DESCBind $days as an integer from the host — the {days: …}::DURATION
map cast accepts a variable, unlike the ISO string form
'P7D'::DURATION which must be a literal.
Age-bracket bucketing
MATCH (p:Person)
WITH p, temporal.in_days(p.born, temporal.today()) / 365 AS age_years
RETURN CASE
WHEN age_years < 18 THEN 'minor'
WHEN age_years < 65 THEN 'adult'
ELSE 'senior'
END AS bracket,
count(*) AS people
ORDER BY people DESCUses CASE to bucket a
numeric age.
Retention cohort
MATCH (u:User)-[:SIGNED_UP_ON]->(d:Day)
WITH temporal.truncate('month', d.on) AS cohort, u
OPTIONAL MATCH (u)-[:LOGGED_IN]->(l:Login)
WHERE l.at >= temporal.now() - 'P30D'::DURATION
RETURN cohort,
count(DISTINCT u) AS total,
count(DISTINCT CASE WHEN l IS NOT NULL THEN u END) AS active_30d
ORDER BY cohortEdge cases
Date arithmetic on month-ends
Duration calendar-aware arithmetic handles month-end clamping:
RETURN '2024-01-31'::DATE + 'P1M'::DURATION; // 2024-02-29
RETURN '2024-03-31'::DATE + 'P1M'::DURATION // 2024-04-30Timezone-aware comparison
DateTime values in different offsets compare by the same UTC
instant — ordering is timezone-safe.
RETURN '2024-01-01T12:00:00Z'::DATETIME =
'2024-01-01T13:00:00+01:00'::DATETIME
// trueCross-type comparison
Date and DateTime aren't directly comparable. Convert via
component reconstruction:
MATCH (e:Event)
WHERE {year: e.at.year, month: e.at.month, day: e.at.day}::DATE = '2024-01-15'::DATE
RETURN e'P1M'::DURATION vs 'P30D'::DURATION
Calendar-aware vs fixed:
RETURN '2024-02-15'::DATE + 'P1M'::DURATION; // 2024-03-15
RETURN '2024-02-15'::DATE + 'P30D'::DURATION // 2024-03-16Storing as string
If you only want ISO string storage, use String — but then sorting
by date requires parsing on every comparison. Prefer the typed form.
Limitations
temporal.truncatesupports only"year"and"month"forDATEvalues.temporal.truncatesupports only"year","month","day", and"hour"forDATETIMEvalues.- Parsing is strict ISO 8601 — non-ISO shapes (
MM/DD/YYYY, RFC-2822) are rejected. - Arithmetic between different temporal types (e.g.
Date - Time) is not supported.
See also
- Temporal Functions — construction, current time, truncation, arithmetic.
- WHERE — temporal predicates and ranges.
- Ordering — chronological sort.
- Aggregation — date-bucketed group-bys.
- Scalars → Integer/Float — underlying component types.