Time Zones
jiff is a newer date and time crate. Its time zone support is built in
rather than an add-on, and Span represents calendar durations. These
recipes show what jiff does differently from chrono and time; see
Choosing between chrono, time and jiff for how the crates compare.
Examine the current time with a time zone
Use jiff when you need named IANA time zones without an extra crate; see
Choosing between chrono, time and jiff. Gets the current Zoned datetime in the system time zone, converts it
to another zone such as Asia/Tokyo with Zoned::in_tz, and reads its
parts. A Zoned carries its time zone, so the UTC offset is always correct
for that instant. time only has fixed offsets, so it cannot do this lookup.
use jiff::{Timestamp, Zoned};
fn main() -> Result<(), jiff::Error> {
let now = Zoned::now();
println!("Local time: {now}");
let tokyo = now.in_tz("Asia/Tokyo")?;
println!("In Tokyo: {tokyo}");
println!("UTC offset: {}", tokyo.offset());
let timestamp: Timestamp = now.timestamp();
println!("Timestamp: {timestamp}");
println!(
"Year {}, month {}, day {}, weekday {:?}",
now.year(),
now.month(),
now.day(),
now.weekday()
);
println!(
"Time {:02}:{:02}:{:02}",
now.hour(),
now.minute(),
now.second()
);
Ok(())
}
Parse and format a datetime with a time zone
Use jiff when the time zone name must survive a round trip through text;
see Choosing between chrono, time and jiff. Parses an RFC 9557 string, which includes an IANA time zone name
([America/New_York]) as well as the offset, into a Zoned using
FromStr. Output is formatted with Zoned::strftime, which can print the
zone abbreviation (EDT), and strtime::parse handles custom patterns.
Invalid input returns an Err.
use jiff::Zoned;
use jiff::civil::Date;
use jiff::fmt::strtime;
fn main() -> Result<(), jiff::Error> {
// RFC 9557 strings carry an IANA time zone name alongside the offset.
let zoned: Zoned = "2024-07-11T01:14:00-04:00[America/New_York]".parse()?;
println!("Parsed: {zoned}");
println!(
"Formatted: {}",
zoned.strftime("%A, %B %-d, %Y at %-I:%M %p %Z")
);
// Parsing with a custom strftime-style pattern.
let date: Date = strtime::parse("%d/%m/%Y", "11/07/2024")?.to_date()?;
println!("Custom: {date}");
// Invalid input is an `Err`, not a panic.
let bad = "not a date".parse::<Zoned>();
println!("Invalid: {}", bad.is_err());
Ok(())
}
Perform calendar arithmetic with Span
Use jiff when calendar arithmetic has to follow daylight saving time; see
Choosing between chrono, time and jiff. A Span is a duration made of calendar units such as days and weeks as
well as clock units. Zoned::checked_add applies it with the time zone taken
into account, so adding one day to 12:00 before a spring-forward change gives
12:00 the next day, while adding 24 hours gives 13:00. A fixed-offset type such
as time’s OffsetDateTime cannot tell these apart. checked_add returns an
Err rather than panicking on overflow, and Zoned::until computes the
difference between two datetimes.
use jiff::{Span, SpanRound, Unit, Zoned, civil};
fn main() -> Result<(), jiff::Error> {
let start = civil::date(2024, 3, 9)
.at(12, 0, 0, 0)
.in_tz("America/New_York")?;
// Calendar-aware arithmetic: clocks spring forward on March 10, so that day
// is only 23 hours long. Adding one day still lands on 12:00, while adding
// 24 hours lands on 13:00.
let next_day = start.checked_add(Span::new().days(1))?;
println!("{start} + 1 day = {next_day}");
let in_24_hours = start.checked_add(Span::new().hours(24))?;
println!("{start} + 24 hours = {in_24_hours}");
// Mixed units, and checked_add returns `Err` on overflow instead of panicking.
let later = start.checked_add(Span::new().weeks(2).days(1).hours(3))?;
println!("Two weeks, a day and 3 hours later: {later}");
let overflow = start.checked_add(Span::new().years(10_000));
println!("Overflow is an error: {}", overflow.is_err());
// The difference between two datetimes, balanced up to hours.
let end: Zoned = "2024-03-12T09:30:00-04:00[America/New_York]".parse()?;
let diff = start.until((Unit::Hour, &end))?;
println!("Until {end}: {diff:#}");
let rounded = start.until(&end)?.round(
SpanRound::new()
.largest(Unit::Day)
.smallest(Unit::Hour)
.relative(&start),
)?;
println!("Rounded: {rounded:#}");
Ok(())
}