Struct AmbiguousZoned

struct AmbiguousZoned { ... }

A possibly ambiguous Zoned, created by TimeZone::to_ambiguous_zoned.

While this is called an ambiguous zoned datetime, the thing that is actually ambiguous is the offset. That is, an ambiguous zoned datetime is actually a triple of a civil::DateTime, a TimeZone and an AmbiguousOffset.

When the offset is ambiguous, it either represents a gap (civil time is skipped) or a fold (civil time is repeated). In both cases, there are, by construction, two different offsets to choose from: the offset from before the transition and the offset from after the transition.

The purpose of this type is to represent that ambiguity (when it occurs) and enable callers to make a choice about how to resolve that ambiguity. In some cases, you might want to reject ambiguity altogether, which is supported by the AmbiguousZoned::unambiguous routine.

This type provides four different out-of-the-box disambiguation strategies:

The AmbiguousZoned::disambiguate method can be used with the Disambiguation enum when the disambiguation strategy isn't known until runtime.

Note also that these aren't the only disambiguation strategies. The AmbiguousOffset type, accessible via AmbiguousZoned::offset, exposes the full details of the ambiguity. So any strategy can be implemented.

Example

This example shows how the "compatible" disambiguation strategy is implemented. Recall that the "compatible" strategy chooses the offset corresponding to the civil datetime after a gap, and the offset corresponding to the civil datetime before a gap.

use jiff::{civil::date, tz::{self, AmbiguousOffset}};

let tz = tz::db().get("America/New_York")?;
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let ambiguous = tz.to_ambiguous_zoned(dt);
let offset = match ambiguous.offset() {
    AmbiguousOffset::Unambiguous { offset } => offset,
    // This is counter-intuitive, but in order to get the civil datetime
    // *after* the gap, we need to select the offset from *before* the
    // gap.
    AmbiguousOffset::Gap { before, .. } => before,
    AmbiguousOffset::Fold { before, .. } => before,
};
let zdt = offset.to_timestamp(dt)?.to_zoned(ambiguous.into_time_zone());
assert_eq!(zdt.to_string(), "2024-03-10T03:30:00-04:00[America/New_York]");

# Ok::<(), Box<dyn std::error::Error>>(())

Implementations

impl AmbiguousZoned

fn time_zone(self: &Self) -> &TimeZone

Returns a reference to the time zone that was used to create this ambiguous zoned datetime.

Example

use jiff::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;
let dt = date(2024, 7, 10).at(17, 15, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(&tz, zdt.time_zone());

# Ok::<(), Box<dyn std::error::Error>>(())
fn into_time_zone(self: Self) -> TimeZone

Consumes this ambiguous zoned datetime and returns the underlying TimeZone. This is useful if you no longer need the ambiguous zoned datetime and want its TimeZone without cloning it. (Cloning a TimeZone is cheap but not free.)

Example

use jiff::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;
let dt = date(2024, 7, 10).at(17, 15, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(tz, zdt.into_time_zone());

# Ok::<(), Box<dyn std::error::Error>>(())
fn datetime(self: &Self) -> DateTime

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

Example

use jiff::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;
let dt = date(2024, 7, 10).at(17, 15, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(zdt.datetime(), dt);

# Ok::<(), Box<dyn std::error::Error>>(())
fn offset(self: &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::{civil::date, tz::{self, AmbiguousOffset}};

let tz = tz::db().get("America/New_York")?;

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

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(zdt.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 zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(zdt.offset(), AmbiguousOffset::Fold {
    before: tz::offset(-4),
    after: tz::offset(-5),
});

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

Returns true if and only if this possibly ambiguous zoned datetime is actually ambiguous.

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

Example

use jiff::{civil::date, tz::{self, AmbiguousOffset}};

let tz = tz::db().get("America/New_York")?;

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

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

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

# Ok::<(), Box<dyn std::error::Error>>(())
fn compatible(self: Self) -> Result<Zoned, Error>

Disambiguates this zoned datetime according to the Disambiguation::Compatible strategy.

If this zoned datetime 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 Zoned with 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.

Example

use jiff::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.compatible()?.to_string(),
    "2024-07-15T17:30:00-04:00[America/New_York]",
);

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.compatible()?.to_string(),
    "2024-03-10T03:30:00-04:00[America/New_York]",
);

// Ambiguous because of a fold.
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.compatible()?.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn earlier(self: Self) -> Result<Zoned, Error>

Disambiguates this zoned datetime according to the Disambiguation::Earlier strategy.

If this zoned datetime 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 Zoned with 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.

Example

use jiff::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.earlier()?.to_string(),
    "2024-07-15T17:30:00-04:00[America/New_York]",
);

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.earlier()?.to_string(),
    "2024-03-10T01:30:00-05:00[America/New_York]",
);

// Ambiguous because of a fold.
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.earlier()?.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn later(self: Self) -> Result<Zoned, Error>

Disambiguates this zoned datetime according to the Disambiguation::Later strategy.

If this zoned datetime 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 Zoned with 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.

Example

use jiff::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.later()?.to_string(),
    "2024-07-15T17:30:00-04:00[America/New_York]",
);

// Ambiguous because of a gap.
let dt = date(2024, 3, 10).at(2, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.later()?.to_string(),
    "2024-03-10T03:30:00-04:00[America/New_York]",
);

// Ambiguous because of a fold.
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.later()?.to_string(),
    "2024-11-03T01:30:00-05:00[America/New_York]",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn unambiguous(self: Self) -> Result<Zoned, Error>

Disambiguates this zoned datetime according to the Disambiguation::Reject strategy.

If this zoned datetime is unambiguous, then this is a no-op.

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

Errors

This returns an error when the combination of the civil datetime and offset would lead to a Zoned with 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::{civil::date, tz};

let tz = tz::db().get("America/New_York")?;

// Not ambiguous.
let dt = date(2024, 7, 15).at(17, 30, 0, 0);
let zdt = tz.to_ambiguous_zoned(dt);
assert_eq!(
    zdt.later()?.to_string(),
    "2024-07-15T17:30:00-04:00[America/New_York]",
);

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

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

# Ok::<(), Box<dyn std::error::Error>>(())
fn disambiguate(self: Self, option: Disambiguation) -> Result<Zoned, Error>

Disambiguates this (possibly ambiguous) timestamp into a concrete time zone aware timestamp.

This is the same as calling one of the disambiguation methods, but the method chosen is indicated by the option given. This is useful when the disambiguation option needs to be chosen at runtime.

Errors

This returns an error if this would have returned a zoned datetime outside of its minimum and maximum values.

This can also return an error when using the Disambiguation::Reject strategy. Namely, when using the Reject strategy, any ambiguous timestamp always results in an error.

Example

This example shows the various disambiguation modes when given a datetime that falls in a "fold" (i.e., a backwards DST transition).

use jiff::{civil::date, tz::{self, Disambiguation}};

let newyork = tz::db().get("America/New_York")?;
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
let ambiguous = newyork.to_ambiguous_zoned(dt);

// In compatible mode, backward transitions select the earlier
// time. In the EDT->EST transition, that's the -04 (EDT) offset.
let zdt = ambiguous.clone().disambiguate(Disambiguation::Compatible)?;
assert_eq!(
    zdt.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);

// The earlier time in the EDT->EST transition is the -04 (EDT) offset.
let zdt = ambiguous.clone().disambiguate(Disambiguation::Earlier)?;
assert_eq!(
    zdt.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);

// The later time in the EDT->EST transition is the -05 (EST) offset.
let zdt = ambiguous.clone().disambiguate(Disambiguation::Later)?;
assert_eq!(
    zdt.to_string(),
    "2024-11-03T01:30:00-05:00[America/New_York]",
);

// Since our datetime is ambiguous, the 'reject' strategy errors.
assert!(ambiguous.disambiguate(Disambiguation::Reject).is_err());

# Ok::<(), Box<dyn std::error::Error>>(())

impl Clone for AmbiguousZoned

fn clone(self: &Self) -> AmbiguousZoned

impl Debug for AmbiguousZoned

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

impl Eq for AmbiguousZoned

impl Freeze for AmbiguousZoned

impl PartialEq for AmbiguousZoned

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

impl RefUnwindSafe for AmbiguousZoned

impl Send for AmbiguousZoned

impl StructuralPartialEq for AmbiguousZoned

impl Sync for AmbiguousZoned

impl Unpin for AmbiguousZoned

impl UnwindSafe for AmbiguousZoned

impl<T> Any for AmbiguousZoned

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for AmbiguousZoned

fn borrow(self: &Self) -> &T

impl<T> BorrowMut for AmbiguousZoned

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

impl<T> CloneToUninit for AmbiguousZoned

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

impl<T> From for AmbiguousZoned

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> ToOwned for AmbiguousZoned

fn to_owned(self: &Self) -> T
fn clone_into(self: &Self, target: &mut T)

impl<T, U> Into for AmbiguousZoned

fn into(self: 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 for AmbiguousZoned

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

impl<T, U> TryInto for AmbiguousZoned

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