Independent, self-contained module providing the element-level Parquet date/time value types.
Provides three public types, each holding ONE element (not a whole column -- unlike
parquet_string_column in the parquet_strings module):
parquet_date -- a calendar date, stored as days since 1970-01-01 (identical to the
physical value of Parquet's DATE / Arrow's date32 type).parquet_time -- a time of day, stored as nanoseconds since midnight (holds any Parquet
TIME unit -- seconds/millis/micros/nanos -- exactly).parquet_timestamp -- an instant, stored losslessly as seconds since 1970-01-01T00:00:00
plus a nanosecond-of-second part (holds any Parquet TIMESTAMP unit, including legacy
INT96 files, exactly).All three are plain value types with an internal null state; a default-initialized element
is null ("no value yet"). Element access follows a two-tier rule: semantic accessors
(get, year, to_string, to_mjd, to_unix, comparison operators, ...) abort on a
null element (guard with is_null), interop accessors (raw/set_raw/get_raw) never
abort (they return 0 for a null element -- validity travels separately via is_null), and
type-to-type conversions (get_date/get_time, set(date, time)) propagate null instead
of aborting. Most procedures are elemental, so they apply directly to whole arrays
(mask = ts%is_null(), call dates%set(years, months, days)).
Calendar math uses the proleptic Gregorian calendar (Howard Hinnant's exact-integer
days_from_civil/civil_from_days algorithms). Timezone interpretation is deliberately
out of scope: a parquet_timestamp always holds the stored epoch offset verbatim, and a
column's timezone metadata is a column-level property of the read/write layer, not of the
element. This module depends only on intrinsic modules (iso_fortran_env,
ieee_arithmetic); it has no dependency on any other module in this library (the
read/write integration layer depends on it, never the reverse).
Why the setters take intent(inout) rather than intent(out), and the obligation that
creates. A POLYMORPHIC intent(out) dummy is not free: the compiler default-initialises the
element through the runtime on entry, and a type-bound procedure's passed-object dummy has to be
polymorphic, so an elemental setter pays that per element. Measured on a 4M-row whole-column
read: the construction loop cost 12.3 ms with intent(out) and 3.4 ms with intent(inout)
(3.6x), taking the whole date-column read from 23.5 ms to 14.5 ms. The same change on the
timestamp setters took its loop from 17.2 ms to 5.7 ms.
The cost of that is an obligation moved from the compiler to this source: intent(out) reset
EVERY component for free, whereas under intent(inout) a component a setter does not assign
keeps whatever the element held before. So every intent(inout) setter here must assign every
component of its type, and adding a component to one of these types means revisiting all of
them. tools/check_source_conventions.py's check_temporal_setters_assign_all enforces exactly
that, deriving both the component list and the setter list from this file so a new component or
a new setter is covered without editing the check.
The converse is equally load-bearing and is enforced too: a setter with a caught-failure path
that RETURNS without assigning self -- %parse with a success argument, and
ts_set_date_time's null propagation -- must KEEP intent(out), because that is precisely what
makes a failed or null-propagating call yield a null element rather than a stale one.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | public, | parameter | :: | parquet_unit_seconds | = | 1 |
Time-unit selectors for |
| integer, | public, | parameter | :: | parquet_unit_millis | = | 2 |
milliseconds since the epoch. |
| integer, | public, | parameter | :: | parquet_unit_micros | = | 3 |
microseconds since the epoch. |
| integer, | public, | parameter | :: | parquet_unit_nanos | = | 4 |
nanoseconds since the epoch. |
| integer(kind=int64), | public, | parameter | :: | parquet_ns_per_sec | = | NS_PER_SECOND |
Public unit-conversion convenience constants for the raw nanosecond values the
difference/offset operators traffic in. The |
| integer(kind=int64), | public, | parameter | :: | parquet_ns_per_day | = | NS_PER_DAY |
exact; ns in one day. |
| real(kind=real64), | public, | parameter | :: | parquet_ns_to_sec | = | 1.0e-9_real64 |
convenience; ns -> fractional seconds. |
| real(kind=real64), | public, | parameter | :: | parquet_ns_to_day | = | 1.0_real64/86400.0e9_real64 |
convenience; ns -> fractional days. |
Constructs a valid parquet_date from (year, month, day); aborts on an invalid civil date (the structure constructor itself is unavailable outside this module -- components are private -- so this generic takes its place).
Constructor specific for the parquet_date generic: builds a valid date element from (year, month, day); aborts on an invalid civil date.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int32), | intent(in) | :: | year |
calendar year. |
||
| integer(kind=int32), | intent(in) | :: | month |
month, 1..12. |
||
| integer(kind=int32), | intent(in) | :: | day |
day of month. |
the constructed element (valid).
Constructs a valid parquet_time from (hour, minute, second[, nanosecond]); aborts on invalid fields.
Constructor specific for the parquet_time generic: builds a valid time element from (hour, minute, second[, nanosecond]); aborts on invalid fields.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int32), | intent(in) | :: | hour |
hour, 0..23. |
||
| integer(kind=int32), | intent(in) | :: | minute |
minute, 0..59. |
||
| integer(kind=int32), | intent(in) | :: | second |
second, 0..59. |
||
| integer(kind=int32), | intent(in), | optional | :: | nanosecond |
sub-second part, 0..999999999 (default 0). |
the constructed element (valid).
Constructs a valid parquet_timestamp, either from full civil fields (year, month, day, hour, minute, second[, nanosecond]) -- aborting on invalid fields -- or from a (parquet_date, parquet_time) pair, where a null input propagates to a null result.
Civil-fields constructor specific for the parquet_timestamp generic; aborts on invalid fields.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int32), | intent(in) | :: | year |
calendar year. |
||
| integer(kind=int32), | intent(in) | :: | month |
month, 1..12. |
||
| integer(kind=int32), | intent(in) | :: | day |
day of month. |
||
| integer(kind=int32), | intent(in) | :: | hour |
hour, 0..23. |
||
| integer(kind=int32), | intent(in) | :: | minute |
minute, 0..59. |
||
| integer(kind=int32), | intent(in) | :: | second |
second, 0..59. |
||
| integer(kind=int32), | intent(in), | optional | :: | nanosecond |
sub-second part, 0..999999999 (default 0). |
the constructed element (valid).
Date-plus-time constructor specific for the parquet_timestamp generic; a null input propagates to a null result (never aborts).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(parquet_date), | intent(in) | :: | date |
the calendar-date part. |
||
| type(parquet_time), | intent(in) | :: | time |
the time-of-day part. |
the constructed element (null if an input is null).
A calendar date (proleptic Gregorian), stored as days since 1970-01-01 -- identical to the physical value of a Parquet DATE / Arrow date32 column. A default-initialized element is null. Range: about +-5.8 million years.
Constructs a valid parquet_date from (year, month, day); aborts on an invalid civil date (the structure constructor itself is unavailable outside this module -- components are private -- so this generic takes its place).
| private impure, elemental function date_new (year, month, day) | Constructor specific for the parquet_date generic: builds a valid date element from (year, month, day); aborts on an invalid civil date. |
| procedure, public :: set => date_set | Sets from a validated (year, month, day). |
| procedure, public :: get => date_get | Returns year, month, day (aborts on null). |
| procedure, public :: year => date_year | Calendar year (aborts on null). |
| procedure, public :: month => date_month | Calendar month 1..12 (aborts on null). |
| procedure, public :: day => date_day | Day of month 1..31 (aborts on null). |
| procedure, public :: is_null => date_is_null | Whether the element is null (never aborts). |
| procedure, public :: set_null => date_set_null | Marks the element null. |
| procedure, public :: set_raw => date_set_raw | Sets the raw day count (interop; marks valid). |
| procedure, public :: raw => date_raw | Raw day count; 0 for a null element (interop). |
| generic, public :: set_mjd => date_set_mjd_i32, date_set_mjd_i64 | Sets from an integer Modified Julian Date. |
| procedure, public :: to_mjd => date_to_mjd | Integer Modified Julian Date (aborts on null). |
| procedure, public :: to_string => date_to_string | ISO-8601 "YYYY-MM-DD" (aborts on null). |
| procedure, public :: parse => date_parse | Sets from an ISO-8601 date string. |
| generic, public :: operator(==) => date_eq | Equality (aborts on a null operand). |
| generic, public :: operator(/=) => date_ne | Inequality (aborts on a null operand). |
| generic, public :: operator(<) => date_lt | Ordering (aborts on a null operand). |
| generic, public :: operator(<=) => date_le | Ordering (aborts on a null operand). |
| generic, public :: operator(>) => date_gt | Ordering (aborts on a null operand). |
| generic, public :: operator(>=) => date_ge | Ordering (aborts on a null operand). |
| generic, public :: operator(-) => date_diff, date_sub_days_i32, date_sub_days_i64 | Difference or day offset (aborts on a null operand / out-of-range result). |
| generic, public :: operator(+) => date_add_days_i32, date_add_days_i64 | Day offset (aborts on a null operand / out-of-range result). |
A time of day, stored as nanoseconds since midnight, [0, 86400e9 - 1]. Holds any Parquet TIME unit (seconds/millis/micros/nanos) exactly. A default-initialized element is null.
Constructs a valid parquet_time from (hour, minute, second[, nanosecond]); aborts on invalid fields.
| private impure, elemental function time_new (hour, minute, second, nanosecond) | Constructor specific for the parquet_time generic: builds a valid time element from (hour, minute, second[, nanosecond]); aborts on invalid fields. |
| procedure, public :: set => time_set | Sets from validated (hour, minute, second[, nanosecond]). |
| procedure, public :: get => time_get | Returns hour, minute, second[, nanosecond] (aborts on null). |
| procedure, public :: hour => time_hour | Hour 0..23 (aborts on null). |
| procedure, public :: minute => time_minute | Minute 0..59 (aborts on null). |
| procedure, public :: second => time_second | Second 0..59 (aborts on null). |
| procedure, public :: nanosecond => time_nanosecond | Sub-second part 0..999999999 (aborts on null). |
| procedure, public :: is_null => time_is_null | Whether the element is null (never aborts). |
| procedure, public :: set_null => time_set_null | Marks the element null. |
| procedure, public :: set_raw => time_set_raw | Sets raw ns-since-midnight (interop; marks valid). |
| procedure, public :: raw => time_raw | Raw ns-since-midnight; 0 for a null element (interop). |
| procedure, public :: to_string => time_to_string | ISO-8601 "HH:MM:SS[.fraction]" (aborts on null). |
| procedure, public :: parse => time_parse | Sets from an ISO-8601 time string. |
| generic, public :: operator(==) => time_eq | Equality (aborts on a null operand). |
| generic, public :: operator(/=) => time_ne | Inequality (aborts on a null operand). |
| generic, public :: operator(<) => time_lt | Ordering (aborts on a null operand). |
| generic, public :: operator(<=) => time_le | Ordering (aborts on a null operand). |
| generic, public :: operator(>) => time_gt | Ordering (aborts on a null operand). |
| generic, public :: operator(>=) => time_ge | Ordering (aborts on a null operand). |
| generic, public :: operator(-) => time_diff, time_sub_ns_i32, time_sub_ns_i64 | Difference or ns offset (wraps; aborts on a null operand or a >24h offset magnitude). |
| generic, public :: operator(+) => time_add_ns_i32, time_add_ns_i64 | Ns offset (wraps; aborts on a null operand or a >24h offset magnitude). |
An instant, stored losslessly as whole seconds since 1970-01-01T00:00:00 plus a normalized nanosecond-of-second part (always 0..999999999, also for pre-epoch instants). Holds any Parquet TIMESTAMP unit exactly over the full int64 range of the stored value. Timezone-agnostic: holds the stored epoch offset verbatim. A default-initialized element is null.
Constructs a valid parquet_timestamp, either from full civil fields (year, month, day, hour, minute, second[, nanosecond]) -- aborting on invalid fields -- or from a (parquet_date, parquet_time) pair, where a null input propagates to a null result.
| private impure, elemental function ts_new_civil (year, month, day, hour, minute, second, nanosecond) | Civil-fields constructor specific for the parquet_timestamp generic; aborts on invalid fields. |
| private impure, elemental function ts_new_date_time (date, time) | Date-plus-time constructor specific for the parquet_timestamp generic; a null input propagates to a null result (never aborts). |
| generic, public :: set => ts_set_civil, ts_set_date_time | Sets from civil fields or a date + time pair. |
| procedure, public :: get => ts_get | Returns all civil fields (aborts on null). |
| procedure, public :: get_date => ts_get_date | The date part; null propagates (never aborts). |
| procedure, public :: get_time => ts_get_time | The time-of-day part; null propagates (never aborts). |
| procedure, public :: is_null => ts_is_null | Whether the element is null (never aborts). |
| procedure, public :: set_null => ts_set_null | Marks the element null. |
| procedure, public :: set_raw => ts_set_raw | Sets the raw (seconds, nanoseconds) pair (interop). |
| procedure, public :: get_raw => ts_get_raw | Raw (seconds, nanoseconds); zeros when null (interop). |
| generic, public :: set_unix => ts_set_unix_i32, ts_set_unix_i64 | Sets from a Unix-time value in a given unit. |
| procedure, public :: to_unix => ts_to_unix | Unix-time value in a given unit (aborts on null/loss). |
| procedure, public :: set_mjd => ts_set_mjd | Sets from a real64 Modified Julian Date. |
| procedure, public :: to_mjd => ts_to_mjd | real64 Modified Julian Date (aborts on null). |
| procedure, public :: set_jd => ts_set_jd | Sets from a real64 Julian Date. |
| procedure, public :: to_jd => ts_to_jd | real64 Julian Date (aborts on null). |
| procedure, public :: to_string => ts_to_string | ISO-8601 "YYYY-MM-DDTHH:MM:SS[.fraction]" (aborts on null). |
| procedure, public :: parse => ts_parse | Sets from an ISO-8601 date-time string. |
| generic, public :: operator(==) => ts_eq | Equality (aborts on a null operand). |
| generic, public :: operator(/=) => ts_ne | Inequality (aborts on a null operand). |
| generic, public :: operator(<) => ts_lt | Ordering (aborts on a null operand). |
| generic, public :: operator(<=) => ts_le | Ordering (aborts on a null operand). |
| generic, public :: operator(>) => ts_gt | Ordering (aborts on a null operand). |
| generic, public :: operator(>=) => ts_ge | Ordering (aborts on a null operand). |
| procedure, public :: diff_seconds => ts_diff_seconds | Real64-seconds difference (ts, ts); never aborts on magnitude. |
| generic, public :: operator(-) => ts_diff_ns, ts_sub_ns_i32, ts_sub_ns_i64 | Difference or ns offset (aborts on a null operand or int64 overflow). |
| generic, public :: operator(+) => ts_add_ns_i32, ts_add_ns_i64 | Ns offset (aborts on a null operand or int64 overflow). |