DecodeInterval (datetime.c:3364-3749) and the Adjust* helpers
(560-670).
It reads the SAME lexed fields the datetime decoders read -- it is a
fourth consumer of datetime/lex.clj, with buflen 256 rather than
153 or 129 -- and then interprets them by a different set of rules.
THE LOOP RUNS RIGHT TO LEFT (datetime.c:3397). Units FOLLOW their
values in an interval literal, so '1 day' is read as day and
then 1, with the unit setting type for the field about to be
read. Nothing else in the datetime family works this way, and
reversing it is not a detail: parsing_unit_val, AGO's
last-field-only rule and DTK_HOUR's hand-off all depend on the
direction.
The accumulator is pg_itm_in (timestamp.h:82): {:usec :mday :mon :year}, where YEAR is kept separate from MONTH during decoding and
folded in only at the end by itmin2interval. Keeping them apart is
what lets AdjustYears multiply by 10, 100 and 1000 for decade,
century and millennium without losing the int32 range.
OVERFLOW IS CHECKED AT EVERY STEP in the C, with
pg_add_s32_overflow and friends, and each failure is
DTERR_FIELD_OVERFLOW -- which interval_in then REMAPS to
DTERR_INTERVAL_OVERFLOW, SQLSTATE 22015, not 22008
(timestamp.c:932-941). Clojure longs do not overflow at int32, so
the bounds are checked explicitly; a missing check is a silently
wrapped value rather than an error.
`DecodeInterval` (datetime.c:3364-3749) and the `Adjust*` helpers
(560-670).
It reads the SAME lexed fields the datetime decoders read -- it is a
fourth consumer of `datetime/lex.clj`, with `buflen` 256 rather than
153 or 129 -- and then interprets them by a different set of rules.
THE LOOP RUNS RIGHT TO LEFT (datetime.c:3397). Units FOLLOW their
values in an interval literal, so `'1 day'` is read as `day` and
then `1`, with the unit setting `type` for the field about to be
read. Nothing else in the datetime family works this way, and
reversing it is not a detail: `parsing_unit_val`, AGO's
last-field-only rule and `DTK_HOUR`'s hand-off all depend on the
direction.
The accumulator is `pg_itm_in` (timestamp.h:82): `{:usec :mday :mon
:year}`, where YEAR is kept separate from MONTH during decoding and
folded in only at the end by `itmin2interval`. Keeping them apart is
what lets `AdjustYears` multiply by 10, 100 and 1000 for decade,
century and millennium without losing the int32 range.
OVERFLOW IS CHECKED AT EVERY STEP in the C, with
`pg_add_s32_overflow` and friends, and each failure is
DTERR_FIELD_OVERFLOW -- which `interval_in` then REMAPS to
DTERR_INTERVAL_OVERFLOW, SQLSTATE 22015, not 22008
(timestamp.c:932-941). Clojure longs do not overflow at int32, so
the bounds are checked explicitly; a missing check is a silently
wrapped value rather than an error.(adjust-fract-days itm frac scale)AdjustFractDays (564-578). The INTEGER part of the scaled fraction
becomes whole days and only the remainder becomes microseconds.
`AdjustFractDays` (564-578). The INTEGER part of the scaled fraction becomes whole days and only the remainder becomes microseconds.
(adjust-fract-microseconds itm frac scale)AdjustFractMicroseconds (536-561). The rounding is explicit and
is NOT Math/round: the C truncates to int64 and then nudges by one
only when the leftover fraction exceeds a half in either direction,
which rounds -0.5 and +0.5 DOWN in magnitude where Math/round
would round +0.5 up.
`AdjustFractMicroseconds` (536-561). The rounding is explicit and is NOT `Math/round`: the C truncates to int64 and then nudges by one only when the leftover fraction exceeds a half in either direction, which rounds -0.5 and +0.5 DOWN in magnitude where `Math/round` would round +0.5 up.
(adjust-fract-years itm frac scale)AdjustFractYears (581-588). rint -- round half to EVEN, which is
Math/rint, not Math/round. rint(0.5) is 0 and round(0.5) is
1, and the difference is reachable: '0.5 mons' would gain a month.
`AdjustFractYears` (581-588). `rint` -- round half to EVEN, which is `Math/rint`, not `Math/round`. `rint(0.5)` is 0 and `round(0.5)` is 1, and the difference is reachable: `'0.5 mons'` would gain a month.
(decode-interval fields)(decode-interval fields range sql-standard?)DecodeInterval. Returns
{:dtype :delta|:late|:early :months :days :micros}, or throws.
range is the typmod field set, defaulting to every field.
sql-standard? enables the force_negative pass, which only
IntervalStyle = SQL_STANDARD turns on.
`DecodeInterval`. Returns
`{:dtype :delta|:late|:early :months :days :micros}`, or throws.
`range` is the typmod field set, defaulting to every field.
`sql-standard?` enables the `force_negative` pass, which only
`IntervalStyle = SQL_STANDARD` turns on.(decode-iso8601-interval input)DecodeISO8601Interval. The designator format (P1Y2M3DT4H5M6S)
and both alternative formats -- basic (P00010203T040506) and
extended (P0001-02-03T04:05:06).
Two documented departures from the spec, both in the C's own
comment: a W field may coexist with other units, and decimals are
allowed in fields other than the least significant.
The C reaches the alternative formats by FALLTHROUGH -- case 'T'
and case '\0' fall into case '-' when the width-8 test fails --
so the three are not separate branches but one entered at different
points. Each branch here yields a uniform [:return m] or
[:recur …] step, because the fallthrough targets cannot be inner
functions: recur inside one would bind to the function, not the
loop.
`DecodeISO8601Interval`. The designator format (`P1Y2M3DT4H5M6S`) and both alternative formats -- basic (`P00010203T040506`) and extended (`P0001-02-03T04:05:06`). Two documented departures from the spec, both in the C's own comment: a `W` field may coexist with other units, and decimals are allowed in fields other than the least significant. The C reaches the alternative formats by FALLTHROUGH -- `case 'T'` and `case '\0'` fall into `case '-'` when the width-8 test fails -- so the three are not separate branches but one entered at different points. Each branch here yields a uniform `[:return m]` or `[:recur …]` step, because the fallthrough targets cannot be inner functions: `recur` inside one would bind to the function, not the loop.
INTERVAL_FULL_RANGE. Every field allowed, which is what a bare
::interval cast uses.
INTERVAL_FULL_RANGE. Every field allowed, which is what a bare `::interval` cast uses.
(range-default-type range)What a BARE number means, given the typmod range
(datetime.c:3421-3474).
This is the whole reason interval '1' hour is one hour and
'1'::interval is one second. The switch is on the EXACT field set,
not on its lowest member, and the arms are the ones the C lists --
anything else falls to SECOND.
What a BARE number means, given the typmod `range` (datetime.c:3421-3474). This is the whole reason `interval '1' hour` is one hour and `'1'::interval` is one second. The switch is on the EXACT field set, not on its lowest member, and the arms are the ones the C lists -- anything else falls to SECOND.
DTK_TIME_M, which a :time field claims whole.
DTK_TIME_M, which a `:time` field claims whole.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |