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 constructor, 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 constructor
There are no bare temporal literals — always use a constructor:
CREATE (e:Event {
title: 'Launch',
at: datetime('2026-05-01T09:00:00Z'),
day: date('2026-05-01'),
clock: localtime('09:00:00'),
runs_for: duration('PT90M')
})
Component maps
CREATE (d:Day {
on: date({year: 2024, month: 1, day: 15})
})
See more in Temporal Functions → Constructors.
From host language
Every binding ships a helper so you don't have to write constructor strings 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 >= datetime() AND e.at < datetime() + duration('P7D')
RETURN e
ORDER BY e.at
Different temporal types are not cross-comparable — convert first or compare in matching units.
Arithmetic
Date + Duration→DateDateTime + Duration→DateTimeDateTime - DateTime→Duration
RETURN date('2024-01-15') + duration('P30D')
-- 2024-02-14
RETURN datetime('2025-01-01T00:00:00Z') - datetime('2024-01-01T00:00:00Z')
-- P366D (a Duration — 2024 is a leap year)
Duration is calendar-aware: duration('P1M') is "one month"
(variable length in days), not exactly 30 days. Use duration('P30D')
for a fixed 30-day window.
Component access
WITH datetime('2024-01-15T10:30:45Z') AS dt
RETURN dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second
For 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 >= datetime() AND e.at < datetime() + duration('P7D')
RETURN e.title, e.at
ORDER BY e.at
Bucketed by month
MATCH (e:Event)
RETURN date.truncate('month', e.on) AS month, count(*) AS events
ORDER BY month
Age from birthday
MATCH (p:Person)
RETURN p.name,
duration.inDays(p.born, date()) / 365 AS approx_age_years
Duration arithmetic
CREATE (m:Meeting {
start: datetime('2026-05-01T09:00:00Z'),
len: duration('PT1H30M')
})
MATCH (m:Meeting)
RETURN m.start, m.start + m.len AS end
Active in last 30 days
MATCH (u:User)
WHERE u.last_seen >= datetime() - duration('P30D')
RETURN count(*) AS active_30d
Group by year of birth
MATCH (p:Person) WHERE p.born IS NOT NULL
RETURN p.born.year AS year, count(*) AS people
ORDER BY year
Window query — past N days
MATCH (e:Event)
WHERE e.at >= datetime() - duration({days: $days})
RETURN e
ORDER BY e.at DESC
Bind $days as an integer from the host — the duration({days: …})
constructor accepts a variable, unlike the ISO string form
duration('P7D') which must be a literal.
Age-bracket bucketing
MATCH (p:Person)
WITH p, duration.inDays(p.born, date()) / 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 DESC
Uses CASE to bucket a
numeric age.
Retention cohort
MATCH (u:User)-[:SIGNED_UP_ON]->(d:Day)
WITH date.truncate('month', d.on) AS cohort, u
OPTIONAL MATCH (u)-[:LOGGED_IN]->(l:Login)
WHERE l.at >= datetime() - duration('P30D')
RETURN cohort,
count(DISTINCT u) AS total,
count(DISTINCT CASE WHEN l IS NOT NULL THEN u END) AS active_30d
ORDER BY cohort
Edge cases
Date arithmetic on month-ends
Duration calendar-aware arithmetic handles month-end clamping:
RETURN date('2024-01-31') + duration('P1M') -- 2024-02-29
RETURN date('2024-03-31') + duration('P1M') -- 2024-04-30
Timezone-aware comparison
DateTime values in different offsets compare by the same UTC
instant — ordering is timezone-safe.
RETURN datetime('2024-01-01T12:00:00Z') =
datetime('2024-01-01T13:00:00+01:00')
-- true
Cross-type comparison
Date and DateTime aren't directly comparable. Convert via
component reconstruction:
MATCH (e:Event)
WHERE date({year: e.at.year, month: e.at.month, day: e.at.day}) = date('2024-01-15')
RETURN e
duration('P1M') vs duration('P30D')
Calendar-aware vs fixed:
RETURN date('2024-02-15') + duration('P1M') -- 2024-03-15
RETURN date('2024-02-15') + duration('P30D') -- 2024-03-16
Storing 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
date.truncatesupports only"year"and"month".datetime.truncatesupports only"day","hour","month".- 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 — constructors, truncation, arithmetic.
- WHERE — temporal predicates and ranges.
- Ordering — chronological sort.
- Aggregation — date-bucketed group-bys.
- Scalars → Integer/Float — underlying component types.