Struct AmbiguousTimestamp

pub struct AmbiguousTimestamp { /* private fields */ }

A possibly ambiguous Timestamp.

While this is called an ambiguous timestamp, the thing that is actually ambiguous is the offset. That is, an ambiguous timestamp is actually a pair of a civil::DateTime and an AmbiguousOffset.

Implementations

impl AmbiguousTimestamp

const fn datetime(&self) -> DateTime

Returns the civil datetime that was used to create this ambiguous timestamp.

Example

use jiff_core::{civil::date, tz::posix};

let tz = posix::TimeZone::parse("EST5EDT,M3.2.0,M11.1.0").unwrap();
let dt = date(2024, 7, 10).at(17, 15, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert_eq!(ts.datetime(), dt);

# Ok::<(), Box<dyn std::error::Error>>(())
const fn offset(&self) -> AmbiguousOffset

Returns the possibly ambiguous offset that is the ultimate source of ambiguity.

Most civil datetimes are not ambiguous, and thus, the offset will not be ambiguous either. In this case, the offset returned will be the AmbiguousOffset::Unambiguous variant.

But, not all civil datetimes are unambiguous. There are exactly two cases where a civil datetime can be ambiguous: when a civil datetime does not exist (a gap) or when a civil datetime is repeated (a fold). In both such cases, the offset is the thing that is ambiguous as there are two possible choices for the offset in both cases: the offset before the transition (whether it's a gap or a fold) or the offset after the transition.

This type captures the fact that computing an offset from a civil datetime in a particular time zone is in one of three possible states:

  1. It is unambiguous.
  2. It is ambiguous because there is a gap in time.
  3. It is ambiguous because there is a fold in time.

Example

use jiff_core::{civil::date, tz::{self, posix, AmbiguousOffset}};

let tz = posix::TimeZone::parse("EST5EDT,M3.2.0,M11.1.0").unwrap();

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert_eq!(ts.offset(), AmbiguousOffset::Unambiguous {
    offset: tz::offset(-4),
});

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert_eq!(ts.offset(), AmbiguousOffset::Gap {
    before: tz::offset(-5),
    after: tz::offset(-4),
});

// Ambiguous because of a fold.
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert_eq!(ts.offset(), AmbiguousOffset::Fold {
    before: tz::offset(-4),
    after: tz::offset(-5),
});

# Ok::<(), Box<dyn std::error::Error>>(())
const fn is_ambiguous(&self) -> bool

Returns true if and only if this possibly ambiguous timestamp is actually ambiguous.

This occurs precisely in cases when the offset is not AmbiguousOffset::Unambiguous.

Example

use jiff_core::{civil::date, tz::posix};

let tz = posix::TimeZone::parse("EST5EDT,M3.2.0,M11.1.0").unwrap();

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert!(!ts.is_ambiguous());

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert!(ts.is_ambiguous());

// Ambiguous because of a fold.
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert!(ts.is_ambiguous());

# Ok::<(), Box<dyn std::error::Error>>(())
const fn compatible(self) -> Result<Timestamp, RangeError>

Disambiguates this timestamp according to the "compatible" strategy.

If this timestamp is unambiguous, then this is a no-op.

The "compatible" strategy selects the offset corresponding to the civil time after a gap, and the offset corresponding to the civil time before a fold. This is what is specified in RFC 5545.

Errors

This returns an error when the combination of the civil datetime and offset would lead to a Timestamp outside of the Timestamp::MIN and Timestamp::MAX limits. This only occurs when the civil datetime is "close" to its own DateTime::MIN and DateTime::MAX limits.

const fn earlier(self) -> Result<Timestamp, RangeError>

Disambiguates this timestamp according to the "earlier" strategy.

If this timestamp is unambiguous, then this is a no-op.

The "earlier" strategy selects the offset corresponding to the civil time before a gap, and the offset corresponding to the civil time before a fold.

Errors

This returns an error when the combination of the civil datetime and offset would lead to a Timestamp outside of the Timestamp::MIN and Timestamp::MAX limits. This only occurs when the civil datetime is "close" to its own DateTime::MIN and DateTime::MAX limits.

const fn later(self) -> Result<Timestamp, RangeError>

Disambiguates this timestamp according to the "later" strategy.

If this timestamp is unambiguous, then this is a no-op.

The "later" strategy selects the offset corresponding to the civil time after a gap, and the offset corresponding to the civil time after a fold.

Errors

This returns an error when the combination of the civil datetime and offset would lead to a Timestamp outside of the Timestamp::MIN and Timestamp::MAX limits. This only occurs when the civil datetime is "close" to its own DateTime::MIN and DateTime::MAX limits.

const fn unambiguous(self) -> Result<Timestamp, AmbiguousError>

Disambiguates this timestamp according to the "reject" strategy.

If this timestamp is unambiguous, then this is a no-op.

The "reject" strategy always returns an error when the timestamp is ambiguous.

Errors

This returns an error when the combination of the civil datetime and offset would lead to a Timestamp outside of the Timestamp::MIN and Timestamp::MAX limits. This only occurs when the civil datetime is "close" to its own DateTime::MIN and DateTime::MAX limits.

This also returns an error when the timestamp is ambiguous.

Example

use jiff_core::{civil::date, tz::{posix, Offset}};

let tz = posix::TimeZone::parse("EST5EDT,M3.2.0,M11.1.0").unwrap();

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert_eq!(
    ts.later().unwrap().to_datetime(Offset::UTC),
    date(2024, 7, 15).at(21, 30, 0, 0),
);

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert!(ts.unambiguous().is_err());

// Ambiguous because of a fold.
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let ts = tz.to_ambiguous_timestamp(dt);
assert!(ts.unambiguous().is_err());

Trait Implementations

impl Clone for AmbiguousTimestamp

fn clone(&self) -> AmbiguousTimestamp

impl Copy for AmbiguousTimestamp

impl Debug for AmbiguousTimestamp

fn fmt(&self, f: &mut Formatter<'_>) -> Result

impl Eq for AmbiguousTimestamp

impl PartialEq for AmbiguousTimestamp

fn eq(&self, other: &AmbiguousTimestamp) -> bool

impl StructuralPartialEq for AmbiguousTimestamp

Auto Trait Implementations

impl Freeze for AmbiguousTimestamp

impl RefUnwindSafe for AmbiguousTimestamp

impl Send for AmbiguousTimestamp

impl Sync for AmbiguousTimestamp

impl Unpin for AmbiguousTimestamp

impl UnsafeUnpin for AmbiguousTimestamp

impl UnwindSafe for AmbiguousTimestamp

Blanket Implementations

impl<T> Any for AmbiguousTimestamp where T: 'static + ?Sized,

fn type_id(&self) -> TypeId

impl<T> Borrow<T> for AmbiguousTimestamp where T: ?Sized,

fn borrow(&self) -> &T

impl<T> BorrowMut<T> for AmbiguousTimestamp where T: ?Sized,

fn borrow_mut(&mut self) -> &mut T

impl<T> CloneToUninit for AmbiguousTimestamp where T: Clone,

unsafe fn clone_to_uninit(&self, dest: *mut u8)

impl<T> From<T> for AmbiguousTimestamp

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> ToOwned for AmbiguousTimestamp where T: Clone,

type Owned = T;
fn to_owned(&self) -> T
fn clone_into(&self, target: &mut T)

impl<T, U> Into<U> for AmbiguousTimestamp where U: From<T>,

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

impl<T, U> TryFrom<U> for AmbiguousTimestamp where U: Into<T>,

type Error = Infallible;
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

impl<T, U> TryInto<U> for AmbiguousTimestamp where U: TryFrom<T>,

type Error = <U as TryFrom<T>>::Error;
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>