Civil.Time
A time of day, with no date and no time zone.
17:45:00 is a time of day. It is not a moment in time and it is not a
duration. It comes around once a day everywhere, at a different moment in each
place, and on the day a time zone changes its clocks it can come around twice
or not at all. It is a reading on a clock: what RFC 3339 calls a
partial-time, and what someone means when they write down when a shop opens.
Seconds are optional, fractions are unlimited
TOML 1.1 makes the seconds field optional, as ISO 8601 does and RFC 3339 does
not. This module follows TOML: 13:37 parses, and is written back as
13:37:00, because the written form always has all three fields. Neither
standard limits the number of digits after the decimal point, and neither does
this module.
The fraction is kept as text, however many digits it has, so nothing is
rounded away. Trailing zeroes are dropped, so that == compares the time
rather than the spelling: 10:32:00.5 and 10:32:00.50 are the same value.
If you need the original spelling, keep the source text; a value cannot hold
both.
Leap seconds
A second of 60 is accepted, because RFC 3339 allows it and a timestamp that
records a leap second is valid. It is stored and written back as 60. This
module does no arithmetic that would need to decide what a leap second means:
secondOfDay returns 86400 for 23:59:60, one past the
last ordinary second of the day.
The 60 is checked as a field on its own, not as part of an HH:MM:SS that
has to read 23:59:60. 12:30:60 is a time here, and secondOfDay gives it
45060. That is RFC 3339's grammar, which allows 60 in the seconds field
and leaves the question of whether a leap second was really inserted at that
instant to whoever knows. It has to: a leap second falls at 23:59:60 UTC,
which is some other wall clock at every other offset, so the same second is
1990-12-31T15:59:60-08:00 in California.
A time of day.
Creating
A time from its parts. The fraction is the digits after the decimal point,
without the point itself, or "" for no fraction.
Time.fromParts { hour = 10, minute = 32, second = 0, fraction = "555" }
|> Maybe.map Time.toString
--> Just "10:32:00.555"
Nothing if the hour is outside 0 to 23, the minute outside 0 to 59, the second
outside 0 to 60, or the fraction is anything but digits.
Trailing zeroes are dropped from the fraction, so a fraction of "5550" gives
the same value as "555".
The fraction is a String and not an Int because its digits are positional
and a number loses that. "5" is half a second and "05" is a twentieth of
one, but both are the number 5; only the text says where after the point the
digits start.
Time.fromParts { hour = 10, minute = 32, second = 0, fraction = "05" }
|> Maybe.map Time.toString
--> Just "10:32:00.05"
A number would also have to fix a unit and a precision, milliseconds or
nanoseconds or some other, and the number of digits here is not limited, so
everything past that unit would be rounded away. Text keeps every digit that
was written.
millisecond and nanosecond are there for
when a number is what you want.
00:00:00.
Reading
The hour, 0 to 23.
The minute, 0 to 59.
The second, 0 to 60. Sixty is a leap second.
The digits after the decimal point, without the point, with trailing zeroes
already removed. "" when there is no fraction.
Time.fromString "10:32:00.5500" |> Maybe.map Time.fraction
--> Just "55"
The fraction as whole milliseconds, truncated.
Time.fromString "10:32:00.5559" |> Maybe.map Time.millisecond
--> Just 555
The fraction as whole nanoseconds, truncated. Digits past the ninth are
dropped; fraction keeps them all.
Time.fromString "10:32:00.5" |> Maybe.map Time.nanosecond
--> Just 500000000
Seconds since midnight, ignoring the fraction.
Time.fromString "01:00:00" |> Maybe.map Time.secondOfDay
--> Just 3600
A leap second gives 86400, one past the last ordinary second of the day.
See the note on leap seconds at the top of this module.
Strings
Read an HH:MM, HH:MM:SS, or HH:MM:SS.fff time.
Time.fromString "13:37" |> Maybe.map Time.toString
--> Just "13:37:00"
Each field has to be exactly two digits, the fraction at least one, and the
whole string has to be the time. All of these are Nothing:
Time.fromString "1:37:00" --> Nothing
Time.fromString "13:37:00." --> Nothing
Time.fromString "133700" --> Nothing
Time.fromString "13:37:00Z" --> Nothing
Write an HH:MM:SS time, with .fff after it when there is a fraction.
Time.fromString "13:37" |> Maybe.map Time.toString
--> Just "13:37:00"
The seconds are always written, even when the value was read from text that had none. The fraction is written without its trailing zeroes.
Write an HH:MM:SS time with the fraction padded or truncated to a fixed
number of places.
Time.fromString "13:37" |> Maybe.map (Time.toStringWithPlaces 3)
--> Just "13:37:00.000"
Time.fromString "10:32:00.5" |> Maybe.map (Time.toStringWithPlaces 3)
--> Just "10:32:00.500"
With zero places, no decimal point is written. Use this when the receiver
requires a fixed number of places, such as exactly three for milliseconds.
Otherwise use toString.