Dates
FlyQL distinguishes calendar days (Type.Date) from instants-in-time (Type.DateTime) in column schemas, and uses that distinction to drive matcher coercion for ISO-8601 string literals. SQL generators, by contrast, pass string literals through verbatim — the database parses them.
Calendar day vs. instant
Section titled “Calendar day vs. instant”Type.Date — calendar day only. Comparisons truncate to YYYYMMDD. Absorbs raw DB types date / date32.
Type.DateTime — point on the timeline at millisecond resolution. Comparisons happen in UTC milliseconds. Absorbs datetime, datetime64, timestamp, timestamptz, year.
Declaring the semantic type matters for the matcher: a Date column compared against '2026-04-06T15:30:00Z' truncates the record value to its Y/M/D component before comparison, whereas a DateTime column compares down to the millisecond.
If a column declared Type.Date receives a datetime-shaped value (native datetime / time.Time / Date instance, or an ISO string carrying a time component), FlyQL emits a one-time UserWarning (Python) / log line (Go) / console.warn (JS) per column suggesting migration to Type.DateTime.
Date as string literal
Section titled “Date as string literal”Compare a date/datetime column to an ISO-8601 string literal. Generators pass the literal through; the database parses it at execute time.
due_date < '2023-12-31'
created_at >= '2023-12-31T00:00:00Z'
event_time >= '2023-01-01' and event_time <= '2023-12-31'
In the matcher, schema-declared Date / DateTime columns drive automatic parsing of ISO-8601 literals AND record values. No explicit conversion function is required for the common case.
Accepted iso8601 shapes
Section titled “Accepted iso8601 shapes”The matcher’s iso8601 family is lenient:
YYYY-MM-DD— pure calendar date (for Date columns, or midnight-in-column-tz for DateTime columns).YYYY-MM-DDTHH:MM:SS— naive datetime.YYYY-MM-DD HH:MM:SS— naive datetime with space separator (normalised pre-parse).YYYY-MM-DDTHH:MM:SS.ffffff— optional sub-second (ms resolution only — see “Precision” below).- Any of the above with a
Zor±HH:MMoffset suffix.
Column metadata for naive literals
Section titled “Column metadata for naive literals”A DateTime column declaration may carry optional metadata:
tz— IANA timezone name used to interpret naive ISO strings. Falls back to the Evaluator’sdefault_timezone, then to"UTC".unit—"ms"(default),"s", or"ns". Applied to numeric record values; non-numeric values ignore it.
{ "ts": { "type": "datetime", "tz": "Europe/Moscow", "unit": "s" }, "event_day": { "type": "date" }}Temporal functions
Section titled “Temporal functions”For relative-time queries — “in the last hour”, “since the start of this week”, “since today” — FlyQL provides built-in temporal functions: ago(...), now(), today(), and startOf(...).
created_at > ago(1h)
updated_at >= ago(7d)
expires_at < now()
event_time > startOf('day')
scheduled_for >= today('Europe/Berlin')
The full grammar (units, ordering rules, timezone arguments) is covered in Values & Types — Temporal Functions.
Daylight saving time
Section titled “Daylight saving time”When a naive datetime literal (no offset, no Z) is interpreted in a DST-observant tz, FlyQL picks the unambiguous resolution defined by the spec:
- Fall-back (ambiguous wall clock) — the earlier occurrence is chosen (
fold=0in Python semantics). Example:'2026-11-01 01:30:00'inAmerica/New_Yorkresolves to 05:30 UTC (EDT), not 06:30 UTC (EST). - Spring-forward (non-existent wall clock) — the value is rejected. The matcher returns
falsefor that record (skipped). Example:'2026-03-08 02:30:00'inAmerica/New_Yorkdoes not exist.
Records that fall into the spring-forward gap are treated as unparseable rather than silently normalized forward.
Supported record-value types (Python)
Section titled “Supported record-value types (Python)”datetime.datetime— tz-aware or naive (naive uses columntzor Evaluator default).datetime.date— calendar day only.pandas.Timestamp— works because it subclassesdatetime.- ISO-8601 strings — see accepted shapes above.
int/float— epoch milliseconds by default; set columnunit="s"for epoch seconds or"ns"for nanoseconds.
Unsupported today:
numpy.datetime64— convert before passing to the matcher:pd.Timestamp(value).to_pydatetime()orvalue.astype("datetime64[ms]").astype(int).
Precision
Section titled “Precision”FlyQL’s matcher compares temporal values at millisecond granularity. Sub-millisecond precision in source data is truncated before comparison:
- Python —
int(dt.timestamp() * 1000)drops microseconds. - Go —
UnixMilli()drops nanoseconds. - JavaScript —
Dateis natively ms.
Users who need nanosecond precision must pre-process records to bigint nanoseconds and declare unit="ns". The JS path uses Number which loses precision above 2^53 — the nanosecond BigInt path is reserved for a follow-up spec.
Schemaless numerics
Section titled “Schemaless numerics”Numeric record values without column schema are treated as epoch milliseconds. There is no autodetection heuristic — a record {"ts": 1712434800} (epoch seconds, by convention) is interpreted as 1970-01-20 not 2024. Users with epoch-seconds data MUST declare unit="s":
{ "ts": { "type": "datetime", "unit": "s" } }This is strict-by-design: an autodetection heuristic (e.g. value < 10^10 → seconds) fails at the boundary and on legitimate old-date values, so we prefer explicit declarations over silent guesses.
When to use which
Section titled “When to use which”| Intent | Use | Example |
|---|---|---|
| Compare to a fixed calendar date or timestamp | String literal | due_date < '2023-12-31' |
| Compare to a sliding time window | ago() | created_at > ago(1h) |
| Compare to the current moment | now() | expires_at < now() |
| Compare to today (timezone-aware) | today() | scheduled_for = today('UTC') |
| Compare to the start of day / week / month | startOf() | event_time > startOf('week') |
String literals stay constant — the same query string returns the same SQL, day after day. Temporal functions are evaluated at execute time, so the same FlyQL query produces a different time window every time it runs.