gql() has openCypher's temporal functions (date(), datetime(),
duration(), x.year, date.truncate(), duration.between(), ...) with Neo4j's meaning. They
build and read the same six values as the Gremlin steps of this part (Cypher's time is the
offsettime, its localtime the time): a date a GQL query returns
is a date value, and a date a Gremlin step stored reads as a date in GQL. The full list of
forms is on The Supported Subset, the differences from Neo4j
on Semantics.
The Gremlin steps keep their PostgreSQL-style rules; where a GQL function means something else,
the GQL function changes, never the step. This page sets each GQL form beside the Gremlin step or
Rhai literal that does the same, and names the differences.
g.gql("RETURN date('2024-03-15') AS d").select("d").next() == date("2024-03-15") // true
g.gql("RETURN $d + duration('P1M') AS d", #{d: date("2024-01-31")}).to_list() // #{"d": date("2024-02-29")}
g.add_v("event").property("at", date("2024-03-15")).iterate();
g.gql("MATCH (e:event) RETURN e.at.year AS y, e.at + duration('P1M') AS next").to_list() // #{"next": date("2024-04-15"), "y": 2024}
the literals date("2015-07-21"), time("21:40"), offsettime("21:40+01:00"), localdatetime(".."), datetime(".."), duration("P1D")
GQL also reads Neo4j's forms: week, ordinal and quarter dates (2015-W30-2, 2015-202, 2015-Q3-21), basic forms (20150721T2140, 2140-02), fractions on any duration unit (P1.5Y); GQL's time('21:40') without an offset is UTC, Rhai's offsettime() needs the offset; both keep a zone in brackets ([Europe/London]) and nine fraction digits
date({year: 2015, week: 30, dayOfWeek: 2}) and the other maps
none: build the text, or date_add() from a start
date(x), localdatetime(x), datetime(x) of another temporal value
the same projections (the date of a datetime at its own offset, a localdatetime read at UTC); GQL's localdatetime(date) and datetime(date) are errors as in Neo4j (localdatetime({date: d}) is midnight), as_date(GType.LOCALDATETIME) of a date is midnight
both are one instant per run; a query run from a script reads the script's instant, so datetime() in g.gql(..) equals the script's now() (a host's pinned clock too); run alone, a query takes it when it starts
g.gql("RETURN date(datetime('2015-07-21T23:40:32+02:00')) AS d").to_list() // #{"d": date("2015-07-21")}
g.inject(datetime("2015-07-21T23:40:32+02:00")).as_date(GType.DATE).next() // date("2015-07-21")
the same order (months clamped, days, time); but in GQL a date takes the whole days of a time part (date + PT33H adds a day) and a localtime ignores months and days, where date_add is an error
dur + dur, dur * n, dur / n
date_add(dur) on a duration (field by field)
GQL scales with the fraction cascade (P1M * 0.5 is P15DT5H14M33S)
duration.between(a, b)
age() (the operands swapped: age counts from the incoming value back to other)
GQL counts whole months, then days, then time, each part signed (Neo4j); age borrows as PostgreSQL does; GQL takes two different types, age one type
GQL gives a duration (P30Y8M), date_diff an integer (368)
duration.inSeconds(a, b)
date_diff(other) (the milliseconds, an integer)
GQL gives a duration of seconds only (PT24H)
g.gql("RETURN date('1997-09-11') + duration('P29DT33H') AS d").to_list() // #{"d": date("1997-10-11")}
g.inject(date("1997-09-11")).date_add(duration("P29DT33H")).next() // error: adding a duration with a time part is not defined for a date
g.gql("RETURN duration.between(date('1984-10-11'), date('2015-06-24')) AS d").to_list() // #{"d": duration("P30Y8M13D")}
g.inject(date("2015-06-24")).age(date("1984-10-11")).next() // duration("P30Y8M13D")
g.inject(date("2015-06-24")).date_diff(date("1984-10-11"), DT.month).next() // 368
GQL writes Neo4j's text ('12:00'), as_string() the canonical ISO text ("12:00:00"), as GQL's CAST(x AS STRING) does
g.gql("RETURN toString(localtime('12:00')) AS a, CAST(localtime('12:00') AS STRING) AS b").to_list() // #{"a": "12:00", "b": "12:00:00"}
g.inject(time("12:00")).as_string().next() // "12:00:00"
A zone name in a value (datetime('2017-08-08T12:31[Europe/Stockholm]'),
datetime({.., timezone: 'Europe/Stockholm'})) is kept, printed (toString writes
..+02:00[Europe/Stockholm]), read as x.timezone, and later arithmetic, truncation and
duration.between follow the zone's daylight saving rules, as Neo4j does; the Gremlin steps
with a zone argument keep the name in the same way. Cypher's time() (ISO zoned_time(),
the literal TIME '12:00+01:00', the clock CURRENT_TIME) is the offsettime; a named zone
gives a time the zone's offset at the query's instant
(Semantics).
g.gql("RETURN datetime('2024-03-30T12:00[Europe/Berlin]') + duration('P1D') AS d").to_list() // #{"d": datetime("2024-03-31T12:00:00+02:00[Europe/Berlin]")}
g.gql("RETURN time({hour: 12, timezone: '+01:00'}) AS t").to_list() // #{"t": offsettime("12:00:00+01:00")}