Class: Horologium::Representations::Iso8601
- Inherits:
-
Object
- Object
- Horologium::Representations::Iso8601
- Defined in:
- lib/horologium/representations/iso8601.rb,
sig/horologium/representations/iso8601.rbs
Overview
An instant written as an extended ISO 8601 date and time, in the scale it
is read in: 2025-05-01T12:00:00.000000000. It is a formatting of the
calendar date Civil reads, so the two agree on every field, and the
string is the shape a log, a fixture, or another tool reads.
The scale is not written into the string. There is no ISO 8601
designator for TAI or TT, and Z means UTC, so a bare time here is a
coordinate in the scale you asked for, not a claim about which scale that
is. UTC writes the Z that belongs to it, where a leap second and a zero
offset are real; the continuous scales carry neither.
The fraction of a second is written to nanosecond resolution, nine
digits, the resolution the example in the design carries. That is display
resolution, not the whole of what an instant holds: for the exact value,
read it as a JulianDate or a Civil with as: :rational. On the way
in, the parser keeps every digit it is given, unbounded at :exact, so a
string says as much as it likes and nothing is dropped before the
library.
Constant Summary collapse
- PATTERN =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
The strict subset of ISO 8601 the parser reads: a full calendar date, and an optional time of day after a
T, down to an optional fraction of a second and an optionalZor numeric offset. The year is four digits or more, with a minus sign for a year before 1. A numeric offset runs from -23:59 to +23:59. Anything outside that shape is refused rather than read part way: a week date, an ordinal date, a bare hour, a comma for the decimal point, a space for theT, an offset out of range. / \A (?<year>-?\d{4,})-(?<month>\d{2})-(?<day>\d{2}) (?: T (?<hour>\d{2}):(?<minute>\d{2}) (?::(?<second>\d{2})(?:\.(?<fraction>\d+))?)? (?<zone>Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)? )? \z /x- HALF_DAY =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Half a day, the gap between a Julian Date, which starts at noon, and the midnight the calendar counts a day from.
Rational(1, 2)
Class Method Summary collapse
-
.parse(value, _low, scale, precision) ⇒ Horologium::Numeric::TwoPartFloat, Horologium::Numeric::Exact
An ISO 8601 string, read as a Julian Date in days, at the precision asked for.
-
.render(reading, _output = :string) ⇒ String
The reading, written as an ISO 8601 string.
Class Method Details
.parse(value, _low, scale, precision) ⇒ Horologium::Numeric::TwoPartFloat, Horologium::Numeric::Exact
An ISO 8601 string, read as a Julian Date in days, at the precision asked for. This is the way in, where render is the way out. The Julian Date is in the scale the string is read in; it is Instant.from_iso8601 that reads it back in TAI.
Nothing is dropped: the date and time become an exact fraction of a
day, and every digit of the fraction of a second is kept, unbounded
at :exact. A numeric offset is subtracted here, in the scale, as
plain arithmetic on the fields; it is not a time zone and consults no
zone data. It counts against the day's SI length, so it shifts by SI
seconds on a leap second day and through the pre-1972 drift alike, not
by a stretched fraction of the day.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/horologium/representations/iso8601.rb', line 137 def parse(value, _low, scale, precision) fields = fields(value) in_scale = Civil.parse(fields.fetch(:civil), nil, scale, precision) offset_seconds = fields.fetch(:offset_seconds) return in_scale if offset_seconds.zero? day = (in_scale.to_r + HALF_DAY).floor Numeric::Precision.subtract( in_scale, Numeric::Precision.build( Rational(offset_seconds, scale.si_seconds_in_day(day)), precision ) ) end |
.render(reading, _output = :string) ⇒ String
The reading, written as an ISO 8601 string. The as type a Julian
Date chooses does not apply here: an ISO 8601 reading is always a
String, so the type is ignored.
The instant is rounded to the nearest nanosecond first, so a fraction that would round up to a whole second carries into the clock before the fields are read, and the fields never show a time that does not exist.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/horologium/representations/iso8601.rb', line 81 def render(reading, _output = :string) civil = Civil.render(nanosecond_reading(reading), :rational) nanoseconds = (civil.second_fraction * Duration::NANOSECONDS_PER_SECOND).round # A continuous scale writes no designator, so a bare time is a # coordinate in the scale it was read in; UTC writes "Z". designator = Horologium .configuration .scale(reading.scale) .zone_designator format( "%<date>sT%<hour>02d:%<minute>02d:%<second>02d.%<nanoseconds>09d" \ "%<designator>s", date: date(civil), hour: civil.hour, minute: civil.minute, second: civil.second, nanoseconds: nanoseconds, designator: designator ) end |