Liking cljdoc? Tell your friends :D

datahike.pg.datetime.parse

DecodeDateTime (datetime.c:979-1534): the state machine that turns lexed fields into a date and time.

It walks the fields once, left to right, and each field CLAIMS the parts of the result it set. A field that claims something already claimed is an error -- that single rule is what makes '2001-02-03 +05 +06' and 'Feb Feb 10 1997' errors rather than last-one-wins, and it is why fmask and tmask are both needed: one is everything set so far, the other is what this field set.

Two pieces of state carry ACROSS fields and are the reason this cannot be a fold over independent fields:

ptype is a prefix set by a UNITS or ISOTIME token that changes how the NEXT field is read. J makes the next number a Julian day, T makes it a time. It must be consumed by the following field -- a literal ending with a dangling J is an error.

text-month? records that a month arrived as a word, which changes DecodeNumber's placement rules and enables a retroactive swap.

The zone is deliberately NOT resolved during the walk. An abbreviation, a zone name and the session default all need the DATE to resolve against -- an offset is a function of the instant -- so the walk only records WHICH zone, and the resolution happens at the end, after ValidateDate. Getting this backwards resolves the zone against the wrong day across a DST boundary.

`DecodeDateTime` (datetime.c:979-1534): the state machine that turns
lexed fields into a date and time.

It walks the fields once, left to right, and each field CLAIMS the
parts of the result it set. A field that claims something already
claimed is an error -- that single rule is what makes
`'2001-02-03 +05 +06'` and `'Feb Feb 10 1997'` errors rather than
last-one-wins, and it is why `fmask` and `tmask` are both needed:
one is everything set so far, the other is what this field set.

Two pieces of state carry ACROSS fields and are the reason this
cannot be a fold over independent fields:

`ptype` is a prefix set by a UNITS or ISOTIME token that changes how
the NEXT field is read. `J` makes the next number a Julian day, `T`
makes it a time. It must be consumed by the following field --
a literal ending with a dangling `J` is an error.

`text-month?` records that a month arrived as a word, which changes
`DecodeNumber`'s placement rules and enables a retroactive swap.

The zone is deliberately NOT resolved during the walk. An
abbreviation, a zone name and the session default all need the
DATE to resolve against -- an offset is a function of the instant --
so the walk only records WHICH zone, and the resolution happens at
the end, after `ValidateDate`. Getting this backwards resolves the
zone against the wrong day across a DST boundary.
raw docstring

decode-datetimeclj

(decode-datetime fields {:keys [date-order now zone?] :as ctx})

DecodeDateTime. Returns {:dtype :date|:epoch|:late|:early :tm {…} :fields #{…} :zone …}, or throws a dterr.

ctx supplies what the C reads from global state: :date-order (DateStyle), :now (the transaction timestamp, for now/today), :zone? (whether this caller accepts a zone at all -- the C's tzp != NULL, which is how date_in refuses 'J2451187 +05').

The ZONE is returned unresolved, as {:kind :offset|:abbrev|:named …}, because resolving it needs the validated date.

`DecodeDateTime`. Returns
`{:dtype :date|:epoch|:late|:early :tm {…} :fields #{…} :zone …}`,
or throws a `dterr`.

`ctx` supplies what the C reads from global state: `:date-order`
(DateStyle), `:now` (the transaction timestamp, for `now`/`today`),
`:zone?` (whether this caller accepts a zone at all -- the C's
`tzp != NULL`, which is how `date_in` refuses `'J2451187 +05'`).

The ZONE is returned unresolved, as `{:kind :offset|:abbrev|:named
…}`, because resolving it needs the validated date.
sourceraw docstring

decode-time-onlyclj

(decode-time-only fields {:keys [date-order now zone?] :as ctx})

DecodeTimeOnly (datetime.c:1838-2216): the same fields, read as a TIME.

This is a separate function from decode-datetime in PostgreSQL and it stays separate here. The two look similar enough to invite factoring and they differ in a dozen places, every one of which changes an answer:

The number thresholds are different. DecodeNumberField is reached at flen > 4 here and at flen >= 6 there, and every call passes fmask | DTK_DATE_M -- pretending the date is already complete. That is what makes '040506' a TIME to this function and a DATE to the other, from identical input.

A leading :date field is only read as a date when it is first AND the field list looks like a date followed by a time -- the C tests i == 0 && nf >= 2 && (ftype[nf-1] == DTK_DATE || ftype[1] == DTK_TIME), a positional test on the OTHER fields. Otherwise it is a zone.

Only two RESERV words are allowed: now and allballs. today, epoch and infinity are format errors for a time -- there is no infinite time of day.

There is no MONTH case and no DOW case, so a month name or a weekday is a format error rather than being read or dropped.

time_overflows is checked ONCE at the end, after AM/PM, rather than per field.

And the zone resolution differs: a named zone with a FIXED offset needs no date, while one with DST rules does -- '12:00 UTC' is fine and '12:00 America/New_York' is not, because the offset is not a function of the time alone.

`DecodeTimeOnly` (datetime.c:1838-2216): the same fields, read as a
TIME.

This is a separate function from `decode-datetime` in PostgreSQL and
it stays separate here. The two look similar enough to invite
factoring and they differ in a dozen places, every one of which
changes an answer:

The number thresholds are different. `DecodeNumberField` is reached
at `flen > 4` here and at `flen >= 6` there, and every call passes
`fmask | DTK_DATE_M` -- pretending the date is already complete. That
is what makes `'040506'` a TIME to this function and a DATE to the
other, from identical input.

A leading `:date` field is only read as a date when it is first AND
the field list looks like a date followed by a time -- the C tests
`i == 0 && nf >= 2 && (ftype[nf-1] == DTK_DATE || ftype[1] ==
DTK_TIME)`, a positional test on the OTHER fields. Otherwise it is a
zone.

Only two RESERV words are allowed: `now` and `allballs`. `today`,
`epoch` and `infinity` are format errors for a time -- there is no
infinite time of day.

There is no MONTH case and no DOW case, so a month name or a weekday
is a format error rather than being read or dropped.

`time_overflows` is checked ONCE at the end, after AM/PM, rather
than per field.

And the zone resolution differs: a named zone with a FIXED offset
needs no date, while one with DST rules does -- `'12:00 UTC'` is
fine and `'12:00 America/New_York'` is not, because the offset is
not a function of the time alone.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close