///|
/// The result of resolving a local (wall-clock) reading against a time
/// zone: it can map to a single result, to two results (the "fold" at a
/// backward DST transition, where the same wall-clock reading occurs
/// twice), or to no result at all (the "gap" at a forward DST transition,
/// where that wall-clock reading never occurs). `T` is usually a
/// `FixedOffset` (see `TimeZone::offset_from_local`) or a `DateTime[Tz]`
/// (see `DateTime::from_local`).
///
/// `Absent` also reports components that do not form a valid reading at all
/// (for example month 13) when a `MappedLocalTime` is built from
/// components, as `DateTime::from_ymd_hms` and `DateTime::with_*` do; a
/// caller that must tell the two apart validates the components first with
/// `NaiveDate::from_ymd` and `NaiveTime::from_hms`.
pub(all) enum MappedLocalTime[T] {
Single(T)
Ambiguous(T, T)
Absent
} derive(Eq, @debug.Debug)
///|
pub extend MappedLocalTime with Eq::{equal}
///|
pub extend MappedLocalTime with Eq::{not_equal}
///|
pub extend MappedLocalTime with @debug.Debug::{to_repr}
///|
/// The value, only when `self` is unambiguous (`Single`); `None` for
/// `Ambiguous` or `Absent`.
pub fn[T] MappedLocalTime::single(self : MappedLocalTime[T]) -> T? {
match self {
Single(value) => Some(value)
Ambiguous(_, _) | Absent => None
}
}
///|
/// The value of an unambiguous (`Single`) local time.
///
/// Aborts, naming the reason, on `Ambiguous` (the reading occurs twice) or
/// `Absent` (the reading never occurs); use `single`, `earliest` or `latest`
/// to handle those cases instead.
pub fn[T] MappedLocalTime::unwrap(self : MappedLocalTime[T]) -> T {
match self {
Single(value) => value
Ambiguous(_, _) =>
abort("MappedLocalTime::unwrap: the local time is ambiguous")
Absent => abort("MappedLocalTime::unwrap: the local time does not exist")
}
}
///|
/// The earliest value `self` could resolve to: the sole value for
/// `Single`, the first (earlier-UTC-instant) value of an `Ambiguous`
/// fold, or `None` for `Absent`.
pub fn[T] MappedLocalTime::earliest(self : MappedLocalTime[T]) -> T? {
match self {
Single(value) => Some(value)
Ambiguous(earliest, _) => Some(earliest)
Absent => None
}
}
///|
/// The latest value `self` could resolve to: the sole value for
/// `Single`, the second (later-UTC-instant) value of an `Ambiguous`
/// fold, or `None` for `Absent`.
pub fn[T] MappedLocalTime::latest(self : MappedLocalTime[T]) -> T? {
match self {
Single(value) => Some(value)
Ambiguous(_, latest) => Some(latest)
Absent => None
}
}
///|
/// Applies `f` to every value carried by `self`, preserving its shape
/// (`Single`/`Ambiguous`/`Absent`).
pub fn[T, U] MappedLocalTime::map(
self : MappedLocalTime[T],
f : (T) -> U,
) -> MappedLocalTime[U] {
match self {
Single(value) => Single(f(value))
Ambiguous(earliest, latest) => Ambiguous(f(earliest), f(latest))
Absent => Absent
}
}
///|
/// Resolves whether `local_ts` (a local wall-clock reading, as a naive
/// Unix instant) falls in the gap or fold around a single transition from
/// `offset_before` to `offset_after` at `transition_utc`. Returns `None`
/// if this transition is not relevant to `local_ts` (a normal single
/// resolution should be used instead).
fn resolve_transition(
local_ts : Int64,
transition_utc : Int64,
offset_before : Int,
offset_after : Int,
) -> MappedLocalTime[FixedOffset]? {
let local_start = transition_utc + offset_before.to_int64()
let local_end = transition_utc + offset_after.to_int64()
if offset_after > offset_before {
if local_ts >= local_start && local_ts < local_end {
Some(Absent)
} else {
None
}
} else if offset_after < offset_before {
if local_ts >= local_end && local_ts < local_start {
Some(
Ambiguous(
FixedOffset::east(offset_before).unwrap(),
FixedOffset::east(offset_after).unwrap(),
),
)
} else {
None
}
} else {
None
}
}
///|
/// A time zone: given a naive datetime, resolves it to a concrete UTC
/// offset.
///
/// The trait is readonly: code outside this package can use it as a bound
/// but cannot implement it, so the zones are the ones this package provides
/// (`Utc`, `FixedOffset`, `FixedZone`, `Location`, `PosixTz` and, on
/// native, `Local`).
///
/// Every `NaiveDateTime` argument is a UTC reading except the one taken by
/// `offset_from_local`, which is a local (wall-clock) reading; the type does
/// not distinguish them, so pass `DateTime::naive_utc` for the former and
/// `DateTime::naive_local` for the latter.
pub trait TimeZone {
/// The offset in effect for the given UTC naive datetime. Always a
/// single answer: UTC has no daylight saving, so this mapping is never
/// ambiguous or absent.
fn offset_from_utc(Self, @core.NaiveDateTime) -> FixedOffset
/// The offset(s) in effect for the given local (wall-clock) naive
/// datetime. See `MappedLocalTime`.
fn offset_from_local(Self, @core.NaiveDateTime) -> MappedLocalTime[
FixedOffset,
]
/// The name in effect for the given UTC naive datetime: an IANA zone's
/// abbreviation (e.g. `"EDT"`), `Utc`'s fixed `"UTC"`, or a bare
/// `FixedOffset`'s own offset string (e.g. `"+09:00"`), since a fixed
/// offset carries no name distinct from its offset.
fn zone_name(Self, @core.NaiveDateTime) -> String
/// Whether daylight saving time is in effect at the given UTC naive
/// datetime. `false` by default: a zone with no daylight saving (`Utc`,
/// `FixedOffset`) never observes it.
fn is_dst(Self, @core.NaiveDateTime) -> Bool = _
/// The validity window of the offset in effect at the given UTC naive
/// datetime. Unbounded on both sides by default: a zone with a single
/// fixed offset (`Utc`, `FixedOffset`) never changes it.
fn transition_bounds(Self, @core.NaiveDateTime) -> TransitionBounds = _
/// The offset a zone abbreviation (e.g. `"EST"`) denotes in this zone,
/// resolved against the given UTC naive datetime, or `None` if the zone
/// does not know it. `None` by default: `Utc` and `FixedOffset` carry no
/// abbreviation of their own to look up.
fn offset_from_abbreviation(Self, String, @core.NaiveDateTime) -> FixedOffset? = _
}
///|
impl TimeZone with fn is_dst(_self, _utc) {
false
}
///|
impl TimeZone with fn offset_from_abbreviation(_self, _abbreviation, _near) {
None
}
///|
impl TimeZone with fn transition_bounds(_self, _utc) {
TransitionBounds::{ start: None, end: None, }
}