Previously, when `TZ` was set to an invalid value, Jiff would still
attempt to detect the system configured time zone. But this is arguably
not great, because it silently (aside from logs) rejects an invalid
`TZ` value. In reality, if `TZ` is set, it is likely that the user
intends for it to have an impact. So if it doesn't, we should bleat
about it.
This manifests as an error when using `TimeZone::try_system()` and
manifests as a error sentinel in the form of `Etc/Unknown` when using
`TimeZone::system()`.
We also tweak some of the logging levels. Namely, in #370, I increased
the number of TRACE-level log messages, which makes it much noisier. So
I've promoted a few things that were TRACE to DEBUG without making the
output much noisier. I guess TRACE should be reserved for variable
length things.
Fixes#364
While in #370 my tests indicated that the slowest tzdb init was still
under ~33ms, right after it merged to master, I saw a spike to 300ms.
300ms is... probably still tolerable, but just barely I think. So this
PR bumps up our threshold to 500ms. We also enable logging to hopefully
get a better sense of where time is being spent.
Finally, we just to improve things by skipping the `right` and `posix`
directories in typicaly `/usr/share/zoneinfo` installations. Jiff
doesn't use them and they tend to be confusing outputs of
`TimeZoneDatabase::available()`. If users actually need those
directories, they can do `TZDIR=/usr/share/zoneinfo/posix`.
Ref #366
Specifically, this improves performance in the case of
`/usr/share/zoneinfo` being on a slow file system. Namely, #366 reports
cases (in CI) where this initialization could take multiple seconds.
In this commit, we remove the file reads---but keep the directory
traversal---for validating files in `/usr/share/zoneinfo` as TZif data.
Namely, it is common that while _most_ files in a zoneinfo directory are
valid TZif, not all are.
The reason for keeping the initial directory traversal is that it
simplifies the implementation for case insensitive time zone identifier
lookups. Without the IDs in memory, doing a case insensitive lookup on
the file system becomes much harder. Especially since IANA time zone
identifiers use mixed case and I believe there is no algorithmic way to
normalize them. If we do need to get rid of the initial directory scan,
we'll likely need to reframe the `TimeZoneDatabase::available()` API as
potentially returning strings that aren't valid identifiers, or do some
kind of data dependent normalization techniques. (Which may need to be
updated as new releases of tzdb come out.)
In any case, removing the 4-byte file reads seems to dramatically cut
down initialization time. From multiple seconds to 33ms in the slowest
case. In particular, the directory traversal is now done in a way where
the number of syscalls on Unix should be about equivalent to the number
of directories in `/usr/share/zoneinfo` and *not* the number of files.
We also add a test for a slow initial `Zoned::now()` call.
Fixes#366
This apparently got out of date. And it was apparently not being
enforced in CI. And the destination directory used by `jiff-cli` was
outdated. So fix all of that here, including re-generation.
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.
... 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.
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.
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.
The jobs that run `./test`, which test a bunch of different feature
combinations, take quite some time. I think a lot of that time is just
spent compiling the different feature combinations. But I suspect we are
also spending a lot of time running doc tests.
So this switches to Rust 2024 *just* for the jobs that run `./test`.
Jiff did previously use some `unsafe`, but it was either trivial (easy
to understand UTF-8 validity elision) or not really checkable by miri
(ffi). But with the new pointer tagging representation of a `TimeZone`,
miri checking is more important.
I "tested" this by changing some of the pointer tagging code to do UB.
And miri caught it. So these tests should be covering what we want it to
cover.
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
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
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
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.
I guess some platforms use `*const u8` for C strings while most
seemingly use `*const i8`. And indeed, `CStr::as_ptr` returns a
`*const u8` or a `*const i8` depending on the platform. So do the right
thing here and use std's `c_char` alias.
Ref #140Fixes#200
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
I have no idea what's going on here. But WASM tests are currently
[failing on CI] with an unscrutable error. What's worse is that I cannot
reproduce it locally. Sigh.
So for now, we rollback the Rust toolchain to an older version. I
suppose this is just kicking the can down the road, but it follows in
the footsteps of numbat (which Jiff's WASM tests were inspired by).
I found Rust 1.81 by just going backwards from the current release
(1.83).
[failing on CI]: 3373244312
[footsteps of `numbat`]: https://github.com/sharkdp/numbat/pull/563/files
This adds a new `jiff::fmt::serde` sub-module that contains helper
sub-modules that work with Serde's `with` attribute. For example:
```rust
use jiff::Timestamp;
struct Record {
#[serde(with = "jiff::fmt::serde::timestamp::second::required")]
timestamp: Timestamp,
}
let json = r#"{"timestamp":1517644800}"#;
let got: Record = serde_json::from_str(&json)?;
assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
assert_eq!(serde_json::to_string(&got)?, json);
```
This is inspired in part by how Chrono supports a similar use case.
It is expected that the behavior should be the same, although this
implementation does support the full gamut of integer types (including
a 128-bit integer number of nanoseconds). Moreover, the naming is
different. Chrono uses a flatter namespace, where as here, we bury
everything into sub-modules. The idea is to leave some room for future
expansion, although I'm not sure there is much else to add. I also feel
like spelling out `timestamp` instead of `ts` is a bit clearer.
The module paths are quite long, e.g.,
`jiff::fmt::serde::timestamp::second::required` and
`jiff::fmt::serde::timestamp::second::optional`. But they are
predictable. And to mitigate users needing to click around through a
deep module tree, we include the full tree in the `jiff::fmt::serde`
module documentation.
Ref #100, Closes#101
I was getting pretty tired of the long compile times in between test
runs. I thought they seemed particularly long for debug mode. It turns
out that I had actually set `opt-level = 3` for running tests. This was
because there were a select few tests that ran quite long in debug mode.
But iteration time is important, so I trimmed down those tests and
disabled optimizations. Tests run much more quickly now after making a
change to the source code.
We also add `cargo test --profile testrelease` to CI. This is just like
normal tests, but disables `debug_assertions`. Jiff has a lot of extra
stuff going on when `debug_assertions` are enabled due to its internal
ranged integer abstraction. So it's useful to run tests without all that
extra stuff too (reflecting what is intended to be run in production).
Finally, we split win-gnu out to its own CI job and run a stripped down
set of tests. Regretable, but it's *twice* as slow as the next slowest
runner. That's sucks enough that we'll just live with worse test
coverage until this becomes an obvious problem. (I'm also not even sure
anyone is using this target anyway. It's not clear why anyone would.)
This PR basically makes the `wasm{32,64}-unknown-unknown` targets work
_almost_ out of the box. All you need to do is enable Jiff's new `js`
crate feature. This will cause Jiff to depend on `js-sys` and
`wasm-bindgen`. Jiff will then use Javascript APIs to determine the
current time and time zone.
This PR also includes a new long form guide, `PLATFORM.md`, which
describes Jiff's platform support in one central location. (Most
information is already in Jiff's API docs, but it's scattered in a
variety of places.)
Finally, this adds a `wasm32-unknown-unknown` test to CI courtesy of
`wasm-pack`. It just does a basic sanity check that the current time and
time zone can be retrieved.
Fixes#56