Commit graph

358 commits

Author SHA1 Message Date
Andrew Gallant
0bdb3b0207
0.2.6 2025-04-07 17:21:51 -04:00
Andrew Gallant
6c1cd0e1b0
changelog: 0.2.6 2025-04-07 17:21:16 -04:00
Andrew Gallant
2bd7148090 tz: add /usr/share/lib/zoneinfo
Apparently Illumos uses a different directory than most other Unix
systems in active use. So add it to the list of directories to scan.

I've been unable to verify this since I don't know how to run in an
Illumos environment, but hopefully this should work.

Fixes #315
2025-04-07 17:20:25 -04:00
Andrew Gallant
7bbe21a6cb civil: fix Date::nth_weekday_of_month
This also removes the error prone `transpose` functions I had added to
the bridge between the internal non-ranged datetime types and the ranged
datetime types.

Fixes #312
2025-04-06 14:51:57 -04:00
Andrew Gallant
f41d586445
shared: remove pointless as_ref
I believe the signature of the constructor was originally generic over
`AsRef<[u8]>`, but it got changed to be concrete. However, the
implementation continued calling `as_ref()` unnecessarily. This looks
harmless, but exposes us to inference failures.

Ref https://github.com/rust-lang/rust/pull/139441
2025-04-06 09:31:23 -04:00
Andrew Gallant
9aeb3f1328
changelog: add entry for #309 2025-04-02 21:48:09 -04:00
Andrew Gallant
9259f79099 tz: fix retrieval of past time zone transitions for America/Sao_Paulo
This bug likely exists for other time zones that have historical DST but
have no DST presently.

The problem here was that we were too strongly deferring to the POSIX
time zone to compute the prior transition when the query appeared after
all historical time zones. But that's only correct if the POSIX time
zone has transitions at all. If it doesn't, then the most recent
transition is the most recent historical transition.

Fixes #309
2025-04-02 21:48:03 -04:00
Andrew Gallant
2b84020287 zoned: fix rounding to "days" when near a time zone transition
Previously, if one were to round `2025-03-09T12:15[America/New_York]`
using the defaults (the half-expand rounding mode), then it would round
up to 2025-03-10. This is despite 2025-03-09 only being 23 hours long,
and thus, the tip-over point for rounding is actually 12:30 and not
12:00. Namely, at 12:00 on 2025-03-09, it had only been 2025-03-09 for
11 hours and not 12 hours.

This sort of rounding requires a special path that wasn't handled
correctly by civil datetime rounding. Interestingly, I had previously
been feeding the day length (which is 23 hours for the aforementioned
example) into civil datetime rounding, but it seems that it wasn't
actually doing anything. So I've ripped that out and special cased
rounding `Zoned` to the nearest day (following Temporal's lead).

I discovered this while writing docs for Biff and noticed that it wasn't
producing the expected result. I double checked with Temporal, and
indeed, it gets this case correct. Now Jiff does as well.

Fixes #305
2025-03-31 18:45:54 -04:00
Andrew Gallant
a22abc7617
bench: add benchmarks for constructing a timestamp type
Both `chrono` and `time` use more elaborate representations for their
canonical "timestamp" data types. In contrast, Jiff just uses an integer
number of nanoseconds (like `std::time::Duration`). This makes, among
other things, construction from an integer virtually free:

    $ cargo bench -- timestamp/from_seconds
    timestamp/from_seconds/span/jiff
                            time:   [385.45 ps 385.53 ps 385.62 ps]
    Found 7 outliers among 100 measurements (7.00%)
      1 (1.00%) high mild
      6 (6.00%) high severe

    timestamp/from_seconds/duration/chrono
                            time:   [4.6363 ns 4.6375 ns 4.6387 ns]
    Found 9 outliers among 100 measurements (9.00%)
      5 (5.00%) high mild
      4 (4.00%) high severe

    timestamp/from_seconds/duration/time
                            time:   [9.6360 ns 9.6387 ns 9.6418 ns]
    Found 8 outliers among 100 measurements (8.00%)
      5 (5.00%) high mild
      3 (3.00%) high severe

Chrono doesn't really have a "timestamp" type, but `DateTime<Utc>` is
the de facto choice. `time` just recently added a `UtcDateTime` type,
which also feels a lot like a timestamp type, but its representation is
more elaborate than Jiff's.
2025-03-31 18:31:59 -04:00
Patryk Wychowaniec
ef5ee45a1b
span: avoid cloning when rounding
PR #303
2025-03-30 13:26:51 -04:00
Andrew Gallant
e2799168b6
civil: fix unclear code
This doesn't change any behavior, but some of the names in this code
were very unclear or outright misleading. I believe it's because this
code went through a lot of refactorings and I never swung back around to
clean it up.
2025-03-30 08:26:11 -04:00
Andrew Gallant
8d9ff39020
0.2.5 2025-03-22 20:40:58 -04:00
Andrew Gallant
86e1497117
deps: bump to jiff-tzdb-platform 0.1.3 2025-03-22 20:40:14 -04:00
Andrew Gallant
939549520e
jiff-tzdb-platform-0.1.3 2025-03-22 20:39:37 -04:00
Andrew Gallant
ebd7ee41f3
deps: bump to jiff-tzdb 0.1.4 2025-03-22 20:39:28 -04:00
Andrew Gallant
3ff4513edf
jiff-tzdb-0.1.4 2025-03-22 20:38:00 -04:00
Andrew Gallant
a93e71a5eb
changelog: 0.2.5 2025-03-22 20:37:14 -04:00
Andrew Gallant
e616bf975b jiff-tzdb: update to tzdb 2025b
Note that this adds a new time zone with
identifier `America/Coyhaique`.

Ref: https://lists.iana.org/hyperkitty/list/tz-announce@iana.org/thread/6JVHNHLB6I2WAYTQ75L6KEPEQHFXAJK3/
2025-03-22 20:35:11 -04:00
Andrew Gallant
83c068803e
fmt: fix ISO 8601 week date example
I was using `%w` to format the day of the week in examples referencing
ISO 8601 week days, but the correct directive is `%u`. The difference
is that the former uses `0` for Sunday and the latter uses `7` for
Sunday. (So in the examples given, this doesn't actually change the
output.)
2025-03-16 14:32:48 -04:00
Andrew Gallant
deee5c4229 doc: clarify the guaranteeds for Zoned::{since,until}
Specifically, that you can't get any units bigger than hours *by
default*.

We also fix what was probably a copy-and-paste error in the `Sub` trait
implementation docs for `Zoned`. (I probably copied it from
`jiff::civil::DateTime`, where it can return days by default.)

And we apply similar clarifications to the `jiff::civil::DateTime` docs
as well.

Ref https://github.com/microsoft/openvmm/pull/1028#discussion_r1994483092
2025-03-14 08:39:55 -04:00
Andrew Gallant
4404fb0f6b
0.2.4 2025-03-10 18:35:37 -04:00
Andrew Gallant
b9119a3b21 rangeint: remove PartialEq and PartialOrd impls for i{8,16,32,64,128}
Unfortunately, these impls can cause inference regressions when
non-robust code is written that assumes there is only one
Partial{Eq,Ord} impl for a particular integer type.

It would be one thing if these trait impls were external or somehow
fundamental to Jiff's design. But they only existed as a convenience. So
we remove the trait impls and take our medicine. We already had a
`Constant` wrapper type (also used for trait impls), so we just switch
all equality and inequality comparisons over to that.

I tested this with the following program:

```rust
use env_logger;

fn main() {
    let x: u64 = 1;
    let y: i128 = 0;
    assert!(y < x.into());

    let x: u32 = 1;
    let y: i64 = 0;
    assert!(y < x.into());

    let x: u16 = 1;
    let y: i32 = 0;
    assert!(y < x.into());

    let x: u8 = 1;
    let y: i16 = 0;
    assert!(y < x.into());
}
```

And this `Cargo.toml`:

```toml
[package]
publish = false
name = "jiff-inference-regression"
version = "0.1.0"
edition = "2024"

[patch.crates-io]
jiff = { path = "/home/andrew/rust/jiff/fixit" }

[dependencies]
env_logger = { version = "0.11.7", features = ["humantime"] }

[[bin]]
name = "jiff-inference-regression"
path = "main.rs"

[profile.release]
debug = true
```

I took this path because it's either this or the reporter fixes their
code. Arguably, the reporter should fix their code since it's likely
their code will break when or if some other crate adds similar trait
impls. But as I said, these trait impls are just for convenience, so
the pragmatic trade-off is to remove them and thus not be the source of
whatever problems folks hit.

[I asked the lang team about this problem][lang-zulip-question], and
they seem to agree that this is the right course of action. (And there
are ideas swirling around on how to mitigate this problem, but that's
for the future.)

Fixes #293

[lang-zulip-question]: 504689811
2025-03-10 18:35:16 -04:00
Andrew Gallant
0a0b5a0bee
jiff-static: improve README
This was still the copy from `jiff-tzdb`. Update it to be about
`jiff-static`.
2025-03-10 07:54:20 -04:00
Andrew Gallant
460a0bc039 tz: fix comment and remove superfluous repr(align(..))
When I originally wrote the comment on `Repr`, I got the alignment
wrong. But I caught that mistake and fixed it before merging anything
to `master`. I just hadn't updated the comment.

The `repr(align(..))` was leftovers from experimenting with a
`TzifDateTime` that *wasn't* packed. I was trying to get `rustc` to
optimize comparisons automatically to a single integer, but couldn't get
ti to work. So I resorted to bit-packing. Since the representation is
now just an `i64`, an explicit alignment is not needed. (And it didn't
help anyway.)
2025-03-07 16:07:01 -05:00
Andrew Gallant
fc9e02dfdf
0.2.3 2025-03-07 08:43:20 -05:00
Andrew Gallant
4dbc59f354 shared: fix implementation of IDate::yesterday
Basically, it was wrong when the date was the first day of the month.
This only impacts `jiff 0.2.2`. The bug wasn't present in previous
releases.

This was a transcription error during one of my refactors and it looks
like there unfortunately wasn't test coverage for it. Test coverage has
been added in this PR.

Fixes #290
2025-03-07 08:42:41 -05:00
Andrew Gallant
624ffc7cf8
0.2.2 2025-03-06 16:35:45 -05:00
Andrew Gallant
c7edc0a105
jiff-static: fix crate layout
I wasn't sure why `cargo package` wasn't picking up
`crates/jiff-static/shared`. So I switched to a traditional `src`
layout, which shouldn't be necessary. Indeed, that didn't fix things.
Turns out, I had a bunk `include` rule.

But I wanted to switch to the `src` scheme anyway, so leave that.
2025-03-06 16:34:30 -05:00
Andrew Gallant
08571714a9
jiff-tzdb-0.1.3 2025-03-06 16:28:38 -05:00
Andrew Gallant
300886fef3
jiff-diesel-0.1.3 2025-03-06 16:28:21 -05:00
Andrew Gallant
252a0806dc
jiff-sqlx-0.1.1 2025-03-06 16:28:08 -05:00
Andrew Gallant
fdd352efe1
changelog: 0.2.2 2025-03-06 16:25:02 -05:00
Andrew Gallant
5faa976b9c ci: try the Rust 2024 doc test perf improvement for Windows
The win-msvc job now seems to be the longest... So let's see if we can
speed it up.
2025-03-06 16:05:00 -05:00
Andrew Gallant
c789dd50b9 fmt: disable snapshot tests on core-only
For whatever reason, these seem to take a hideously long time to run in
CI. They even take a long time to run locally, *relatively* speaking. In
core-only, `insta` doesn't support snapshotting at all, which is a huge
bummer. So we just tell insta to force the tests to pass and don't do
any updating. So these tests weren't really being run anyway.

I'm not sure what insta is doing here to be honest, and I don't really
understand why insta can't handle the core-only tests. I mean, I am
still importing the standard library when tests are run, even in
core-only mode. Maybe the insta macros assume the standard library
prelude is present or something? IDK.
2025-03-06 16:05:00 -05:00
Andrew Gallant
35b0c4d422 ci: break ./scripts/test into pieces...
... so that we can run each piece in its own job in CI.

This creates an obscene number of jobs, but I'm really hoping this cuts
down on the total wall clock time.
2025-03-06 16:05:00 -05:00
Andrew Gallant
9dd4b9337d ci: bump to wasmtime 30.0.1
It's what's on my local system, so hopefully it works.
2025-03-06 16:05:00 -05:00
Andrew Gallant
fa62c4fbd7 scripts: fix test-wasm
I'm not sure why this used to work, but now it matches CI and actually
works.
2025-03-06 16:05:00 -05:00
Andrew Gallant
1471f752e5 test: move test and test-wasm into scripts
We are going to try and break `test` apart in order to speed up CI
builds. I don't want to pollute the root project directory with more
random test scripts, so let's tuck them away for now.
2025-03-06 16:05:00 -05:00
Andrew Gallant
eb61588126 zoned: improve docs for Zoned::subsec_nanosecond
It is tempting to think of this method as just being a shortcut for
`zdt.timestamp().subsec_nanosecond()`, but it actually isn't! It's
returning the fractional seconds on the *civil* datetime, not the
timestamp. These are usually the same for times after the Unix epoch,
but can differ for times before it.

While the original request in #283 asks for `subsec_millisecond()` and
`subsec_microsecond()` on `Zoned` in order to be consistent with
`Timestamp`, I'm going to pass on those for now. In particular, since
these would return the fractional second value from the *civil*
datetime, `subsec_millisecond()` would always be equivalent to
`millisecond()`. `subsec_microsecond()` wouldn't be the same as
`microsecond()` (just like `subsec_nanosecond()` isn't the same as
`nanosecond()`), but I find that this just overall adds to the confusion
of the methods here. And if you do need `subsec_microsecond()`, you can
just do `subsec_nanosecond() / 1_000`.

The reason that these additional methods make sense for `Timestamp` is
that `Timestamp` doesn't have a civil datetime. So there are no
individual `millisecond()` or `microsecond()` units. A `Timestamp` is
closer to a `SignedDuration` than a `civil::DateTime`.

Closes #283
2025-03-06 16:05:00 -05:00
Andrew Gallant
0b0a9a3088
test: OMG, remove exit 0
I have no idea how this wound up here. But this means that CI times are
actually longer than what I've been observing. *sob*
2025-03-06 10:11:16 -05:00
Andrew Gallant
77554cfb9b ci: split the "main" job into more pieces
This is the longest job by far, so I think it's worth splitting out some
of its tasks to see if we can reduce overall wall clock time on CI.
2025-03-06 10:09:11 -05:00
Andrew Gallant
9a23a442af jiff-static: update shared copy of code 2025-03-06 10:09:11 -05:00
Andrew Gallant
2bf9eb6ebc ci: check that jiff-cli generate doesn't produce diffs
This should help ensure that generated code doesn't get stale.

This is especially pertinent with the new `src/shared` module, which has
to be copied over to `crates/jiff-static/shared` any time a change is
made. Not all changes result in breakage (theoretical or otherwise), so
it's easy to forget to do.
2025-03-06 10:09:11 -05:00
Andrew Gallant
d6959a2664 changelog: mention perf improvements 2025-03-05 18:48:44 -05:00
Andrew Gallant
bd4ef644e8 bench: make some small updates to benchmarks
And also include updated benchmark results from `time 0.3.38`.
2025-03-05 18:48:44 -05:00
Andrew Gallant
98164d6263 tz: pack TZif civil datetime into i64
This leads to amazing speed-ups for TZ lookups on civil datetimes:

    tz/posix_datetime_to_offset/jiff               1.00     32.6±0.70ns        ? ?/sec    1.01     32.9±0.28ns        ? ?/sec
    tz/posix_timestamp_to_offset/jiff              1.00     21.8±0.17ns        ? ?/sec    1.04     22.6±0.15ns        ? ?/sec
    tz/tzif_bundled_datetime_to_offset/jiff        2.57     23.4±0.19ns        ? ?/sec    1.00      9.1±0.06ns        ? ?/sec
    tz/tzif_bundled_timestamp_to_offset/jiff       1.00      6.0±0.05ns        ? ?/sec    1.04      6.2±0.05ns        ? ?/sec
    tz/tzif_future_datetime_to_offset/jiff         1.35     50.5±0.60ns        ? ?/sec    1.00     37.4±0.67ns        ? ?/sec
    tz/tzif_future_timestamp_to_offset/jiff        1.00     21.2±0.15ns        ? ?/sec    1.00     21.2±0.17ns        ? ?/sec
    tz/tzif_historical_datetime_to_offset/jiff     2.68     23.4±0.17ns        ? ?/sec    1.00      8.7±0.08ns        ? ?/sec
    tz/tzif_historical_timestamp_to_offset/jiff    1.00      6.0±0.05ns        ? ?/sec    1.00      6.0±0.05ns        ? ?/sec

It turns out that comparing civil datetimes is actually quite
expensive. Getting them down to a single integer comparison makes
the binary search much quicker.
2025-03-05 18:48:44 -05:00
Andrew Gallant
f6a5cc6a22 tz: refactor TZif representation to use column storage
This makes binary search for TZ lookups substantially faster.

This is yet another brutal refactor. Changing anything in POSIX time
zones or TZif handling is now a monster pain in the ass because all
of that code is shared in a very awkward way with `jiff-static`.

Ref #271
2025-03-05 18:48:44 -05:00
Andrew Gallant
c9f72a5b56 tz: make tzif::Transition smaller
This is an easy win that uses 64-bit integers to represent a timestamp
instead of 96-bit integers. This is okay because this reflects what the
actual source IANA time zone database uses.

This makes the binary search lookup a fair a bit faster.

Next I'd like to split `Transition` into three sequences: timestamps,
civil datetimes and the local type index. This should make them as
small as possible and further improve binary search lookups (I hope).
2025-03-05 18:48:44 -05:00
Andrew Gallant
5af655e5ea tz: add new enabled-by-default tz-fat feature
When enabled, this feature will "fatten" TZif data by adding more time
zone transitions. This corresponds to what tzdb's `zic` program does
when `-b fat` is given, except Jiff does it at runtime. If the TZif data
has already been fattened, then this has no effect.

The reason for this is that it smooths out performance differences in
time zone runtime lookups between pre-fattened TZif data and "slim"
TZif data. It is unpredictable whether `/usr/share/zoneinfo` is
actually fat or not, so this helps makes performance more predictable
regardless of what the source TZif data looks like.

This uses about 25% more heap memory in my experiments. For a single
time zone, this is, in an absolute sense, likely insignificant. But if
you have thousands of time zones loaded into memory, it can add up. But
that's a somewhat niche use case. However, this can make binary sizes
bigger when the `jiff-static` proc macro is used.

So while unlikely to matter too much, the `tz-fat` feature can be
disabled if you want to prioritize memory usage and binary size.

Fixes #271
2025-03-05 18:48:44 -05:00
Andrew Gallant
092f05ff9f tz/posix: move most code to shared
This was yet another absolutely brutal refactor. But in order to
"fatten" up TZif data after parsing, we need to be able to actually use
POSIX time zones in order to compute missing transitions. And in order
to do that, basically the entire POSIX time zone implementation needs to
be in `shared`. And that means no ranged integers. Which in turn means
implementing several datetime algorithms on just primitives.

This was just overall brutal, and I am getting very close to ripping
out ranged integers.
2025-03-05 18:48:44 -05:00