Skip to content
heff

std.offsetdatetime

A date and time of day at an offset from UTC: 2024-02-29T15:04:05+08:00, what a clock on the wall said where the clock was 8 hours ahead of UTC — an instant (std.instant), and how it was read there. NodaTime’s OffsetDateTime is the model: a std.localdatetime and a std.offset. Two are equal where both their date-times and their offsets are, so 15:04:05+08:00 and 07:04:05Z, the same instant, are not equal; and they have no order, as by their instants or by their date-times would each be a surprise — compare their instants. RFC 3339 writes one, and reads it (show, parse). Import it by a name, import std.offsetdatetime as OffsetDateTime, as its names are short.

import std.instant as Instant
import std.offset as Offset
import std.offsetdatetime as OffsetDateTime
-- | the instant in Taipei, as RFC 3339 writes it
-- | >>> inTaipei Instant.epoch
-- | "1970-01-01T08:00:00+08:00"
inTaipei : Instant.Instant -> String
inTaipei t =
maybe "" (\o -> show (OffsetDateTime.at t o)) (Offset.fromHours 8)

OffsetDateTime

data OffsetDateTime

A date-time and its offset: the date and time of day a clock on the wall says, and how far ahead of UTC the clock is.

make

make : LocalDateTime.LocalDateTime -> Offset.Offset -> OffsetDateTime

The date-time at the offset.

local

local : OffsetDateTime -> LocalDateTime.LocalDateTime

Its date-time, as the clock at its offset tells it.

offset

offset : OffsetDateTime -> Offset.Offset

Its offset.

at

at : Instant.Instant -> Offset.Offset -> OffsetDateTime

The instant as a clock at the offset tells it.

>>> List.map (\h -> maybe "" (\o -> show (local (at Instant.epoch o))) (Offset.fromHours h)) [0, 8, -5]
["1970-01-01T00:00:00", "1970-01-01T08:00:00", "1969-12-31T19:00:00"]

instant

instant : OffsetDateTime -> Instant.Instant

The instant it is: its date-time less its offset.

>>> maybe "" (\o -> show (instant (at (Instant.fromUnixNanoseconds 1709219045500000000) o))) (Offset.fromHoursAndMinutes (-9) 30)
"2024-02-29T15:04:05.5Z"

instanceEqualityOffsetDateTime

instance instanceEqualityOffsetDateTime : Equality OffsetDateTime

The same date-time at the same offset, as NodaTime has it: the same instant at two offsets is two values.

>>> maybe False (\o -> at Instant.epoch o == at Instant.epoch o) (Offset.fromHours 8)
True
>>> maybe True (\o -> at Instant.epoch o == at Instant.epoch Offset.utc) (Offset.fromHours 8)
False

instanceDebugOffsetDateTime

instance instanceDebugOffsetDateTime : Debug OffsetDateTime

As show writes it, 2024-02-29T15:04:05+08:00, as NodaTime’s debug form is.

>>> debug (at Instant.epoch Offset.utc)
"1970-01-01T00:00:00Z"

instanceShowOffsetDateTime

instance instanceShowOffsetDateTime : Show OffsetDateTime

As RFC 3339 writes it, as Go’s RFC3339Nano does: the date-time as std.localdatetime writes it — a fraction of a second only where there is one — and the offset as std.offset does, Z for UTC (2024-02-29T15:04:05.5+08:00). An offset that is not whole minutes is written with its seconds, +05:30:15, which RFC 3339 has not, so as to be exact; and a year out of 0000 to 9999 as std.localdate writes it.

>>> List.map (\n -> maybe "" (\o -> show (at (Instant.fromUnixNanoseconds 1709219045500000000) o)) (Offset.fromSeconds n)) [0, 28800, -34200, 19815]
["2024-02-29T15:04:05.5Z", "2024-02-29T23:04:05.5+08:00", "2024-02-29T05:34:05.5-09:30", "2024-02-29T20:34:20.5+05:30:15"]

parse

parse : String -> Maybe OffsetDateTime

The date-time RFC 3339 writes as s, as std.date’s parseRFC3339 reads it: 2006-01-02T15:04:05Z, the T and the Z in either case, perhaps a fraction of a second of 1 to 9 digits after the seconds, and an offset Z or +hh:mm or -hh:mm. Nothing if it writes none, or a field is out of its range (the month 13, February 30, the hour 24, the second 60), or the offset is beyond 18 hours (+19:00), as an offset here is.

>>> List.map (\s -> maybe "" show (parse s)) ["1985-04-12T23:20:50.52Z", "1996-12-19t16:39:57-08:00", "2024-02-29T23:59:59.000000001z", "0000-01-01T00:00:00+18:00", "9999-12-31T00:00:00.100Z"]
["1985-04-12T23:20:50.52Z", "1996-12-19T16:39:57-08:00", "2024-02-29T23:59:59.000000001Z", "0000-01-01T00:00:00+18:00", "9999-12-31T00:00:00.1Z"]
>>> maybe "" (\t -> show (instant t)) (parse "2023-11-15T06:13:20.123456789+08:00")
"2023-11-14T22:13:20.123456789Z"
>>> (List.map (\s -> maybe True (\_ -> False) (parse s)) ["2006-13-02T15:04:05Z", "2023-02-29T00:00:00Z", "2006-01-02T24:00:00Z", "2006-01-02T15:04:60Z", "2006-01-02T15:04:05.Z", "2006-01-02T15:04:05.1234567890Z", "2006-01-02T15:04:05+08:60", "2006-01-02T15:04:05+19:00", "2006-01-02 15:04:05Z", "2006-01-02T15:04:05", "2006-01-02T15:04:05+0800", "-006-01-02T15:04:05Z", "2006-01-02T15:04:05ZZ"] : List Bool)
[True, True, True, True, True, True, True, True, True, True, True, True, True]
>>> List.all (\s -> maybe False (\t -> parse (show t) == Just t) (parse s)) ["2024-02-29T15:04:05.5+08:00", "1969-12-31T23:59:59.999999999-09:30", "0000-01-01T00:00:00Z"]
True