tz: simplify and optimize ReasonablePosixTimeZone

It's still not quite as fast as I would hope, but both of the
primary TZ operations are now about 2x as fast.

We also simplify the data structure internals. This helped
make it faster since more stuff is now pre-computed. But now
we can't roundtrip a parsed POSIX TZ exactly. But I think this
is not a big deal and there is no specific requirement for
needing a POSIX TZ to be formatted exactly how it was given.
(Of course, semantically, the POSIX TZ will round-trip.) For
example, a POSIX time zone that uses `+` explicitly is allowed,
but Jiff will never format a POSIX TZ with a `+` anywhere.

This was partially motivated by optimization, but I was initially
moved to do this because I plan to expose some of these internals
for use with proc macros. And I wanted the exposed type definitions
to be simpler. A `ReasonablePosixTz` was pretty unwieldy before.
But now it's much tighter.

Ref #256
This commit is contained in:
Andrew Gallant 2025-02-18 22:51:40 -05:00
parent fd3eeecbf5
commit 9151fb1653
6 changed files with 516 additions and 475 deletions

View file

@ -6,6 +6,7 @@ mod datetime;
mod parse;
mod print;
mod timestamp;
mod tz;
mod zoned;
fn main() {
@ -29,6 +30,7 @@ fn main() {
parse::define(&mut c);
print::define(&mut c);
timestamp::define(&mut c);
tz::define(&mut c);
zoned::define(&mut c);
// This is an undocument API. Woohoo.
c.final_summary();

54
bench/src/tz.rs Normal file
View file

@ -0,0 +1,54 @@
use std::hint::black_box as bb;
use criterion::Criterion;
use jiff::{
civil,
tz::{Offset, TimeZone},
Timestamp,
};
use crate::benchmark;
pub(super) fn define(c: &mut Criterion) {
posix_to_offset(c);
posix_to_timestamp(c);
}
/// Measures how long it takes to map a timestamp to an offset using a POSIX
/// time zone.
fn posix_to_offset(c: &mut Criterion) {
const NAME: &str = "tz/posix_to_offset";
const STAMP: Timestamp = Timestamp::constant(1719755160, 0);
const TZ: &str = "EST5EDT,M3.2.0,M11.1.0";
const EXPECTED: Offset = Offset::constant(-4);
let tz = TimeZone::posix(TZ).unwrap();
{
benchmark(c, format!("{NAME}/jiff"), |b| {
b.iter(|| {
let offset = bb(&tz).to_offset(bb(STAMP));
assert_eq!(offset, EXPECTED);
})
});
}
}
/// Measures how long it takes to map a civil datetime to a possibly ambiguous
/// timestamp using a POSIX time zone.
fn posix_to_timestamp(c: &mut Criterion) {
const NAME: &str = "tz/posix_to_timestamp";
const DATETIME: civil::DateTime = civil::date(2024, 6, 30).at(9, 46, 0, 0);
const TZ: &str = "EST5EDT,M3.2.0,M11.1.0";
const EXPECTED: Timestamp = Timestamp::constant(1719755160, 0);
let tz = TimeZone::posix(TZ).unwrap();
{
benchmark(c, format!("{NAME}/jiff"), |b| {
b.iter(|| {
let ambiguous = bb(&tz).to_ambiguous_timestamp(bb(DATETIME));
let ts = ambiguous.unambiguous().unwrap();
assert_eq!(ts, EXPECTED);
})
});
}
}