Dates and Times

Graphersal has five date and time types. They are values of their own, like numbers and UUIDs: a property holds a date, not a string that looks like one, and a query compares, sorts and computes with it. The model follows PostgreSQL and the SQL standard; the part TinkerPop defines (datetime(), asDate(), dateAdd(), dateDiff(), DT.second to DT.day) behaves exactly as TinkerPop 3.8 specifies it (TinkerPop compatibility).

The five types

Graphersal (type_of, GType)What it isLiteralPostgreSQLSQL standardPythonTinkerPop / java.time
datetime (GType.DATETIME)an instant with the UTC offset it was written withdatetime("2023-08-02T09:30:00+02:00")timestamptz (keeps no offset)TIMESTAMP WITH TIME ZONEaware datetime.datetimedatetime, OffsetDateTime
localdatetime (GType.LOCALDATETIME)a wall-clock date and time, no zonelocaldatetime("2023-08-02T09:30")timestampTIMESTAMPnaive datetime.datetimeLocalDateTime
date (GType.DATE)a calendar daydate("2023-08-02")dateDATEdatetime.dateLocalDate
time (GType.TIME)a time of day, no zonetime("09:30:15.5")timeTIMEnaive datetime.timeLocalTime
duration (GType.DURATION)months, days and microseconds, each with its own signduration("P1Y2M3DT4H")intervalINTERVALdatetime.timedelta, or graphersal.Interval with monthsDuration, Period
type_of(datetime("2023-08-02T09:30:00+02:00"))   // "datetime"
date("2024-02-29").day   // 29
duration("P1Y2M3DT4H").months   // 14
g.inject(datetime("2023-08-02T09:30Z")).is(P.typeOf(GType.DATETIME)).count().next()   // 1
  • Resolution: one microsecond for every type, as in PostgreSQL and Python. TinkerPop's millisecond views (as_number() of a datetime, date_diff()) round down to milliseconds.
  • Range: the years -9999 to 9999 in the proleptic Gregorian calendar (date("-0044-03-15") is year 44 BC counted astronomically, year 0 = 1 BC). A value outside the range, or a result that would leave it, is an error, never a wrapped value.
  • Offsets: a datetime keeps its offset (±18:00, whole seconds) and shows it, unlike PostgreSQL's timestamptz, which keeps only the instant. A time zone is never stored in a value: operations take one (Time Zones).
  • A duration is not normalized: P1M, P30D and PT720H are three different values. A month has no fixed length and a day is not always 24 hours in a time zone, so the three fields stay apart, as in PostgreSQL's interval.
  • No guessing: a string stays a string whatever it looks like. A date comes from a constructor, a conversion step (Conversions), a schema that declares the property (Schema), a typed value in a file (Storage and Exchange) or a Python datetime (From Python).

Script values

In a script each type is a value of its own Rhai type with getters and operators:

TypeGetters
datetimeyear, month, day, hour, minute, second, microsecond (on its own wall clock), offset (seconds), epoch_millis
localdatetimeyear, month, day, hour, minute, second, microsecond
dateyear, month, day
timehour, minute, second, microsecond
durationmonths, days, micros
let d = datetime("2023-08-02T09:30:15.25+02:00"); [d.hour, d.offset, d.microsecond]   // [9, 7200, 250000]
datetime("2023-08-02T12:00Z") + duration("P1M")   // datetime("2023-09-02T12:00:00Z")
date("2023-08-02") - date("2023-01-01")   // duration("P213D")
time("10:00") - time("09:30")   // duration("PT30M")
datetime("2023-08-02T09:30+02:00").as_string()   // "2023-08-02T09:30:00+02:00"

+ and - with a duration work as date_add() does; the difference of two values of one type is a duration (two datetimes or localdatetimes give days of 24 hours and a time part, two dates days, two times the elapsed time). as_string() is the canonical text, to_string() the DSL literal. A value printed or shown as a script value is its literal (date("2023-08-02")); tables, JSON and the playground show the canonical text.

The pages of this part

The steps have generated reference pages: as_date, as_duration, date_add, date_diff, age, date_trunc, date_part, at_zone and as_local; the constructors, now(), today() and DT are on Functions, Tokens and Values.