Struct Offset

struct Offset { ... }

Represents a fixed time zone offset.

Negative offsets correspond to time zones west of the prime meridian, while positive offsets correspond to time zones east of the prime meridian. Equivalently, in all cases, civil-time - offset = UTC.

Display format

This type implements the std::fmt::Display trait. It will convert the offset to a string format in the form {sign}{hours}[:{minutes}[:{seconds}]], where minutes and seconds are only present when non-zero. For example:

use jiff::tz;

let o = tz::offset(-5);
assert_eq!(o.to_string(), "-05");
let o = tz::Offset::from_seconds(-18_000).unwrap();
assert_eq!(o.to_string(), "-05");
let o = tz::Offset::from_seconds(-18_060).unwrap();
assert_eq!(o.to_string(), "-05:01");
let o = tz::Offset::from_seconds(-18_062).unwrap();
assert_eq!(o.to_string(), "-05:01:02");

// The min value.
let o = tz::Offset::from_seconds(-93_599).unwrap();
assert_eq!(o.to_string(), "-25:59:59");
// The max value.
let o = tz::Offset::from_seconds(93_599).unwrap();
assert_eq!(o.to_string(), "+25:59:59");
// No offset.
let o = tz::offset(0);
assert_eq!(o.to_string(), "+00");

Example

This shows how to create a zoned datetime with a time zone using a fixed offset:

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

let offset = tz::offset(-4).to_time_zone();
let zdt = date(2024, 7, 8).at(15, 20, 0, 0).to_zoned(offset)?;
assert_eq!(zdt.to_string(), "2024-07-08T15:20:00-04:00[-04:00]");

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

Notice that the zoned datetime still includes a time zone annotation. But since there is no time zone identifier, the offset instead is repeated as an additional assertion that a fixed offset datetime was intended.

Implementations

impl Offset

const fn constant(hours: i8) -> Offset

Creates a new time zone offset in a const context from a given number of hours.

Negative offsets correspond to time zones west of the prime meridian, while positive offsets correspond to time zones east of the prime meridian. Equivalently, in all cases, civil-time - offset = UTC.

The fallible non-const version of this constructor is Offset::from_hours.

Panics

This routine panics when the given number of hours is out of range. Namely, hours must be in the range -25..=25.

Example

use jiff::tz::Offset;

let o = Offset::constant(-5);
assert_eq!(o.seconds(), -18_000);
let o = Offset::constant(5);
assert_eq!(o.seconds(), 18_000);

Alternatively, one can use the terser jiff::tz::offset free function:

use jiff::tz;

let o = tz::offset(-5);
assert_eq!(o.seconds(), -18_000);
let o = tz::offset(5);
assert_eq!(o.seconds(), 18_000);
fn from_hours(hours: i8) -> Result<Offset, Error>

Creates a new time zone offset from a given number of hours.

Negative offsets correspond to time zones west of the prime meridian, while positive offsets correspond to time zones east of the prime meridian. Equivalently, in all cases, civil-time - offset = UTC.

Errors

This routine returns an error when the given number of hours is out of range. Namely, hours must be in the range -25..=25.

Example

use jiff::tz::Offset;

let o = Offset::from_hours(-5)?;
assert_eq!(o.seconds(), -18_000);
let o = Offset::from_hours(5)?;
assert_eq!(o.seconds(), 18_000);

# Ok::<(), Box<dyn std::error::Error>>(())
fn from_seconds(seconds: i32) -> Result<Offset, Error>

Creates a new time zone offset in a const context from a given number of seconds.

Negative offsets correspond to time zones west of the prime meridian, while positive offsets correspond to time zones east of the prime meridian. Equivalently, in all cases, civil-time - offset = UTC.

Errors

This routine returns an error when the given number of seconds is out of range. The range corresponds to the offsets -25:59:59..=25:59:59. In units of seconds, that corresponds to -93,599..=93,599.

Example

use jiff::tz::Offset;

let o = Offset::from_seconds(-18_000)?;
assert_eq!(o.seconds(), -18_000);
let o = Offset::from_seconds(18_000)?;
assert_eq!(o.seconds(), 18_000);

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

Returns the total number of seconds in this offset.

The value returned is guaranteed to represent an offset in the range -25:59:59..=25:59:59. Or more precisely, the value will be in units of seconds in the range -93,599..=93,599.

Negative offsets correspond to time zones west of the prime meridian, while positive offsets correspond to time zones east of the prime meridian. Equivalently, in all cases, civil-time - offset = UTC.

Example

use jiff::tz;

let o = tz::offset(-5);
assert_eq!(o.seconds(), -18_000);
let o = tz::offset(5);
assert_eq!(o.seconds(), 18_000);
fn negate(self: Self) -> Offset

Returns the negation of this offset.

A negative offset will become positive and vice versa. This is a no-op if the offset is zero.

This never panics.

Example

use jiff::tz;

assert_eq!(tz::offset(-5).negate(), tz::offset(5));
// It's also available via the `-` operator:
assert_eq!(-tz::offset(-5), tz::offset(5));
fn signum(self: Self) -> i8

Returns the "sign number" or "signum" of this offset.

The number returned is -1 when this offset is negative, 0 when this offset is zero and 1 when this span is positive.

Example

use jiff::tz;

assert_eq!(tz::offset(5).signum(), 1);
assert_eq!(tz::offset(0).signum(), 0);
assert_eq!(tz::offset(-5).signum(), -1);
fn is_positive(self: Self) -> bool

Returns true if and only if this offset is positive.

This returns false when the offset is zero or negative.

Example

use jiff::tz;

assert!(tz::offset(5).is_positive());
assert!(!tz::offset(0).is_positive());
assert!(!tz::offset(-5).is_positive());
fn is_negative(self: Self) -> bool

Returns true if and only if this offset is less than zero.

Example

use jiff::tz;

assert!(!tz::offset(5).is_negative());
assert!(!tz::offset(0).is_negative());
assert!(tz::offset(-5).is_negative());
fn is_zero(self: Self) -> bool

Returns true if and only if this offset is zero.

Or equivalently, when this offset corresponds to Offset::UTC.

Example

use jiff::tz;

assert!(!tz::offset(5).is_zero());
assert!(tz::offset(0).is_zero());
assert!(!tz::offset(-5).is_zero());
fn to_time_zone(self: Self) -> TimeZone

Converts this offset into a TimeZone.

This is a convenience function for calling TimeZone::fixed with this offset.

Example

use jiff::tz::offset;

let tz = offset(-4).to_time_zone();
assert_eq!(
    tz.to_datetime(jiff::Timestamp::UNIX_EPOCH).to_string(),
    "1969-12-31T20:00:00",
);
fn to_datetime(self: Self, timestamp: Timestamp) -> DateTime

Converts the given timestamp to a civil datetime using this offset.

Example

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

assert_eq!(
    tz::offset(-8).to_datetime(Timestamp::UNIX_EPOCH),
    date(1969, 12, 31).at(16, 0, 0, 0),
);
fn to_timestamp(self: Self, dt: DateTime) -> Result<Timestamp, Error>

Converts the given civil datetime to a timestamp using this offset.

Errors

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

Example

This example shows how to find the timestamp corresponding to 1969-12-31T16:00:00-08.

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

assert_eq!(
    tz::offset(-8).to_timestamp(date(1969, 12, 31).at(16, 0, 0, 0))?,
    Timestamp::UNIX_EPOCH,
);
# Ok::<(), Box<dyn std::error::Error>>(())

This example shows some maximum boundary conditions where this routine will fail:

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

let dt = date(9999, 12, 31).at(23, 0, 0, 0);
assert!(tz::offset(-8).to_timestamp(dt).is_err());

// If the offset is big enough, then converting it to a UTC
// timestamp will fit, even when using the maximum civil datetime.
let dt = date(9999, 12, 31).at(23, 59, 59, 999_999_999);
assert_eq!(tz::Offset::MAX.to_timestamp(dt).unwrap(), Timestamp::MAX);
// But adjust the offset down 1 second is enough to go out-of-bounds.
assert!((tz::Offset::MAX - 1.seconds()).to_timestamp(dt).is_err());

Same as above, but for minimum values:

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

let dt = date(-9999, 1, 1).at(1, 0, 0, 0);
assert!(tz::offset(8).to_timestamp(dt).is_err());

// If the offset is small enough, then converting it to a UTC
// timestamp will fit, even when using the minimum civil datetime.
let dt = date(-9999, 1, 1).at(0, 0, 0, 0);
assert_eq!(tz::Offset::MIN.to_timestamp(dt).unwrap(), Timestamp::MIN);
// But adjust the offset up 1 second is enough to go out-of-bounds.
assert!((tz::Offset::MIN + 1.seconds()).to_timestamp(dt).is_err());
fn checked_add<A: Into<OffsetArithmetic>>(self: Self, duration: A) -> Result<Offset, Error>

Adds the given span of time to this offset.

Since time zone offsets have second resolution, any fractional seconds in the duration given are ignored.

This operation accepts three different duration types: Span, SignedDuration or std::time::Duration. This is achieved via From trait implementations for the OffsetArithmetic type.

Errors

This returns an error if the result of adding the given span would exceed the minimum or maximum allowed Offset value.

This also returns an error if the span given contains any non-zero units bigger than hours.

Example

This example shows how to add one hour to an offset (if the offset corresponds to standard time, then adding an hour will usually give you DST time):

use jiff::{tz, ToSpan};

let off = tz::offset(-5);
assert_eq!(off.checked_add(1.hours()).unwrap(), tz::offset(-4));

And note that while fractional seconds are ignored, units less than seconds aren't ignored if they sum up to a duration at least as big as one second:

use jiff::{tz, ToSpan};

let off = tz::offset(5);
let span = 900.milliseconds()
    .microseconds(50_000)
    .nanoseconds(50_000_000);
assert_eq!(
    off.checked_add(span).unwrap(),
    tz::Offset::from_seconds((5 * 60 * 60) + 1).unwrap(),
);
// Any leftover fractional part is ignored.
let span = 901.milliseconds()
    .microseconds(50_001)
    .nanoseconds(50_000_001);
assert_eq!(
    off.checked_add(span).unwrap(),
    tz::Offset::from_seconds((5 * 60 * 60) + 1).unwrap(),
);

This example shows some cases where checked addition will fail.

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

// Adding units above 'hour' always results in an error.
assert!(Offset::UTC.checked_add(1.day()).is_err());
assert!(Offset::UTC.checked_add(1.week()).is_err());
assert!(Offset::UTC.checked_add(1.month()).is_err());
assert!(Offset::UTC.checked_add(1.year()).is_err());

// Adding even 1 second to the max, or subtracting 1 from the min,
// will result in overflow and thus an error will be returned.
assert!(Offset::MIN.checked_add(-1.seconds()).is_err());
assert!(Offset::MAX.checked_add(1.seconds()).is_err());

Example: adding absolute durations

This shows how to add signed and unsigned absolute durations to an Offset. Like with Spans, any fractional seconds are ignored.

use std::time::Duration;

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

let off = offset(-10);

let dur = SignedDuration::from_hours(11);
assert_eq!(off.checked_add(dur)?, offset(1));
assert_eq!(off.checked_add(-dur)?, offset(-21));

// Any leftover time is truncated. That is, only
// whole seconds from the duration are considered.
let dur = Duration::new(3 * 60 * 60, 999_999_999);
assert_eq!(off.checked_add(dur)?, offset(-7));

# Ok::<(), Box<dyn std::error::Error>>(())
fn checked_sub<A: Into<OffsetArithmetic>>(self: Self, duration: A) -> Result<Offset, Error>

This routine is identical to Offset::checked_add with the duration negated.

Errors

This has the same error conditions as Offset::checked_add.

Example

use std::time::Duration;

use jiff::{tz, SignedDuration, ToSpan};

let off = tz::offset(-4);
assert_eq!(
    off.checked_sub(1.hours())?,
    tz::offset(-5),
);
assert_eq!(
    off.checked_sub(SignedDuration::from_hours(1))?,
    tz::offset(-5),
);
assert_eq!(
    off.checked_sub(Duration::from_secs(60 * 60))?,
    tz::offset(-5),
);

# Ok::<(), Box<dyn std::error::Error>>(())
fn saturating_add<A: Into<OffsetArithmetic>>(self: Self, duration: A) -> Offset

This routine is identical to Offset::checked_add, except the result saturates on overflow. That is, instead of overflow, either Offset::MIN or Offset::MAX is returned.

Example

This example shows some cases where saturation will occur.

use jiff::{tz::Offset, SignedDuration, ToSpan};

// Adding units above 'day' always results in saturation.
assert_eq!(Offset::UTC.saturating_add(1.weeks()), Offset::MAX);
assert_eq!(Offset::UTC.saturating_add(1.months()), Offset::MAX);
assert_eq!(Offset::UTC.saturating_add(1.years()), Offset::MAX);

// Adding even 1 second to the max, or subtracting 1 from the min,
// will result in saturationg.
assert_eq!(Offset::MIN.saturating_add(-1.seconds()), Offset::MIN);
assert_eq!(Offset::MAX.saturating_add(1.seconds()), Offset::MAX);

// Adding absolute durations also saturates as expected.
assert_eq!(Offset::UTC.saturating_add(SignedDuration::MAX), Offset::MAX);
assert_eq!(Offset::UTC.saturating_add(SignedDuration::MIN), Offset::MIN);
assert_eq!(Offset::UTC.saturating_add(std::time::Duration::MAX), Offset::MAX);
fn saturating_sub<A: Into<OffsetArithmetic>>(self: Self, duration: A) -> Offset

This routine is identical to Offset::saturating_add with the span parameter negated.

Example

This example shows some cases where saturation will occur.

use jiff::{tz::Offset, SignedDuration, ToSpan};

// Adding units above 'day' always results in saturation.
assert_eq!(Offset::UTC.saturating_sub(1.weeks()), Offset::MIN);
assert_eq!(Offset::UTC.saturating_sub(1.months()), Offset::MIN);
assert_eq!(Offset::UTC.saturating_sub(1.years()), Offset::MIN);

// Adding even 1 second to the max, or subtracting 1 from the min,
// will result in saturationg.
assert_eq!(Offset::MIN.saturating_sub(1.seconds()), Offset::MIN);
assert_eq!(Offset::MAX.saturating_sub(-1.seconds()), Offset::MAX);

// Adding absolute durations also saturates as expected.
assert_eq!(Offset::UTC.saturating_sub(SignedDuration::MAX), Offset::MIN);
assert_eq!(Offset::UTC.saturating_sub(SignedDuration::MIN), Offset::MAX);
assert_eq!(Offset::UTC.saturating_sub(std::time::Duration::MAX), Offset::MIN);
fn until(self: Self, other: Offset) -> Span

Returns the span of time from this offset until the other given.

When the other offset is more west (i.e., more negative) of the prime meridian than this offset, then the span returned will be negative.

Properties

Adding the span returned to this offset will always equal the other offset given.

Examples

use jiff::{tz, ToSpan};

assert_eq!(
    tz::offset(-5).until(tz::Offset::UTC),
    (5 * 60 * 60).seconds().fieldwise(),
);
// Flipping the operands in this case results in a negative span.
assert_eq!(
    tz::Offset::UTC.until(tz::offset(-5)),
    -(5 * 60 * 60).seconds().fieldwise(),
);
fn since(self: Self, other: Offset) -> Span

Returns the span of time since the other offset given from this offset.

When the other is more east (i.e., more positive) of the prime meridian than this offset, then the span returned will be negative.

Properties

Adding the span returned to the other offset will always equal this offset.

Examples

use jiff::{tz, ToSpan};

assert_eq!(
    tz::Offset::UTC.since(tz::offset(-5)),
    (5 * 60 * 60).seconds().fieldwise(),
);
// Flipping the operands in this case results in a negative span.
assert_eq!(
    tz::offset(-5).since(tz::Offset::UTC),
    -(5 * 60 * 60).seconds().fieldwise(),
);
fn duration_until(self: Self, other: Offset) -> SignedDuration

Returns an absolute duration representing the difference in time from this offset until the given other offset.

When the other offset is more west (i.e., more negative) of the prime meridian than this offset, then the duration returned will be negative.

Unlike Offset::until, this returns a duration corresponding to a 96-bit integer of nanoseconds between two offsets.

When should I use this versus Offset::until?

See the type documentation for SignedDuration for the section on when one should use Span and when one should use SignedDuration. In short, use Span (and therefore Offset::until) unless you have a specific reason to do otherwise.

Examples

use jiff::{tz, SignedDuration};

assert_eq!(
    tz::offset(-5).duration_until(tz::Offset::UTC),
    SignedDuration::from_hours(5),
);
// Flipping the operands in this case results in a negative span.
assert_eq!(
    tz::Offset::UTC.duration_until(tz::offset(-5)),
    SignedDuration::from_hours(-5),
);
fn duration_since(self: Self, other: Offset) -> SignedDuration

This routine is identical to Offset::duration_until, but the order of the parameters is flipped.

Examples

use jiff::{tz, SignedDuration};

assert_eq!(
    tz::Offset::UTC.duration_since(tz::offset(-5)),
    SignedDuration::from_hours(5),
);
assert_eq!(
    tz::offset(-5).duration_since(tz::Offset::UTC),
    SignedDuration::from_hours(-5),
);
fn round<R: Into<OffsetRound>>(self: Self, options: R) -> Result<Offset, Error>

Returns a new offset that is rounded according to the given configuration.

Rounding an offset has a number of parameters, all of which are optional. When no parameters are given, then no rounding is done, and the offset as given is returned. That is, it's a no-op.

As is consistent with Offset itself, rounding only supports units of hours, minutes or seconds. If any other unit is provided, then an error is returned.

The parameters are, in brief:

  • OffsetRound::smallest sets the smallest Unit that is allowed to be non-zero in the offset returned. By default, it is set to Unit::Second, i.e., no rounding occurs. When the smallest unit is set to something bigger than seconds, then the non-zero units in the offset smaller than the smallest unit are used to determine how the offset should be rounded. For example, rounding +01:59 to the nearest hour using the default rounding mode would produce +02:00.
  • OffsetRound::mode determines how to handle the remainder when rounding. The default is RoundMode::HalfExpand, which corresponds to how you were likely taught to round in school. Alternative modes, like RoundMode::Trunc, exist too. For example, a truncating rounding of +01:59 to the nearest hour would produce +01:00.
  • OffsetRound::increment sets the rounding granularity to use for the configured smallest unit. For example, if the smallest unit is minutes and the increment is 15, then the offset returned will always have its minute component set to a multiple of 15.

Errors

In general, there are two main ways for rounding to fail: an improper configuration like trying to round an offset to the nearest unit other than hours/minutes/seconds, or when overflow occurs. Overflow can occur when the offset would exceed the minimum or maximum Offset values. Typically, this can only realistically happen if the offset before rounding is already close to its minimum or maximum value.

Example: rounding to the nearest multiple of 15 minutes

Most time zone offsets fall on an hour boundary, but some fall on the half-hour or even 15 minute boundary:

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

let offset = Offset::from_seconds(-(44 * 60 + 30)).unwrap();
let rounded = offset.round((Unit::Minute, 15))?;
assert_eq!(rounded, Offset::from_seconds(-45 * 60).unwrap());

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

Example: rounding can fail via overflow

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

assert_eq!(Offset::MAX.to_string(), "+25:59:59");
assert_eq!(
    Offset::MAX.round(Unit::Minute).unwrap_err().to_string(),
    "rounding offset `+25:59:59` resulted in a duration of 26h, \
     which overflows `Offset`",
);

impl Add for Offset

fn add(self: Self, rhs: SignedDuration) -> Offset

impl Add for Offset

fn add(self: Self, rhs: Span) -> Offset

impl Add for Offset

fn add(self: Self, rhs: UnsignedDuration) -> Offset

impl AddAssign for Offset

fn add_assign(self: &mut Self, rhs: SignedDuration)

impl AddAssign for Offset

fn add_assign(self: &mut Self, rhs: Span)

impl AddAssign for Offset

fn add_assign(self: &mut Self, rhs: UnsignedDuration)

impl Clone for Offset

fn clone(self: &Self) -> Offset

impl Copy for Offset

impl Debug for Offset

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

impl Display for Offset

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

impl Eq for Offset

impl Freeze for Offset

impl Hash for Offset

fn hash<__H: $crate::hash::Hasher>(self: &Self, state: &mut __H)

impl Neg for Offset

fn neg(self: Self) -> Offset

impl Ord for Offset

fn cmp(self: &Self, other: &Offset) -> Ordering

impl PartialEq for Offset

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

impl PartialOrd for Offset

fn partial_cmp(self: &Self, other: &Offset) -> Option<Ordering>

impl RefUnwindSafe for Offset

impl Send for Offset

impl StructuralPartialEq for Offset

impl Sub for Offset

fn sub(self: Self, rhs: Span) -> Offset

impl Sub for Offset

fn sub(self: Self, rhs: SignedDuration) -> Offset

impl Sub for Offset

fn sub(self: Self, rhs: UnsignedDuration) -> Offset

impl Sub for Offset

fn sub(self: Self, rhs: Offset) -> Span

impl SubAssign for Offset

fn sub_assign(self: &mut Self, rhs: Span)

impl SubAssign for Offset

fn sub_assign(self: &mut Self, rhs: SignedDuration)

impl SubAssign for Offset

fn sub_assign(self: &mut Self, rhs: UnsignedDuration)

impl Sync for Offset

impl TryFrom for Offset

fn try_from(sdur: SignedDuration) -> Result<Offset, Error>

impl Unpin for Offset

impl UnsafeUnpin for Offset

impl UnwindSafe for Offset

impl<T> Any for Offset

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for Offset

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

impl<T> BorrowMut for Offset

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

impl<T> CloneToUninit for Offset

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

impl<T> From for Offset

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> ToOwned for Offset

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

impl<T> ToString for Offset

fn to_string(self: &Self) -> String

impl<T, U> Into for Offset

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 Offset

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

impl<T, U> TryInto for Offset

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