Struct TimeZone

struct TimeZone { ... }

A representation of a time zone.

A time zone is a set of rules for determining the civil time, via an offset from UTC, in a particular geographic region. In many cases, the offset in a particular time zone can vary over the course of a year through transitions into and out of daylight saving time.

A TimeZone can be one of three possible representations:

The most practical and useful representation is an IANA time zone. Namely, it enjoys broad support and its database is regularly updated to reflect real changes in time zone rules throughout the world. On Unix systems, the time zone database is typically found at /usr/share/zoneinfo. For more information on how Jiff interacts with The Time Zone Database, see TimeZoneDatabase.

In typical usage, users of Jiff shouldn't need to reference a TimeZone directly. Instead, there are convenience APIs on datetime types that accept IANA time zone identifiers and do automatic database lookups for you. For example, to convert a timestamp to a zone aware datetime:

use jiff::Timestamp;

let ts = Timestamp::from_second(1_456_789_123)?;
let zdt = ts.in_tz("America/New_York")?;
assert_eq!(zdt.to_string(), "2016-02-29T18:38:43-05:00[America/New_York]");

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

Or to convert a civil datetime to a zoned datetime corresponding to a precise instant in time:

use jiff::civil::date;

let dt = date(2024, 7, 15).at(21, 27, 0, 0);
let zdt = dt.in_tz("America/New_York")?;
assert_eq!(zdt.to_string(), "2024-07-15T21:27:00-04:00[America/New_York]");

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

Or even converted a zoned datetime from one time zone to another:

use jiff::civil::date;

let dt = date(2024, 7, 15).at(21, 27, 0, 0);
let zdt1 = dt.in_tz("America/New_York")?;
let zdt2 = zdt1.in_tz("Israel")?;
assert_eq!(zdt2.to_string(), "2024-07-16T04:27:00+03:00[Israel]");

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

The system time zone

The system time zone can be retrieved via TimeZone::system. If it couldn't be detected or if the tz-system crate feature is not enabled, then TimeZone::UTC is returned. TimeZone::system is what's used internally for retrieving the current zoned datetime via Zoned::now.

While there is no platform independent way to detect your system's "default" time zone, Jiff employs best-effort heuristics to determine it. (For example, by examining /etc/localtime on Unix systems.) When the heuristics fail, Jiff will emit a WARN level log. It can be viewed by installing a log compatible logger, such as env_logger.

Custom time zones

At present, Jiff doesn't provide any APIs for manually constructing a custom time zone. However, TimeZone::tzif is provided for reading any valid TZif formatted data, as specified by RFC 8536. This provides an interoperable way of utilizing custom time zone rules.

A TimeZone is immutable

Once a TimeZone is created, it is immutable. That is, its underlying time zone transition rules will never change. This is true for system time zones or even if the IANA Time Zone Database it was loaded from changes on disk. The only way such changes can be observed is by re-requesting the TimeZone from a TimeZoneDatabase. (Or, in the case of the system time zone, by calling TimeZone::system.)

A TimeZone is cheap to clone

A TimeZone can be cheaply cloned. It uses automic reference counting internally. When alloc is disabled, cloning a TimeZone is still cheap because POSIX time zones and TZif time zones are unsupported. Therefore, cloning a time zone does a deep copy (since automic reference counting is not available), but the data being copied is small.

Time zone equality

TimeZone provides an imperfect notion of equality. That is, when two time zones are equal, then it is guaranteed for them to have the same rules. However, two time zones may compare unequal and yet still have the same rules.

The equality semantics are as follows:

Time zone equality is, for example, used in APIs like Zoned::since when asking for spans with calendar units. Namely, since days can be of different lengths in different time zones, Zoned::since will return an error when the two zoned datetimes are in different time zones and when the caller requests units greater than hours.

Dealing with ambiguity

The principal job of a TimeZone is to provide two different transformations:

The timestamp-to-civil time conversion is done via TimeZone::to_datetime, or its lower level counterpart, TimeZone::to_offset. The civil time-to-timestamp conversion is done via one of the following routines:

Here is an example where we explore the different disambiguation strategies for a fold in time, where in this case, the 1 o'clock hour is repeated:

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

let tz = TimeZone::get("America/New_York")?;
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
// It's ambiguous, so asking for an unambiguous instant presents an error!
assert!(tz.to_ambiguous_zoned(dt).unambiguous().is_err());
// Gives you the earlier time in a fold, i.e., before DST ends:
assert_eq!(
    tz.to_ambiguous_zoned(dt).earlier()?.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);
// Gives you the later time in a fold, i.e., after DST ends.
// Notice the offset change from the previous example!
assert_eq!(
    tz.to_ambiguous_zoned(dt).later()?.to_string(),
    "2024-11-03T01:30:00-05:00[America/New_York]",
);
// "Just give me something reasonable"
assert_eq!(
    tz.to_ambiguous_zoned(dt).compatible()?.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);

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

Serde integration

At present, a TimeZone does not implement Serde's Serialize or Deserialize traits directly. Nor does it implement std::fmt::Display or std::str::FromStr. The reason for this is that it's not totally clear if there is one single obvious behavior. Moreover, some TimeZone values do not have an obvious succinct serialized representation. (For example, when /etc/localtime on a Unix system is your system's time zone, and it isn't a symlink to a TZif file in /usr/share/zoneinfo. In which case, an IANA time zone identifier cannot easily be deduced by Jiff.)

Instead, Jiff offers helpers for use with Serde's with attribute via the fmt::serde module:

use jiff::tz::TimeZone;

#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct Record {
    #[serde(with = "jiff::fmt::serde::tz::optional")]
    tz: Option<TimeZone>,
}

let json = r#"{"tz":"America/Nuuk"}"#;
let got: Record = serde_json::from_str(&json)?;
assert_eq!(got.tz, Some(TimeZone::get("America/Nuuk")?));
assert_eq!(serde_json::to_string(&got)?, json);

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

Alternatively, you may use the fmt::temporal::DateTimeParser::parse_time_zone or fmt::temporal::DateTimePrinter::print_time_zone routines to parse or print TimeZone values without using Serde.

Implementations

impl TimeZone

fn system() -> TimeZone

Returns the system configured time zone, if available.

Detection of a system's default time zone is generally heuristic based and platform specific.

If callers need to know whether discovery of the system time zone failed, then use TimeZone::try_system.

Fallback behavior

If the system's default time zone could not be determined, or if the tz-system crate feature is not enabled, then this returns TimeZone::unknown. A WARN level log will also be emitted with a message explaining why time zone detection failed. The fallback to an unknown time zone is a practical trade-off, is what most other systems tend to do and is also recommended by relevant standards such as freedesktop.org.

An unknown time zone behaves like TimeZone::UTC, but will print as Etc/Unknown when converting a Zoned to a string.

If you would instead like to fall back to UTC instead of the special "unknown" time zone, then you can do TimeZone::try_system().unwrap_or(TimeZone::UTC).

Platform behavior

This section is a "best effort" explanation of how the time zone is detected on supported platforms. The behavior is subject to change.

On all platforms, the TZ environment variable overrides any other heuristic, and provides a way for end users to set the time zone for specific use cases. In general, Jiff respects the POSIX TZ rules. Here are some examples:

  • TZ=America/New_York for setting a time zone via an IANA Time Zone Database Identifier.
  • TZ=/usr/share/zoneinfo/America/New_York for setting a time zone by providing a file path to a TZif file directly.
  • TZ=EST5EDT,M3.2.0,M11.1.0 for setting a time zone via a daylight saving time transition rule.

Otherwise, when TZ isn't set, then:

On Unix non-Android systems, this inspects /etc/localtime. If it's a symbolic link to an entry in /usr/share/zoneinfo, then the suffix is considered an IANA Time Zone Database identifier. Otherwise, /etc/localtime is read as a TZif file directly.

On Android systems, this inspects the persist.sys.timezone property.

On Windows, the system time zone is determined via GetDynamicTimeZoneInformation. The result is then mapped to an IANA Time Zone Database identifier via Unicode's CLDR XML data.

fn try_system() -> Result<TimeZone, Error>

Returns the system configured time zone, if available.

If the system's default time zone could not be determined, or if the tz-system crate feature is not enabled, then this returns an error.

Detection of a system's default time zone is generally heuristic based and platform specific.

Note that callers should generally prefer using TimeZone::system. If a system time zone could not be found, then it falls back to TimeZone::UTC automatically. This is often what is recommended by relevant standards such as freedesktop.org. Conversely, this routine is useful if detection of a system's default time zone is critical.

Platform behavior

This section is a "best effort" explanation of how the time zone is detected on supported platforms. The behavior is subject to change.

On all platforms, the TZ environment variable overrides any other heuristic, and provides a way for end users to set the time zone for specific use cases. In general, Jiff respects the POSIX TZ rules. Here are some examples:

  • TZ=America/New_York for setting a time zone via an IANA Time Zone Database Identifier.
  • TZ=/usr/share/zoneinfo/America/New_York for setting a time zone by providing a file path to a TZif file directly.
  • TZ=EST5EDT,M3.2.0,M11.1.0 for setting a time zone via a daylight saving time transition rule.

Otherwise, when TZ isn't set, then:

On Unix systems, this inspects /etc/localtime. If it's a symbolic link to an entry in /usr/share/zoneinfo, then the suffix is considered an IANA Time Zone Database identifier. Otherwise, /etc/localtime is read as a TZif file directly.

On Windows, the system time zone is determined via GetDynamicTimeZoneInformation. The result is then mapped to an IANA Time Zone Database identifier via Unicode's CLDR XML data.

fn get(time_zone_name: &str) -> Result<TimeZone, Error>

A convenience function for performing a time zone database lookup for the given time zone identifier. It uses the default global time zone database via tz::db().

Errors

This returns an error if the given time zone identifier could not be found in the default TimeZoneDatabase.

Example

use jiff::{tz::TimeZone, Timestamp};

let tz = TimeZone::get("Japan")?;
assert_eq!(
    tz.to_datetime(Timestamp::UNIX_EPOCH).to_string(),
    "1970-01-01T09:00:00",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn fixed(offset: Offset) -> TimeZone

Returns a time zone with a fixed offset.

A fixed offset will never have any transitions and won't follow any particular time zone rules. In general, one should avoid using fixed offset time zones unless you have a specific need for them. Otherwise, IANA time zones via TimeZone::get should be preferred, as they more accurately model the actual time zone transitions rules used in practice.

Example

use jiff::{tz::{self, TimeZone}, Timestamp};

let tz = TimeZone::fixed(tz::offset(10));
assert_eq!(
    tz.to_datetime(Timestamp::UNIX_EPOCH).to_string(),
    "1970-01-01T10:00:00",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn posix(posix_tz_string: &str) -> Result<TimeZone, Error>

Creates a time zone from a POSIX TZ rule string.

A POSIX time zone provides a way to tersely define a single daylight saving time transition rule (or none at all) that applies for all years.

Users should avoid using this kind of time zone unless there is a specific need for it. Namely, POSIX time zones cannot capture the full complexity of time zone transition rules in the real world. (See the example below.)

Errors

This returns an error if the given POSIX time zone string is invalid.

Example

This example demonstrates how a POSIX time zone may be historically inaccurate:

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

// The tzdb entry for America/New_York.
let iana = TimeZone::get("America/New_York")?;
// The POSIX TZ string for New York DST that went into effect in 2007.
let posix = TimeZone::posix("EST5EDT,M3.2.0,M11.1.0")?;

// New York entered DST on April 2, 2006 at 2am:
let dt = date(2006, 4, 2).at(2, 0, 0, 0);
// The IANA tzdb entry correctly reports it as ambiguous:
assert!(iana.to_ambiguous_timestamp(dt).is_ambiguous());
// But the POSIX time zone does not:
assert!(!posix.to_ambiguous_timestamp(dt).is_ambiguous());

# Ok::<(), Box<dyn std::error::Error>>(())
fn tzif(name: &str, data: &[u8]) -> Result<TimeZone, Error>

Creates a time zone from TZif binary data, whose format is specified in RFC 8536. All versions of TZif (up through version 4) are supported.

This constructor is typically not used, and instead, one should rely on time zone lookups via time zone identifiers with routines like TimeZone::get. However, this constructor does provide one way of using custom time zones with Jiff.

The name given should be a IANA time zone database identifier.

Errors

This returns an error if the given data was not recognized as valid TZif.

fn unknown() -> TimeZone

Returns a TimeZone that is specifially marked as "unknown."

This corresponds to the Unicode CLDR identifier Etc/Unknown, which is guaranteed to never be a valid IANA time zone identifier (as of the 2025a release of tzdb).

This type of TimeZone is used in circumstances where one wants to signal that discovering a time zone failed for some reason, but that execution can reasonably continue. For example, TimeZone::system returns this type of time zone when the system time zone could not be discovered.

Example

Jiff permits an "unknown" time zone to losslessly be transmitted through serialization:

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

let tz = TimeZone::unknown();
let zdt = date(2025, 2, 1).at(17, 0, 0, 0).to_zoned(tz)?;
assert_eq!(zdt.to_string(), "2025-02-01T17:00:00Z[Etc/Unknown]");
let got: Zoned = "2025-02-01T17:00:00Z[Etc/Unknown]".parse()?;
assert_eq!(got, zdt);

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

Note that not all systems support this. Some systems will reject Etc/Unknown because it is not a valid IANA time zone identifier and does not have an entry in the IANA time zone database. However, Jiff takes this approach because it surfaces an error condition in detecting the end user's time zone. Callers not wanting an "unknown" time zone can use TimeZone::try_system().unwrap_or(TimeZone::UTC) instead of TimeZone::system. (Where the latter falls back to the "unknown" time zone when a system configured time zone could not be found.)

fn iana_name(self: &Self) -> Option<&str>

When this time zone was loaded from an IANA time zone database entry, then this returns the canonicalized name for that time zone.

Example

use jiff::tz::TimeZone;

let tz = TimeZone::get("america/NEW_YORK")?;
assert_eq!(tz.iana_name(), Some("America/New_York"));

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

Returns true if and only if this time zone is unknown.

This has the special internal identifier of Etc/Unknown, and this is what will be used when converting a Zoned to a string.

Note that while Etc/Unknown looks like an IANA time zone identifier, it is specifically and explicitly not one. It is reserved and is guaranteed to never be an IANA time zone identifier.

An unknown time zone can be created via TimeZone::unknown. It is also returned by TimeZone::system when a system configured time zone could not be found.

Example

use jiff::tz::TimeZone;

let tz = TimeZone::unknown();
assert_eq!(tz.iana_name(), None);
assert!(tz.is_unknown());
fn to_datetime(self: &Self, timestamp: Timestamp) -> DateTime

Returns the civil datetime corresponding to the given timestamp in this time zone.

This operation is always unambiguous. That is, for any instant in time supported by Jiff (that is, a Timestamp), there is always precisely one civil datetime corresponding to that instant.

Note that this is considered a lower level routine. Consider working with zoned datetimes instead, and use Zoned::datetime to get its civil time if necessary.

Example

use jiff::{tz::TimeZone, Timestamp};

let tz = TimeZone::get("Europe/Rome")?;
assert_eq!(
    tz.to_datetime(Timestamp::UNIX_EPOCH).to_string(),
    "1970-01-01T01:00:00",
);

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

As mentioned above, consider using Zoned instead:

use jiff::{tz::TimeZone, Timestamp};

let zdt = Timestamp::UNIX_EPOCH.in_tz("Europe/Rome")?;
assert_eq!(zdt.datetime().to_string(), "1970-01-01T01:00:00");

# Ok::<(), Box<dyn std::error::Error>>(())
fn to_offset(self: &Self, _timestamp: Timestamp) -> Offset

Returns the offset corresponding to the given timestamp in this time zone.

This operation is always unambiguous. That is, for any instant in time supported by Jiff (that is, a Timestamp), there is always precisely one offset corresponding to that instant.

Given an offset, one can use APIs like Offset::to_datetime to create a civil datetime from a timestamp.

This also returns whether this timestamp is considered to be in "daylight saving time," as well as the abbreviation for the time zone at this time.

Example

use jiff::{tz::{self, Dst, TimeZone}, Timestamp};

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

// A timestamp in DST in New York.
let ts = Timestamp::from_second(1_720_493_204)?;
let offset = tz.to_offset(ts);
assert_eq!(offset, tz::offset(-4));
assert_eq!(offset.to_datetime(ts).to_string(), "2024-07-08T22:46:44");

// A timestamp *not* in DST in New York.
let ts = Timestamp::from_second(1_704_941_204)?;
let offset = tz.to_offset(ts);
assert_eq!(offset, tz::offset(-5));
assert_eq!(offset.to_datetime(ts).to_string(), "2024-01-10T21:46:44");

# Ok::<(), Box<dyn std::error::Error>>(())
fn to_offset_info<'t>(self: &'t Self, _timestamp: Timestamp) -> TimeZoneOffsetInfo<'t>

Returns the offset information corresponding to the given timestamp in this time zone. This includes the offset along with daylight saving time status and a time zone abbreviation.

This is like TimeZone::to_offset, but returns the aforementioned extra data in addition to the offset. This data may, in some cases, be more expensive to compute.

Example

use jiff::{tz::{self, Dst, TimeZone}, Timestamp};

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

// A timestamp in DST in New York.
let ts = Timestamp::from_second(1_720_493_204)?;
let info = tz.to_offset_info(ts);
assert_eq!(info.offset(), tz::offset(-4));
assert_eq!(info.dst(), Dst::Yes);
assert_eq!(info.abbreviation(), "EDT");
assert_eq!(
    info.offset().to_datetime(ts).to_string(),
    "2024-07-08T22:46:44",
);

// A timestamp *not* in DST in New York.
let ts = Timestamp::from_second(1_704_941_204)?;
let info = tz.to_offset_info(ts);
assert_eq!(info.offset(), tz::offset(-5));
assert_eq!(info.dst(), Dst::No);
assert_eq!(info.abbreviation(), "EST");
assert_eq!(
    info.offset().to_datetime(ts).to_string(),
    "2024-01-10T21:46:44",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn to_fixed_offset(self: &Self) -> Result<Offset, Error>

If this time zone is a fixed offset, then this returns the offset. If this time zone is not a fixed offset, then an error is returned.

If you just need an offset for a given timestamp, then you can use TimeZone::to_offset. Or, if you need an offset for a civil datetime, then you can use TimeZone::to_ambiguous_timestamp or TimeZone::to_ambiguous_zoned, although the result may be ambiguous.

Generally, this routine is useful when you need to know whether the time zone is fixed, and you want to get the offset without having to specify a timestamp. This is sometimes required for interoperating with other datetime systems that need to distinguish between time zones that are fixed and time zones that are based on rules such as those found in the IANA time zone database.

Example

use jiff::tz::{Offset, TimeZone};

let tz = TimeZone::get("America/New_York")?;
// A named time zone is not a fixed offset
// and so cannot be converted to an offset
// without a timestamp or civil datetime.
assert_eq!(
    tz.to_fixed_offset().unwrap_err().to_string(),
    "cannot convert non-fixed IANA time zone \
     to offset without timestamp or civil datetime",
);

let tz = TimeZone::UTC;
// UTC is a fixed offset and so can be converted
// without a timestamp.
assert_eq!(tz.to_fixed_offset()?, Offset::UTC);

// And of course, creating a time zone from a
// fixed offset results in a fixed offset time
// zone too:
let tz = TimeZone::fixed(jiff::tz::offset(-10));
assert_eq!(tz.to_fixed_offset()?, jiff::tz::offset(-10));

# Ok::<(), Box<dyn std::error::Error>>(())
fn to_zoned(self: &Self, dt: DateTime) -> Result<Zoned, Error>

Converts a civil datetime to a Zoned in this time zone.

The given civil datetime may be ambiguous in this time zone. A civil datetime is ambiguous when either of the following occurs:

  • When the civil datetime falls into a "gap." That is, when there is a jump forward in time where a span of time does not appear on the clocks in this time zone. This typically manifests as a 1 hour jump forward into daylight saving time.
  • When the civil datetime falls into a "fold." That is, when there is a jump backward in time where a span of time is repeated on the clocks in this time zone. This typically manifests as a 1 hour jump backward out of daylight saving time.

This routine automatically resolves both of the above ambiguities via the Disambiguation::Compatible strategy. That in, the case of a gap, the time after the gap is used. In the case of a fold, the first repetition of the clock time is used.

Example

This example shows how disambiguation works:

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

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

// This demonstrates disambiguation behavior for a gap.
let zdt = tz.to_zoned(date(2024, 3, 10).at(2, 30, 0, 0))?;
assert_eq!(zdt.to_string(), "2024-03-10T03:30:00-04:00[America/New_York]");
// This demonstrates disambiguation behavior for a fold.
// Notice the offset: the -04 corresponds to the time while
// still in DST. The second repetition of the 1 o'clock hour
// occurs outside of DST, in "standard" time, with the offset -5.
let zdt = tz.to_zoned(date(2024, 11, 3).at(1, 30, 0, 0))?;
assert_eq!(zdt.to_string(), "2024-11-03T01:30:00-04:00[America/New_York]");

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

Converts a civil datetime to a possibly ambiguous zoned datetime in this time zone.

The given civil datetime may be ambiguous in this time zone. A civil datetime is ambiguous when either of the following occurs:

  • When the civil datetime falls into a "gap." That is, when there is a jump forward in time where a span of time does not appear on the clocks in this time zone. This typically manifests as a 1 hour jump forward into daylight saving time.
  • When the civil datetime falls into a "fold." That is, when there is a jump backward in time where a span of time is repeated on the clocks in this time zone. This typically manifests as a 1 hour jump backward out of daylight saving time.

Unlike TimeZone::to_zoned, this method does not do any automatic disambiguation. Instead, callers are expected to use the methods on AmbiguousZoned to resolve any ambiguity, if it occurs.

Example

This example shows how to return an error when the civil datetime given is ambiguous:

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

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

// This is not ambiguous:
let dt = date(2024, 3, 10).at(1, 0, 0, 0);
assert_eq!(
    tz.to_ambiguous_zoned(dt).unambiguous()?.to_string(),
    "2024-03-10T01:00:00-05:00[America/New_York]",
);
// But this is a gap, and thus ambiguous! So an error is returned.
let dt = date(2024, 3, 10).at(2, 0, 0, 0);
assert!(tz.to_ambiguous_zoned(dt).unambiguous().is_err());
// And so is this, because it's a fold.
let dt = date(2024, 11, 3).at(1, 0, 0, 0);
assert!(tz.to_ambiguous_zoned(dt).unambiguous().is_err());

# Ok::<(), Box<dyn std::error::Error>>(())
fn into_ambiguous_zoned(self: Self, dt: DateTime) -> AmbiguousZoned

Converts a civil datetime to a possibly ambiguous zoned datetime in this time zone, and does so by assuming ownership of this TimeZone.

This is identical to TimeZone::to_ambiguous_zoned, but it avoids a TimeZone::clone() call. (Which are cheap, but not completely free.)

Example

This example shows how to create a Zoned value from a TimeZone and a DateTime without cloning the TimeZone:

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

let tz = TimeZone::get("America/New_York")?;
let dt = date(2024, 3, 10).at(1, 0, 0, 0);
assert_eq!(
    tz.into_ambiguous_zoned(dt).unambiguous()?.to_string(),
    "2024-03-10T01:00:00-05:00[America/New_York]",
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn to_timestamp(self: &Self, dt: DateTime) -> Result<Timestamp, Error>

Converts a civil datetime to a Timestamp in this time zone.

The given civil datetime may be ambiguous in this time zone. A civil datetime is ambiguous when either of the following occurs:

  • When the civil datetime falls into a "gap." That is, when there is a jump forward in time where a span of time does not appear on the clocks in this time zone. This typically manifests as a 1 hour jump forward into daylight saving time.
  • When the civil datetime falls into a "fold." That is, when there is a jump backward in time where a span of time is repeated on the clocks in this time zone. This typically manifests as a 1 hour jump backward out of daylight saving time.

This routine automatically resolves both of the above ambiguities via the Disambiguation::Compatible strategy. That in, the case of a gap, the time after the gap is used. In the case of a fold, the first repetition of the clock time is used.

This routine is identical to TimeZone::to_zoned, except it returns a Timestamp instead of a zoned datetime. The benefit of this method is that it never requires cloning or consuming ownership of a TimeZone, and it doesn't require construction of Zoned which has a small but non-zero cost. (This is partially because a Zoned value contains a TimeZone, but of course, a Timestamp does not.)

Example

This example shows how disambiguation works:

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

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

// This demonstrates disambiguation behavior for a gap.
let ts = tz.to_timestamp(date(2024, 3, 10).at(2, 30, 0, 0))?;
assert_eq!(ts.to_string(), "2024-03-10T07:30:00Z");
// This demonstrates disambiguation behavior for a fold.
// Notice the offset: the -04 corresponds to the time while
// still in DST. The second repetition of the 1 o'clock hour
// occurs outside of DST, in "standard" time, with the offset -5.
let ts = tz.to_timestamp(date(2024, 11, 3).at(1, 30, 0, 0))?;
assert_eq!(ts.to_string(), "2024-11-03T05:30:00Z");

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

Converts a civil datetime to a possibly ambiguous timestamp in this time zone.

The given civil datetime may be ambiguous in this time zone. A civil datetime is ambiguous when either of the following occurs:

  • When the civil datetime falls into a "gap." That is, when there is a jump forward in time where a span of time does not appear on the clocks in this time zone. This typically manifests as a 1 hour jump forward into daylight saving time.
  • When the civil datetime falls into a "fold." That is, when there is a jump backward in time where a span of time is repeated on the clocks in this time zone. This typically manifests as a 1 hour jump backward out of daylight saving time.

Unlike TimeZone::to_timestamp, this method does not do any automatic disambiguation. Instead, callers are expected to use the methods on AmbiguousTimestamp to resolve any ambiguity, if it occurs.

This routine is identical to TimeZone::to_ambiguous_zoned, except it returns an AmbiguousTimestamp instead of a AmbiguousZoned. The benefit of this method is that it never requires cloning or consuming ownership of a TimeZone, and it doesn't require construction of Zoned which has a small but non-zero cost. (This is partially because a Zoned value contains a TimeZone, but of course, a Timestamp does not.)

Example

This example shows how to return an error when the civil datetime given is ambiguous:

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

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

// This is not ambiguous:
let dt = date(2024, 3, 10).at(1, 0, 0, 0);
assert_eq!(
    tz.to_ambiguous_timestamp(dt).unambiguous()?.to_string(),
    "2024-03-10T06:00:00Z",
);
// But this is a gap, and thus ambiguous! So an error is returned.
let dt = date(2024, 3, 10).at(2, 0, 0, 0);
assert!(tz.to_ambiguous_timestamp(dt).unambiguous().is_err());
// And so is this, because it's a fold.
let dt = date(2024, 11, 3).at(1, 0, 0, 0);
assert!(tz.to_ambiguous_timestamp(dt).unambiguous().is_err());

# Ok::<(), Box<dyn std::error::Error>>(())
fn preceding<'t>(self: &'t Self, timestamp: Timestamp) -> TimeZonePrecedingTransitions<'t>

Returns an iterator of time zone transitions preceding the given timestamp. The iterator returned yields TimeZoneTransition elements.

The order of the iterator returned moves backward through time. If there is a previous transition, then the timestamp of that transition is guaranteed to be strictly less than the timestamp given.

This is a low level API that you generally shouldn't need. It's useful in cases where you need to know something about the specific instants at which time zone transitions occur. For example, an embedded device might need to be explicitly programmed with daylight saving time transitions. APIs like this enable callers to explore those transitions.

A time zone transition refers to a specific point in time when the offset from UTC for a particular geographical region changes. This is usually a result of daylight saving time, but it can also occur when a geographic region changes its permanent offset from UTC.

The iterator returned is not guaranteed to yield any elements. For example, this occurs with a fixed offset time zone. Logically, it would also be possible for the iterator to be infinite, except that eventually the timestamp would overflow Jiff's minimum timestamp value, at which point, iteration stops.

Example: time since the previous transition

This example shows how much time has passed since the previous time zone transition:

use jiff::{Unit, Zoned};

let now: Zoned = "2024-12-31 18:25-05[US/Eastern]".parse()?;
let trans = now.time_zone().preceding(now.timestamp()).next().unwrap();
let prev_at = trans.timestamp().to_zoned(now.time_zone().clone());
let span = now.since((Unit::Year, &prev_at))?;
assert_eq!(format!("{span:#}"), "1mo 27d 17h 25m");

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

Example: show the 5 previous time zone transitions

This shows how to find the 5 preceding time zone transitions (from a particular datetime) for a particular time zone:

use jiff::{tz::offset, Zoned};

let now: Zoned = "2024-12-31 18:25-05[US/Eastern]".parse()?;
let transitions = now
    .time_zone()
    .preceding(now.timestamp())
    .take(5)
    .map(|t| (
        t.timestamp().to_zoned(now.time_zone().clone()),
        t.offset(),
        t.abbreviation().to_string(),
    ))
    .collect::<Vec<_>>();
assert_eq!(transitions, vec![
    ("2024-11-03 01:00-05[US/Eastern]".parse()?, offset(-5), "EST".to_string()),
    ("2024-03-10 03:00-04[US/Eastern]".parse()?, offset(-4), "EDT".to_string()),
    ("2023-11-05 01:00-05[US/Eastern]".parse()?, offset(-5), "EST".to_string()),
    ("2023-03-12 03:00-04[US/Eastern]".parse()?, offset(-4), "EDT".to_string()),
    ("2022-11-06 01:00-05[US/Eastern]".parse()?, offset(-5), "EST".to_string()),
]);

# Ok::<(), Box<dyn std::error::Error>>(())
fn following<'t>(self: &'t Self, timestamp: Timestamp) -> TimeZoneFollowingTransitions<'t>

Returns an iterator of time zone transitions following the given timestamp. The iterator returned yields TimeZoneTransition elements.

The order of the iterator returned moves forward through time. If there is a following transition, then the timestamp of that transition is guaranteed to be strictly greater than the timestamp given.

This is a low level API that you generally shouldn't need. It's useful in cases where you need to know something about the specific instants at which time zone transitions occur. For example, an embedded device might need to be explicitly programmed with daylight saving time transitions. APIs like this enable callers to explore those transitions.

A time zone transition refers to a specific point in time when the offset from UTC for a particular geographical region changes. This is usually a result of daylight saving time, but it can also occur when a geographic region changes its permanent offset from UTC.

The iterator returned is not guaranteed to yield any elements. For example, this occurs with a fixed offset time zone. Logically, it would also be possible for the iterator to be infinite, except that eventually the timestamp would overflow Jiff's maximum timestamp value, at which point, iteration stops.

Example: time until the next transition

This example shows how much time is left until the next time zone transition:

use jiff::{Unit, Zoned};

let now: Zoned = "2024-12-31 18:25-05[US/Eastern]".parse()?;
let trans = now.time_zone().following(now.timestamp()).next().unwrap();
let next_at = trans.timestamp().to_zoned(now.time_zone().clone());
let span = now.until((Unit::Year, &next_at))?;
assert_eq!(format!("{span:#}"), "2mo 8d 7h 35m");

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

Example: show the 5 next time zone transitions

This shows how to find the 5 following time zone transitions (from a particular datetime) for a particular time zone:

use jiff::{tz::offset, Zoned};

let now: Zoned = "2024-12-31 18:25-05[US/Eastern]".parse()?;
let transitions = now
    .time_zone()
    .following(now.timestamp())
    .take(5)
    .map(|t| (
        t.timestamp().to_zoned(now.time_zone().clone()),
        t.offset(),
        t.abbreviation().to_string(),
    ))
    .collect::<Vec<_>>();
assert_eq!(transitions, vec![
    ("2025-03-09 03:00-04[US/Eastern]".parse()?, offset(-4), "EDT".to_string()),
    ("2025-11-02 01:00-05[US/Eastern]".parse()?, offset(-5), "EST".to_string()),
    ("2026-03-08 03:00-04[US/Eastern]".parse()?, offset(-4), "EDT".to_string()),
    ("2026-11-01 01:00-05[US/Eastern]".parse()?, offset(-5), "EST".to_string()),
    ("2027-03-14 03:00-04[US/Eastern]".parse()?, offset(-4), "EDT".to_string()),
]);

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

impl Clone for TimeZone

fn clone(self: &Self) -> TimeZone

impl Debug for TimeZone

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

impl Eq for TimeZone

impl Freeze for TimeZone

impl PartialEq for TimeZone

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

impl RefUnwindSafe for TimeZone

impl Send for TimeZone

impl StructuralPartialEq for TimeZone

impl Sync for TimeZone

impl Unpin for TimeZone

impl UnwindSafe for TimeZone

impl<T> Any for TimeZone

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for TimeZone

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

impl<T> BorrowMut for TimeZone

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

impl<T> CloneToUninit for TimeZone

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

impl<T> From for TimeZone

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> ToOwned for TimeZone

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

impl<T, U> Into for TimeZone

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 TimeZone

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

impl<T, U> TryInto for TimeZone

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