Commit graph

85 commits

Author SHA1 Message Date
Andrew Gallant
5d60f33c58
0.2.13 2025-05-05 22:06:38 -04:00
Andrew Gallant
d6bae333b0
0.2.12 2025-05-03 09:21:58 -04:00
Andrew Gallant
0d1cf196a0
0.2.11 2025-05-01 15:36:48 -04:00
Andrew Gallant
75ed28ea23
0.2.10 2025-04-21 19:53:49 -04:00
Andrew Gallant
e2f8b9a7c9
0.2.9 2025-04-19 15:02:40 -04:00
Andrew Gallant
d27af19fe2
0.2.8 2025-04-13 17:59:11 -04:00
Andrew Gallant
3e32e71ac8
0.2.7 2025-04-13 14:08:10 -04:00
Andrew Gallant
5a6f268e82 cargo: add perf-inline crate feature
This is copied over from `regex`. Basically, when this is enabled (which
is the default), then a bunch of `inline(always)` annotations are
scattered in places (as motivated by micro-benchmarking). Otherwise,
these annotations are removed.
2025-04-11 15:58:33 -04:00
Andrew Gallant
3a470c773c cargo: remove dependency on clap
Just to write one example, I don't think it's carrying its weight. Keep
the example, but mark it as `ignore`.
2025-04-10 20:54:30 -04:00
Andrew Gallant
0541c1979c cargo: restore sanity to iterative development
I'm not sure when or how exactly it happened, but in the last weeks,
I've noticed that `rustc` gets effectively stun-locked whenever I make a
change to a source file in Jiff. A quick examination of what the fuck my
computer is doing seems to reveal that it's spending oodles of time
compiling diesel over and over.

I have no idea why this is happening and I don't really care to spend
the time unraveling the mysteries of diesel.

So I took a hammer to the problem. I have effectively shunted all
examples and all "integration" crates out of Jiff's core workspace and
into their own little bloated fiefdoms. To compensate for the fact that
`cargo test --all` no longer tests these things, I've added shell
scripts to run the requisite tests. And those shell scripts are now run
in CI.

I'm now back to a state where I can save a file in Jiff and I get
sub-second `cargo check` response times.
2025-04-10 20:54:30 -04:00
Andrew Gallant
0bdb3b0207
0.2.6 2025-04-07 17:21:51 -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
ebd7ee41f3
deps: bump to jiff-tzdb 0.1.4 2025-03-22 20:39:28 -04:00
Andrew Gallant
4404fb0f6b
0.2.4 2025-03-10 18:35:37 -04:00
Andrew Gallant
fc9e02dfdf
0.2.3 2025-03-07 08:43:20 -05:00
Andrew Gallant
624ffc7cf8
0.2.2 2025-03-06 16:35:45 -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
ba44975f23 jiff-tzdb: switch to rearguard IANA data
This also documents _why_ we do this and other relevant settings
involved in generating the bundled tzdb data.

To give a sense of how this changes things, consider this Rust program:

```rust
use jiff::{tz::TimeZoneDatabase, Timestamp};

fn main() -> anyhow::Result<()> {
    let winter: Timestamp = "2524-01-05T00Z".parse()?;
    let summer: Timestamp = "2524-07-05T00Z".parse()?;

    let tzdb = TimeZoneDatabase::from_dir("/usr/share/zoneinfo")?;
    let tz = tzdb.get("Europe/Dublin")?;
    let info = tz.to_offset_info(winter);
    println!("winter time from zoneinfo: {info:?}");
    let info = tz.to_offset_info(summer);
    println!("summer time from zoneinfo: {info:?}");

    let tzdb = TimeZoneDatabase::bundled();
    let tz = tzdb.get("Europe/Dublin")?;
    let info = tz.to_offset_info(winter);
    println!(" winter time from bundled: {info:?}");
    let info = tz.to_offset_info(summer);
    println!(" summer time from bundled: {info:?}");

    Ok(())
}
```

Before this PR, on my Archlinux system, I get this output:

```
winter time from zoneinfo: TimeZoneOffsetInfo { offset: 00:00:00, dst: Yes, abbreviation: Borrowed("GMT") }
summer time from zoneinfo: TimeZoneOffsetInfo { offset: 01:00:00, dst: No, abbreviation: Borrowed("IST") }
 winter time from bundled: TimeZoneOffsetInfo { offset: 00:00:00, dst: Yes, abbreviation: Borrowed("GMT") }
 summer time from bundled: TimeZoneOffsetInfo { offset: 01:00:00, dst: No, abbreviation: Borrowed("IST") }
```

That is, the tzdb from `/usr/share/zoneinfo` on my system matches what
the tzdb from `jiff-tzdb` does. However, on my macOS system, I get
this output:

```
winter time from zoneinfo: TimeZoneOffsetInfo { offset: 00:00:00, dst: No, abbreviation: Borrowed("GMT") }
summer time from zoneinfo: TimeZoneOffsetInfo { offset: 01:00:00, dst: Yes, abbreviation: Borrowed("IST") }
 winter time from bundled: TimeZoneOffsetInfo { offset: 00:00:00, dst: Yes, abbreviation: Borrowed("GMT") }
 summer time from bundled: TimeZoneOffsetInfo { offset: 01:00:00, dst: No, abbreviation: Borrowed("IST") }
```

That's because `/usr/share/zoneinfo` on macOS (2025-02-27) uses
rearguard data. This PR makes `jiff-tzdb` match macOS, so that the
output of the above program _with_ this PR on my Linux system is now:

```
winter time from zoneinfo: TimeZoneOffsetInfo { offset: 00:00:00, dst: Yes, abbreviation: Borrowed("GMT") }
summer time from zoneinfo: TimeZoneOffsetInfo { offset: 01:00:00, dst: No, abbreviation: Borrowed("IST") }
 winter time from bundled: TimeZoneOffsetInfo { offset: 00:00:00, dst: No, abbreviation: Borrowed("GMT") }
 summer time from bundled: TimeZoneOffsetInfo { offset: 01:00:00, dst: Yes, abbreviation: Borrowed("IST") }
```

The reason that Jiff is switching to rearguard data is a bit subtle and has to
do with a difference in how the IANA Time Zone Database treats its internal
"daylight saving time" flag and what people in the "real world" consider
"daylight saving time." For example, in the standard distribution of the IANA
Time Zone Database, `Europe/Dublin` has its daylight saving time flag set to
_true_ during Winter and set to _false_ during Summer. The actual time shifts
are the same as, e.g., `Europe/London`, but which one is actually labeled
"daylight saving time" is not.

The IANA Time Zone Database does this for `Europe/Dublin`, presumably, because
_legally_, time during the Summer in Ireland is called `Irish Standard Time`,
and time during the Winter is called `Greenwich Mean Time`. These legal names
are reversed from what is typically the case, where "standard" time is during
the Winter and daylight saving time is during the Summer. The IANA Time Zone
Database implements this tweak in legal language via a "negative daylight
saving time offset." This is somewhat odd, and some consumers of the IANA Time
Zone Database cannot handle it. Thus, the rearguard format was born for,
seemingly, legacy programs.

Jiff can handle negative daylight saving time offsets just fine, but we use the
rearguard format anyway so that the underlying data more accurately reflects
on-the-ground reality for humans living in `Europe/Dublin`. In particular,
using the rearguard data enables [localization of time zone names] to be done
correctly.

Closes #258

[localization of time zone names]: https://github.com/BurntSushi/jiff/issues/258
2025-02-27 16:16:35 -05:00
Andrew Gallant
04d895345a jiff-tz-static: add initial version of new proc macro
This isn't quite done, but it does parse TZif and emits the correct
Jiff code to construct a `TimeZone` in a const context.

The main thing missing here is a fair bit of polish and a change
to the TimeZone internals to actually support this method of
construction in core-only environments without increasing the size
of `TimeZone` (i.e., pointer tagging).
2025-02-26 16:55:17 -05:00
Andrew Gallant
921f2cc74b
0.2.1 2025-02-16 15:53:08 -05:00
Andrew Gallant
d7438df8fa dev: remove jiff-icu from dev dependencies
Since jiff-icu depends on jiff, making jiff depend on jiff-icu for dev
causes a loop that leads to extra compilations when running tests for
jiff. This is annoying, because it means it's harder to run tests and
show doc examples that use jiff-icu. Bah. But there was only one, so
just mark it as `text` for now to ignore it completely.
2025-02-16 15:50:26 -05:00
Andrew Gallant
e094b01adf
crates: re-add jiff-{icu,sqlx,diesel} to workspace
Now that `jiff 0.2` is published, I guess this should work now.
2025-02-10 21:39:16 -05:00
Andrew Gallant
6ea3611b11
0.2.0 2025-02-10 21:38:07 -05:00
Andrew Gallant
d0d1b2ec27
crates: temporarily disconnect jiff-{icu,sqlx,diesel} from workspace
... I can't figure out how to publish `jiff 0.2` with these crates
trying to select `jiff 0.2` from crates.io... which doesn't exist yet.

WTF. I guess I'm probably holding Cargo workspaces wrong. ¯\_(ツ)_/¯
2025-02-10 21:37:54 -05:00
Andrew Gallant
6661b0ca3f doc: beef up the ICU4X docs a bit
Basically, anywhere we linked to the `icu` crate, we now also include a
mention of `jiff-icu` to facilitate conversions. And we update our
comparison example to include use of this crate.
2025-02-10 21:24:55 -05:00
Andrew Gallant
13d553b944 jiff-diesel: add integration crate for Diesel
This is like #240, but adds a crate with wrapper types that implements
Diesel traits. Unlike with SQLx, and unless I'm missing something,
Diesel actually exposes enough of an API to implement datetime support
for MySQL. So that's included here.

Diesel does seem to use some internal privileged APIs for its own
`chrono` and `time` integration that avoids unnecessary allocations
when parsing datetimes from SQLite values. Again, unless I'm missing
something, Jiff is forced to allocate into a `String` first. (But Jiff
only really needs a `&[u8]`.)

I found this experience, along with SQLx, to be absolutely mind-numbing.
Just writing out the example code (which I also used for ad hoc testing)
took an incredible amount of time. I spent _way_ more time playing
fucking type tetris with both SQLx and Diesel than I did anything else
_combined_. It's utterly ridiculous. This further solidifies my opinion
that when you publish crates with an obscene amount of inter-connected
traits, the resulting API becomes very difficult to use.

I'm happy to iterate on the implementation and APIs of this crate (and
`jiff-sqlx`) after an initial release. But I very much appreciate
reviews from Diesel and SQLx experts.

I'm going to say that this closes #50 since this I think this, along
with `jiff-sqlx` and `jiff-icu`, gives us a solid foundation to build
upon. We can track more specific integrations in new issues.

Closes #50
2025-02-08 15:16:20 -05:00
Andrew Gallant
0dff6c6443 jiff-sqlx: add integration crate for SQLx
This PR adds a new `jiff-sqlx` crate. It defines wrapper types for
`Timestamp`, `DateTime`, `Date`, `Time` and `Span`. For each wrapper
type, the SQLx encoding traits are implemented. (Except, with `Span`,
only the decoding trait is implemented.)

This is similar to #141, but organizes things a bit differently. This
also comes with SQLite support. MySQL support is missing since it seems,
at present, to require exposing APIs in SQLx for a correct
implementation.

This initial implementation also omits `Zoned` entirely. I've left a
comment in the source code explaining why. The quick summary is that, at
least for PostgreSQL, I don't see a way to provide support for it
without either silently losing data (the time zone) or just storing it
as an RFC 9557 timestamp in a `TEXT` field. The downside of the latter
is that it doesn't use PostgreSQL native datetime types. (Becuase we
can't. Because PostgreSQL doesn't support storing anything other than
civil time and timestamps with respect to its datetime types.) I do
personally lean toward just using RFC 9557 as a `TEXT` type, but I'd
like to collect real use cases first to make sure that's the right way
to go.

Ref #50, Closes #141
Ref https://github.com/launchbadge/sqlx/issues/3487
2025-02-07 07:24:09 -05:00
Andrew Gallant
6544ed46da jiff-icu: add crate for converting to-and-from icu_calendar
This adds a new crate, `jiff-icu`, with `ConvertFrom` and `ConvertInto`
traits. These mirror the `TryFrom` and `TryInto` traits from `std`. I
took this approach for conversions specifically because it avoids
needing to define wrapper types for everything. It does means we can't
use the standard traits, but I think just importing one trait and having
that add conversion routines on to the types in `jiff` and
`icu_calendar` is an overall lighter weight solution.

One thing I'm slightly unsure about is whether I want `jiff-icu` to just
encompass _everything_ that makes sense to convert between inside of
`icu`, or whether it should actually be `jiff-icu-calendar`. And then
maybe down the road add `jiff-icu-timezone` or something? I'm partial to
just adding everything in `icu` to `jiff-icu` that makes sense to add,
and perhaps exposing some features (that are turned on by default) to
opt out of `icu` crates that you don't need.

I also think this will set the template for other crates I'd like to
add, like `jiff-chrono`. With that said, I think `jiff-icu` is of
particular importance, because it will enable an easier on-ramp to
locale and non-Gregorian support. That is, presumably, `jiff-chrono` and
`jiff-time` (if I do those) will be things that you wouldn't _ideally_
use, but `jiff-icu` will likely be part of intended and recommended
workflows for users.

Ref #50
2025-02-03 19:58:56 -05:00
Andrew Gallant
6920d3973d crates: move all sub-crates into crates/ directory
I should have just done this from the start. I hate doing these kinds of
moves because it fucks up revision history. But if I'm going to be
adding `jiff-icu`, `jiff-chrono`, `jiff-sqlx` and so on, then I really
think we need a sub-directory.

I'm keeping Jiff proper in `src/` though.
2025-02-02 14:10:48 -05:00
Andrew Gallant
5b0791824f
0.1.29 2025-02-02 09:19:56 -05:00
Andrew Gallant
86bff5f10f
0.1.28 2025-01-27 11:59:55 -05:00
Andrew Gallant
e15199a838 dev-dependencies: remove dependency on serde-yml
... and replace it with serde-yaml. This is non-ideal since serde-yaml
is deprecated, but I'll take that over the shenanigans going on with
serde-yml (that I was unaware of, since this was a dev-dependency).

Ref https://x.com/davidtolnay/status/1883906113428676938
2025-01-27 11:59:34 -05:00
Andrew Gallant
d31f0b5cf4
0.1.27 2025-01-25 16:05:46 -05:00
Andrew Gallant
e343211397
0.1.26 2025-01-23 16:52:37 -05:00
Andrew Gallant
0edf9d75f7
0.1.25 2025-01-21 17:07:16 -05:00
Andrew Gallant
4531012f51
0.1.24 2025-01-16 13:34:06 -05:00
Andrew Gallant
6ca47d2afc
deps: bump jiff-tzdb-platform 2025-01-16 13:34:03 -05:00
Andrew Gallant
d1364880d7
deps: bump jiff-tzdb 2025-01-16 13:33:36 -05:00
Andrew Gallant
9eb038008a
0.1.23 2025-01-13 18:11:42 -05:00
Ben Beasley
55fea157c2
cargo: restore license files that were lost in 0.1.22
PR #202
2025-01-13 09:41:17 -05:00
Andrew Gallant
6eba0219b3
0.1.22 2025-01-12 15:38:59 -05:00
Andrew Gallant
6926d6d84f tz: add support for the Android platform
Android support has two prongs:

* The special Android concatenated time zone database will now be read
  by Jiff automatically.
* The `persist.sys.timezone` Android property is read to determine the
  system's configured IANA time zone identifier.

Closes #140
2025-01-12 15:38:41 -05:00
Andrew Gallant
737076ac9c
0.1.21 2025-01-04 12:00:57 -05:00
Andrew Gallant
edb720ca0d
0.1.20 2025-01-03 22:23:05 -05:00
Andrew Gallant
4ea5eff1fe
0.1.19 2025-01-02 11:02:29 -05:00
Andrew Gallant
3b859f1696
0.1.18 2024-12-31 21:57:19 -05:00
Andrew Gallant
60ad10fb04
0.1.17 2024-12-31 10:06:13 -05:00
Andrew Gallant
e6286a6d10 jiff: add core-only (no-std and no-alloc) support to Jiff
There are a few main downsides to core-only support:

1. The error messages produced by Jiff are now awful.
2. Time zone integration is basically gone. All you get are fixed offset
   time zones. Neither POSIX time zones nor IANA time zones are
   supported.
3. The sizes of some types (e.g., `TimeZone` and `Zoned`) are now bigger
   than they are when `alloc` is enabled.

It is possible (1) could be mitigated somewhat, but not entirely. One
way they could be improved is by using more structured error types
instead of strings at the point where the error occurs. When using
strings, some kind of interpolation is needed to convert it to a heap
allocated `String`. But if we used structured error types, we wouldn't
need this heap allocation in the vast majority of cases. However,
doing this is a major pain. Additionally, a significant component
of Jiff's error messages is its chaining, which enables the easy
contextualization of errors. I don't see how to do this in core-only
contexts. In any case, I could maybe be convinced to switch to more
structured errors, but I would need real users of Jiff in core-only mode
to convince me to spend that effort and maintenance headache.

As for (2), time zone integration could be improved. But again, I'd like
to see use cases before digging into this in order to avoid exposing the
wrong API. In particular, POSIX time zones do actually work fine in
core-only contexts, but I've disabled them for now because they greatly
increase the size of a `TimeZone`. There are, I think, ways of shrinking
it without changing the API, but it would be a lot of effort. However,
IANA time zone support is a somewhat different beast, and that requires
some careful thought and real use cases to guide its development.

And for (3), I'm not really sure how to get around inflating the size of
types. My suspicion is that this is really unavoidable, and that it
probably makes using things like `Zoned` in a core-only context
untenable. For core-only, my guess is that they will want an `Unzoned`
type that is like `Zoned`, but doesn't carry a time zone and instead
requires callers to pass in a `&TimeZone` for every API that needs it.
But again, this is a lot of effort to build and I don't want to do it
unless there are compelling use cases for it.

Otherwise, here are some additional small changes that fell out of this
work:

* `Zoned`'s API now includes `FromStr` and `intz` in no-std mode.
This makes it more consistent with other APIs that offer `intz`. I
believe this was a holdover from when `jiff::tz::db()` was itself only
available when `std` was enabled. But I had changed to a model where it
was always available, but, e.g., the time zone database was empty. I
updated routines like `Timestamp::intz` to always be available, but
apparently didn't do it for `Zoned`.
* The docs now make it clear that you need `std` to use the global time
zone database. And explicitly mention that even without `std`, but so
long as `alloc` is enabled, you can still use a `TimeZoneDatabase` with
a bundled copy of the IANA time zone database.

I originally tried to do this work in a commit-by-commit manner, but it
got way too messy. So I just piled everything into one commit.
2024-12-31 10:03:28 -05:00
Luca Barbato
b451d57053 no_std: support platforms that do not have native atomics
Fixes #162, Closes #177
2024-12-29 13:46:14 -05:00