Oninit® UDR — tzdte: Function reference
Every function, cast and operator that ships with
tzdte.
Defaults are strict: if in doubt, fail. A local time that
does not exist or occurs twice, an unknown or wrongly spelled zone, an offset
that disagrees with the zone, and a bad flag are all errors, never guesses.
Correction is opt-in, through the flags below.
Flags
Functions marked [flags] take one optional last argument, either an
INTEGER bitmap or an LVARCHAR of names. Omitted,
0 and '' all mean the defaults.
| Bit | Name | Effect |
| 1 | AutoCorrectInsert | A local time in a gap moves forward by the real gap; one in an overlap takes the earliest instant. Used by tzdte_from_local. |
| 2 | AutoCorrectRetrieve | The same two corrections for a wall-clock result: tzdte_add_days, tzdte_add_months, tzdte_trunc_day, tzdte_rebase. |
| 4 | RecalculateOffset | Present the value under IANA rules (the current generation, or the one you name) instead of its stored offset. The instant does not change. |
| 8 | UsePreviousIanaGeneration | If the requested generation is not installed, use the highest installed one below it. |
| 16 | UseNextIanaGeneration | If the requested generation is not installed, use the lowest installed one above it. Cannot be combined with 8. |
- Text form: names separated by commas, exact case, no spaces:
'AutoCorrectInsert,RecalculateOffset'.
- Anything else is an error: an unknown name or bit, a negative number, a
duplicate, an empty or trailing item, spaces, or both generation
directions.
- A flag never corrects a zone name, guesses a zone, picks a generation
for you or hides a corrupt value.
- A text literal cannot carry flags, so use
tzdte_from_local
where you need AutoCorrectInsert.
Constructing values
| Function | Returns | Detail |
tzdte_from_local(dt, zone [, flags]) [flags] | tzdte | Builds a value from a wall-clock DATETIME in a zone, resolved under the current generation, which is recorded in the value. A missing or repeated local time is an error unless AutoCorrectInsert is given: a gap moves forward by the real gap, an overlap takes the earliest instant. |
tzdte_from_utc(dt, zone) | tzdte | Builds a value from a DATETIME taken as UTC, in zone, with the offset from the current generation. |
tzdte_from_datetime(dt) | tzdte | The explicit cast from DATETIME. The value is taken as UTC and given the zone UTC. |
tzdte_now([zone]) | tzdte | The current instant. The clock is read once per statement, like CURRENT. zone defaults to UTC. It never follows the server's TZ. |
tzdte_at(t, zone) | tzdte | The same instant in another zone, with the offset from the current generation. |
Taking values apart
| Function | Returns | Detail |
tzdte_utc(t) | DATETIME YEAR TO FRACTION(5) | The instant as a UTC DATETIME, exact. |
tzdte_local(t [, flags]) [flags] | DATETIME YEAR TO FRACTION(5) | The wall-clock time in the value's own zone at its stored offset. With RecalculateOffset, under the current generation's rules. |
tzdte_zone(t) | LVARCHAR | The zone name the value was written with. For a fixed-offset value, the offset itself, such as +05:30. |
tzdte_offset(t [, flags]) [flags] | INTEGER | The stored offset from UTC, in seconds, east positive. With RecalculateOffset, the offset the current generation gives. |
tzdte_iso(t [, flags]) [flags] | LVARCHAR | ISO 8601 text without the zone name, for clients that need it. |
tzdte_text(t [, flags]) [flags] | LVARCHAR | The display text, the same as the cast to LVARCHAR, but accepting flags such as RecalculateOffset. |
tzdte_stored_generation(t) | INTEGER | The IANA generation id stored in the value; 0 for UTC and fixed-offset values. |
Casts
| Cast | Kind | Detail |
LVARCHAR to tzdte | Implicit | Parses text. Needs an offset, a zone, or both. Zone names match exactly, including case. If both are given, the offset must agree with the zone's rules in the value's generation, which is the current one or the one named by a trailing #<gen>. |
tzdte to LVARCHAR | Explicit | Formats the stored local time and offset as YYYY-MM-DD HH:MM:SS.fffff±HH:MM Zone. Needs no zone rules. UNLOAD and dbexport add #<gen> for named zones so that LOAD restores the identical value. |
DATETIME to tzdte | Explicit | The DATETIME is taken as UTC. |
tzdte to DATETIME | Explicit | The UTC instant. |
There is no implicit cast between DATETIME and
tzdte: it would silently treat CURRENT, which is
server-local time, as UTC.
Converting to DATETIME
Which DATETIME you get depends on which time you ask for:
| You want | Use | Result for 2026-07-01 10:00 America/Chicago |
| The UTC time | tzdte_utc(t) or t::DATETIME YEAR TO FRACTION(5) | 2026-07-01 15:00:00.00000 |
| The wall-clock time in the value's own zone | tzdte_local(t) | 2026-07-01 10:00:00.00000 |
| The wall-clock time in another zone | tzdte_local(tzdte_at(t, 'Asia/Tokyo')) | 2026-07-02 00:00:00.00000 |
| A shorter qualifier | EXTEND(tzdte_local(t), YEAR TO DAY) | 2026-07-01 |
- The cast gives the UTC time, not the local time. Use
tzdte_local() for local time.
- The result is
DATETIME YEAR TO FRACTION(5), which has the same
10-microsecond resolution as a tzdte, so the cast loses nothing.
- The zone and the stored offset are not kept. Keep the zone
(
tzdte_zone(t)) alongside if you need to rebuild the value later
with tzdte_from_utc(dt, zone) or tzdte_from_local(dt, zone).
Comparison
=, <>, <,
<=, >, >= and
compare() use the UTC instant only. The zone is metadata. They
never touch zone data, so they are cheap in sort-merge joins and index
scans.
DISTINCT keeps an arbitrary zone among equal instants.
GROUP BY on the column does too when an index on the column
supplies the order. Otherwise it fails with error 9643, as does
COUNT(DISTINCT col): the type cannot be hashed. Group or count
over a derived value instead, such as GROUP BY tzdte_utc(col).
- A unique index rejects 10:00 America/Chicago if 15:00
UTC is already present, in July.
Arithmetic
| Expression | Returns | Detail |
t + day-time interval, interval + t, t - interval | tzdte | Exact elapsed time, so INTERVAL(24) HOUR TO HOUR and INTERVAL(1) DAY TO DAY are the same. (t + d) - t = d always holds. |
t + YEAR TO MONTH interval | error | Not accepted (error 1266). Use tzdte_add_months for calendar months. |
t1 - t2 | INTERVAL DAY(9) TO FRACTION(5) | Elapsed time, exact (10 microseconds). |
tzdte_add_days(t, n) | tzdte | Same wall-clock time n days later, under the current generation. [flags] A time that does not exist on the target day, or occurs twice, is an error unless AutoCorrectRetrieve is given. |
tzdte_add_months(t, n) | tzdte | Calendar months, clamping to the last day of the month. [flags] Gap and overlap as for tzdte_add_days. |
tzdte_trunc_day(t) | tzdte | Local midnight. [flags] Where midnight does not exist or occurs twice (America/Havana), an error unless AutoCorrectRetrieve is given. |
Rule changes and generations
A value keeps the offset and the IANA generation it was written with, and
is displayed from those. A rule change never rewrites a row. These functions
find and act on the difference:
| Function | Returns | Detail |
tzdte_rules_changed(t) | BOOLEAN | True when the stored offset no longer matches the zone's rules in the current generation. UTC and fixed-offset values never change. |
tzdte_recalc(t [, flags]) [flags] | tzdte | The same instant and zone with the offset and generation of the current generation. The row is not changed. |
tzdte_recalc_gen(t, gen [, flags]) [flags] | tzdte | The same, under generation gen. A generation that is not installed is an error unless UsePreviousIanaGeneration or UseNextIanaGeneration is given. |
tzdte_rebase(t [, flags]) [flags] | tzdte | A new conversion of the original wall-clock time under the current generation: a new instant and a new stored generation. For schedules, not for records of things that happened. Gap and overlap as for tzdte_add_days. |
Administration
| Routine | Detail |
tzdte_reload() | Builds the cache of every installed generation from tzdte_zone_data. Cheap when nothing has changed. Called from sysdbopen() and after a load. A generation that fails validation is left out; if it is the current one the reload fails and the previous cache stays. |
tzdte_tzdata_version() | The tzdata version of the current generation. |
tzdte_current_generation() | The id of the current generation. |
tzdte_generation_checksum(gen) | Recomputes the canonical SHA-256 of generation gen from its rows and compares it with the stored one. Returns JSON text: {"generation":1,"tzdata_version":"2026a","stored":"…","calculated":"…","status":"PASS"}. status is PASS, FAIL or MISSING. It detects accidental or unsupported changes; it is not a defence against someone who changes both the data and the checksum. |
tzdte_generation_valid(gen) | True when the generation's rows decode and its checksum passes. DBA only. |
tzdte_apply_tzdata() | Applies a staged bundle as a new generation. Insert only: a generation or tzdata version that is already installed is refused. DBA only. Called by tzdte_load_tzdata.sql. |
Errors
| Condition | Detail |
| No zone or offset | The literal has neither. It is rejected, not assumed to be UTC. |
| Zone not found | Names match exactly: america/chicago is an error, as are abbreviations such as CST and any name absent from tzdte_zones. |
| Local time missing or ambiguous | An error by default. Give an offset in the text, or use AutoCorrectInsert (input) or AutoCorrectRetrieve (calendar results). |
| Offset does not match the zone | Text such as 2026-07-01 10:00-06:00 America/Chicago whose offset is not the zone's offset in the value's generation. |
| Invalid flags | Unknown, duplicate, malformed or conflicting flags. |
| Required IANA generation is not installed | From tzdte_recalc_gen or RecalculateOffset, unless a fallback flag is given. Default retrieval needs no generation. |
| Out of range | The instant or the local time falls outside 0001-01-01 to 9999-12-31. |
| Corrupt value | A stored or received value fails its structural checks. It is reported, never repaired. |
| Year-to-month interval | Error 1266 from + or -. Use tzdte_add_months, which clamps to the month end. |
| Not hashable | Error 9643 from COUNT(DISTINCT col), or from GROUP BY col when no index on col supplies the order. Group or count over a derived value such as tzdte_utc(col). |
Examples
-- 09:00 Chicago on the day before the spring change
SELECT tzdte_from_local(DATETIME (2026-03-07 09:00:00) YEAR TO SECOND,
'America/Chicago')::LVARCHAR
FROM sysmaster:sysdual;
-- 2026-03-07 09:00:00.00000-06:00 America/Chicago
-- the same instant in Tokyo
SELECT tzdte_at('2026-07-01 10:00:00 America/Chicago', 'Asia/Tokyo')::LVARCHAR
FROM sysmaster:sysdual;
-- 2026-07-02 00:00:00.00000+09:00 Asia/Tokyo
-- a local time inside the spring gap is an error ...
SELECT tzdte_from_local(DATETIME (2026-03-08 02:30:00) YEAR TO SECOND,
'America/Chicago')
FROM sysmaster:sysdual;
-- tzdte: local time does not exist in zone (DST gap); use AutoCorrectInsert
-- or give an offset
-- ... unless you ask for it to be corrected
SELECT tzdte_from_local(DATETIME (2026-03-08 02:30:00) YEAR TO SECOND,
'America/Chicago', 'AutoCorrectInsert')::LVARCHAR
FROM sysmaster:sysdual;
-- 2026-03-08 03:30:00.00000-05:00 America/Chicago
-- the same instant under the current rules, on request
SELECT tzdte_text(starts_at, 'RecalculateOffset') FROM meetings;