This document discusses some of the design decisions that led to Jiff's API. The purpose of writing this document is to help folks understand why Jiff's API is the way it is, above and beyond "Jiff did it this way to match Temporal."
This document is written as an FAQ, although it is restricted in scope to the API design of Jiff. This isn't a FAQ for questions like, "How do I add 1 day to a zoned datetime?"
Unlike "Comparison with other Rust datetime crates," this document is opinionated. That is, some value judgments are expressed that are opinion based, and on which reasonable people may disagree.
I wanted something short and related to time for the library name. Most of the "obvious" names are taken.
I thought a lot about phrases or words that had some connection to time. There are more than you think. One phrase I heard a lot as a kid who grew up in New England was, "I'll be back in a jiff" or "I'll be back in a jiffy." The meaning of that phrase was, roughly, "I'll be back very soon." So "jiff" refers to some "short span of time." Since "jiff" was shorter than "jiffy," that's what I went with.
Jiff is pronounced like "gif" with a soft "g," as in "gem."
At the time of writing, there are four existing prominent datetime libraries
for Rust: chrono
, time
, hifitime
and icu
. "Comparison with
other Rust datetime crates" goes over the "facts of comparison"
between the crates (mostly for just chrono
and time
), but it intentionally
leaves out value judgments. To answer this question, I have to introduce value
judgments and my opinions on existing libraries. These are opinions on which
reasonable people can disagree. Moreover, since these are opinions meant to
justify an alternative, these opinions tend to be oriented on the faults (as
this author sees them) in existing crates as opposed to their benefits.
Broadly speaking, my view on Rust datetime libraries was that they had reached a local maximum, and there didn't seem to be much movement toward breaking out of that local maximum and getting to something that was categorically better. Much of my thoughts revolve around not just the functionality provided, but also the API design of these crates. Thus, I perceived a gap in the ecosystem that I felt like I could fill.
I'll share my brief thoughts on each crate. I'll cover "why not contribute to an existing library and make it better" in the next question.
In my view, Chrono is the closest to Jiff in terms of the functionality it provides. It has some support for DST safe arithmetic for example, and even some support for doing calendar math. But its support is incomplete. While one can add units of days to Chrono datetimes, Chrono lacks the ability to do math on multiple calendar units at the same time. Moreover, Chrono cannot produce calendar durations between two time zone aware datetimes.
Moreover, Chrono's integration with the IANA Time Zone Database is somewhat
spotty. Support for it isn't included in the chrono
crate itself, but
is instead something you need to opt into with additional crates, such
as chrono-tz
or tzfile
. And each crate comes with its own set of
trade-offs. chrono-tz
embeds the entire database into your binary and makes
the time zones available at compile time, while tzfile
reads the database
from your system's copy of the database (i.e., /usr/share/zoneinfo
on Unix).
In my view, this creates a difficult situation for non-expert users of Chrono
where the "right" choice isn't obvious. In my opinion, the default should be
to read time zone transition data from the system's copy of the database on
disk, and only bundle the data into the binary as a last resort when a copy of
the database isn't reliably available (like on Windows). The main reason for
this is that the database is frequently updated (a few times each year) since
the rules for time zone transitions can change. If that time zone data is
embedded into your binary, then you need to 1) wait for chrono-tz
to update
their data and 2) re-compile your application and ship it out to users.
In contrast, Jiff abstracts the method of time zone data discovery. That is, there are no API differences between "read time zone data from the system database" and "read embedded time zone data." Of course, Jiff provides options to choose which method to use (like forcing bundling on Unix), but all of this is transparent to users of Jiff itself. That is, Jiff tries to do the right thing by default. There's no need to go off and find a different crate to handle time zone data for you.
While on the topic of time zones, Chrono also has no support for serializing
IANA time zone identifiers. This implies that if you have a time zone aware
datetime in Chrono (whether by chrono-tz
or tzfile
), then you can't
losslessly serialize and deserialize it. Namely, serialization will lose
the time zone the datetime is associated with, and instead only include
the offset. Then when it's deserialized, you're left with an offset-only
datetime that won't, for example, provide DST safe arithmetic based on the
original time zone. In contrast, Jiff follows RFC 9557 to support embedding
IANA time zone identifiers in the serialized representation. For example,
2024-07-21T17:11-04[America/New_York]
.
There are a variety of other things that Jiff supports which Chrono does not, but that's covered in the comparison section between Jiff and Chrono.
As for API design, I found Chrono's API to be overengineered and difficult to deal with. This is a frustratingly vague complaint, but here are some things that I believe contribute to that opinion:
- I've used Chrono for various things, and I found it very difficult to figure out from its API what the right operations to use were.
- Chrono has been steadily deprecating huge portions of its API in favor of fallible routines. I find the resulting documentation difficult to read and the naming to be very clunky.
- The fallible routines return a mixture of
Option<T>
andResult<T, E>
. This is frustrating in my experience because theOption<T>
usually needs to be converted to a human readable error message, and I think the library should make use of its contextual information to do this for you. Indeed, Jiff rarely returnsOption<T>
and instead returnsResult<T, E>
with a contextualized human readable error message. (Although I'm sure the error messages could use a lot of improvement.) - I find the use of generics in Chrono to be overengineered. It has some
key benefits, for example, making the notion of "time zone" an open concept
that can be defined by users of the crate. But in my opinion, this is rarely
needed. The Chrono crate ecosystem makes use of this via the
chrono-tz
andtzfile
crates, but Jiff covers both of those use cases (broadly speaking) automatically. - Chrono overall puts a large emphasis on "fixed offset" datetimes, but these are rarely the right abstraction to use. It's possible this is due to the fact that IANA time zone support is external to Chrono itself. Instead, Jiff just tries to do the right thing by default, and gives users IANA time zone support out of the box. Jiff does support fixed offset datetimes as well, but they are de-emphasized.
- I find generic traits like
Datelike
to also be very confusing because they split the APIs of types likeNaiveDate
into concrete methods and generic methods, and there's no obvious rhyme or reason as to how those methods are split up. - Chrono's API doesn't offer clean on-ramps from civil ("naive") datetimes to
time zone aware datetimes. For example, Chrono provides
NaiveDate::succ_opt
to get the next day, but this method isn't available onNaiveDateTime
orDateTime
. Instead, to implement it correctly onDateTime
, you have to get the naive date, get the date for tomorrow viasucc_opt
, and then convert it back to the sameNaiveDateTime
and finally make it time zone aware by applying the original time zone to it. In contrast, in Jiff, it's just a matter of callingZoned::tomorrow
. Indeed, (almost) any method you can call oncivil::Date
in Jiff is also available oncivil::DateTime
and likewise forZoned
. This makes transitioning between datetime types very easy. Chrono almost appears to provide this same experience via traits likeDatelike
, but doesn't fully commit and, in my opinion, the end result has a feeling of arbitrariness to it. - Chrono lacks a standard timestamp type. You can approximate this with a
DateTime<Utc>
, but it's a more complicated type that includes a full datetime representation. In contrast, Jiff provides aTimestamp
type for when you just want the number of seconds from the Unix epoch. And then integrates it with the rest of the datetime types in a consistent way.
My main gripe with the time
crate is that it has no IANA Time Zone Database
support at all. This means it cannot do DST safe arithmetic. Consequently, it
emphasizes the use of "fixed offset" datetimes in a similar fashion as Chrono,
except time
does not provide any extension mechanism like a TimeZone
trait.
In my view, fixed offset datetimes are rarely the right thing to use. In my
opinion, this makes writing correct datetime code with the time
crate rather
difficult. In contrast, Jiff provides full IANA Time Zone Database support,
and it should be very rare to need fixed offset datetimes (although Jiff does
support them via TimeZone::fixed
).
The time
crate also, at present, relies on unsound libc
APIs for
determining the current time zone offset, but makes them sound by requiring (by
default) that UtcOffset::current_local_offset
is only called when there is
only 1 thread. This is a result of the libc
APIs accessing the environment
in a way that is unsynchronized with Rust's standard library access to the
environment. This is likely a temporary limitation, but at time of writing,
this state has persisted for quite some time already. In contrast, Jiff detects
the current time zone of the platform on its own without use of libc
, and
thus sidesteps this issue. (This is also what Chrono does.)
I overall find the API of time
to be easier to understand than Chrono, likely
because there are fewer generics. But time
is also supporting a lot less than
Chrono and Jiff (because of the missing time zone support).
As with Chrono, I've done a
more detailed comparison between Jiff and time
.
hifitime
is more of a specialized datetime library suited to scientific
applications, and so while Jiff and hifitime
have overlapping use cases,
hifitime
fundamentally has a different target demographic than Jiff. As noted
in a comparison between Jiff and hifitime
, hifitime
doesn't have any time zone support, but it does support conversions between
many different time scales and leap second support. Leap second support, in
this context, means that the durations between two points in time take leap
seconds into account in hifitime
, but Jiff pretends as if they don't exist.
In terms of building a new datetime library, I felt like hifitime
wasn't
really targeting the "general purpose datetime library" use case that I felt
chrono
and time
were. And so, whether it existed or not didn't really
impact whether another general purpose datetime library should be built.
icu
is, as I understand it, still under construction with respect to datetime
support. For example, it doesn't have IANA Time Zone Database support.
But, it does support locale aware formatting of datetimes and non-Gregorian
calendars.
When I started working on Jiff, I didn't have a good understanding of what the
icu
crate offers. I still don't really. In part because the API is difficult
for me to understand and in part because I haven't dedicated a ton of time to
studying its API. But either way, I don't think it is currently in a position
to be a general purpose datetime library and I wasn't clear on what its goals
were.
Since I haven't spent a lot of time with icu
, I didn't have much to say about
it in my comparison with it and Jiff.
Given that Rust already has at least two prominent datetime libraries, doesn't adding another one just make things worse? And why not contribute to an existing library to make it better instead of starting over from scratch?
I first want to say that I acknowledge that throwing a new crate into the ecosystem, and adding yet another choice, does actually come with downsides. There is a cost to having too many choices, and when possible, I do believe it is better to improve an existing project rather than start a new one.
Improving existing projects can be difficult. Jiff has a different design than
both chrono
and time
. Evolving either one of those crates into what Jiff is
would, in my view, require a huge amount of resources. Not just in time, but in
social capital as well. Because it wouldn't be greenfield development done by
one person making all of the design choices, but instead someone from outside
the project trying to convince the maintainers of established projects to move
in a radically different direction. I know what it's like to be on the side of
maintaining an established API for a library with a lot of users. There is a
huge inertial cost to making sweeping API changes.
Moreover, when I started Jiff, I was not a domain expert in datetime libraries
or datetime handling in general. Therefore, my opinion that chrono
and time
could be better would arguably not carry a lot of weight. It was only through
the process of actually building a datetime library did I learn enough to form
nuanced opinions about the status quo. When I started, my opinions were much
more vague (but still strong enough to start this project).
On top of all of this, I was intrinsically motivated to work on this problem. I found it very interesting, and especially because I perceived there to be a gap in the ecosystem that I thought I could fill in. I had a vision for what a datetime library should look like. And it took a lot of iteration to get from my initial vision to something that works in practice. Doing this on an existing datetime library with real users would be extremely difficult.
And speaking as someone who has had folks publish better versions of some of my own crates, I know what it's like to be on the other end of this. Sometimes you just have to start fresh.
Here's a list. More may be added in the future:
The two duration types provided by Jiff are Span
and SignedDuration
. A
SignedDuration
is effectively identical to a std::time::Duration
, but it's
signed instead of unsigned. A Span
is also a duration type, but is likely
different than most other duration types you've used before.
While a SignedDuration
can be thought of as a single integer corresponding
to the number of nanoseconds between two points in time, a Span
is a
collection of individual unit values that combine to represent the difference
between two point in time. Stated more concretely, while the spans 2 hours
and 120 minutes
both correspond to the same duration of time, when
represented as a Jiff Span
, they correspond to two distinct values in
memory. This is something that is fundamentally not expressible by a type
like SignedDuration
, where 2 hours
and 120 minutes
are completely
indistinguishable.
One of the key advantages of a Span
is that it can represent units of
non-uniform length. For example, not every month has the same number of days,
but a Span
can still represent units of months because it tracks the values
of each unit independently. For example, Jiff is smart enough to know that the
difference between 2024-03-01
and 2024-04-01
is the same number of months
as 2024-04-01
and 2024-05-01
, even though the number of days is different:
use jiff::{civil::date, ToSpan, Unit};
fn main() -> anyhow::Result<()> {
let date1 = date(2024, 3, 1);
let date2 = date(2024, 4, 1);
let date3 = date(2024, 5, 1);
// When computing durations between `Date` values,
// the spans default to days.
assert_eq!(date1.until(date2)?, 31.days());
assert_eq!(date2.until(date3)?, 30.days());
// But we can request bigger units!
assert_eq!(date1.until((Unit::Month, date2))?, 1.month());
assert_eq!(date2.until((Unit::Month, date3))?, 1.month());
Ok(())
}
While most folks are very in tune with the fact that years and months have
non-uniform length, a less obvious truth is that days themselves also have
non-uniform length in the presence of time zones. For example, 2024-03-10
in
America/New_York
was only 23 hours long (the region entered daylight saving
time, creating a gap in time), while 2024-11-03
was 25 hours long (the region
left daylight saving time, creating a fold in time). Being unaware of this
corner case leads to folks assuming that "1 day" and "24 hours" are always
exactly equivalent. But they aren't. The design of Jiff leans into this and
ensures that so long as you're using Span
to encode a concept of days and are
doing arithmetic with it on Zoned
values, then you can never get it wrong.
Jiff will always take time zones into account when dealing with units of days
or bigger.
The design of Span
comes from Temporal, which uses only one duration
type. From that issue, there are some significant
advantages to using a Span
. In my own words:
- It more closely lines up with ISO 8601 durations, which themselves combine calendar and clock units.
- With a
Span
, it is very easy to move between5 years 2 months
and the number of hours in that same span. - Jiff's
Span
type specifically represents each unit as distinct from the others. In contrast, most absolute duration types (likestd::time::Duration
and Jiff's ownSignedDuration
), are "just" a 96-bit integer number of nanoseconds. This means that, for example,1 hour 30 minutes
is impossible to differentiate from90 minutes
. But depending on the use case, you might want one or the other. Jiff'sSpan
design (copied from Temporal) enables users to express durations in whatever units they want. And this expression can be manipulated via APIs likeSpan::round
in intuitive ways.
A SignedDuration
is still useful in some respects. For example, when you
need tighter integration with the standard library's std::time::Duration
(since a SignedDuration
is the same, but just signed), or when you need
better performance than what Span
gives you. In particular, since a Span
keeps track of the values for each individual unit, it is a much heavier type
than a SignedDuration
. It uses up more stack space and also required more
computation to do arithmetic with it.
First, let's start by explaining what a TimeZone
is. In Jiff, a TimeZone
is a concrete type that cannot be extended by users of Jiff. Instead, users of
Jiff are forced to use one of three different kinds of time zones:
- A "fixed offset" time zone where the civil time for any particular instant
is computed by simply adding or subtracting a fixed number of seconds from UTC.
The
TimeZone::fixed
constructor enables callers to build time zones with any offset within the limits imposed by Jiff. - A POSIX time zone, typically set via the
TZ
environment variable. These are rarely used by end users, but do provide a way to specify a rule for when daylight saving time transitions occur thoughtout the year. (But it does not support historical transitions that might not conform to the current rule.) TheTimeZone::posix
constructor enables callers to build aTimeZone
with a POSIX time zone string. - TZif formatted data, usually from the
IANA Time Zone Database. This data contains historical time zone transitions
in addition to rules governing the future in the form of POSIX TZ strings.The
TimeZone::tzif
constructor enables callers to build aTimeZone
with any TZif formatted data.
The jiff::tz::TimeZoneDatabase
automatically looks for TZif formatted
files in your system's copy of the IANA Time Zone Database, usually at
/usr/share/zoneinfo
. (On Windows, Jiff embeds a copy of the IANA Time Zone
Database into the compiled artifact itself.)
So why isn't TimeZone
a trait? Well, the above formulation should cover the
vast majority of use cases. And even if that doesn't cover everything, it
is possible for callers to use TimeZone::tzif
to construct arbitrary time
zones by building their own TZif data. This is a somewhat large hurdle though,
so if this is something that is commonly needed, I'm open to exploring other
options for building custom time zones. For example, perhaps we introduce a way
to describe time zone transitions in Rust code that can then be used to build a
TimeZone
directly. But, the benefit of TZif is that it is inter-operable and
a standard. There are tools that can build them.
An important thing to note here is that I actually approach questions like
"Why isn't TimeZone
a trait?" as instead "Why should TimeZone
be a trait?"
In particular, I personally perceive costs to introducing generics, especially
on a fundamental type in the crate. For example, if TimeZone
were a trait,
then Zoned
would not be a concrete type. It would be generic over a type
parameter that implements the TimeZone
trait. This in turn implies that
anyone using a Zoned
in their own types or APIs needs to think about the
TimeZone
trait and likely incorporate it into their own type signatures. This
is because a TimeZone
trait implies an open system that infects everything
it touches. The complexity isn't contained. But a concrete TimeZone
type,
like what Jiff has, encapsulates everything there is about time zones.
(Making Zoned
generic over a type parameter with a default type does
contain the complexity in some cases, but not all. I explored using default
type parameters in Jiff for supporting leap seconds, and
it was overall quite awkward in my opinion.)
The trade off is that we do give up some flexibility. For example, Chrono uses
a TimeZone
trait design. This enables external crates to provide their own
implementations of the TimeZone
trait. But the two principle instances of
this occurring, chrono-tz
and tzfile
, are both supported by Jiff itself.
With that said, one key advantage of Chrono's design is that it permits its
zone aware datetime type (DateTime<T>
) to be Copy
if T
is Copy
. It is
somewhat difficult (although not literally impossible) to make a TZif-backed
time zone implement Copy
, but a reference to it is Copy
and a reference
to it can still implement Chrono's TimeZone
trait. (And indeed, this is what
the tzfile
crate does.) This ultimately leads to more flexibility compared
to Jiff, where its Zoned
type embeds a TimeZone
and a TimeZone
cannot
easily be made Copy
without giving up something else.
My opinions on the costs of generics tend to overestimate them compared to many
others in my experience, so your mileage may vary on where you land on this
issue. Buy in my opinion, being able to just write Zoned
as a concrete type
without any generics is a huge win for comprehensibility.
When initially setting out to build Jiff, I really wanted the TimeZone
type
to implement Copy
. The reason why I wanted it to implement Copy
is because
I wanted all datetime types to have "plain old data" semantics. That is, I want
callers to think of them as small immutable bits of data that can be freely
copied without worry. This makes APIs a little nicer because you can ask for a
Zoned
instead of a &Zoned
, assuming the Zoned
type is small enough.
But, in order for Zoned
to be Copy
, it must be the case that TimeZone
is
Copy
. This is because Zoned
embeds a TimeZone
. Indeed, this is the reason
for its existence: it couples an instant in time with a particular geographic
region. This makes it possible to expose very ergonomic high level APIs that
don't require the caller to keep passing in a TimeZone
value repeatedly.
So, how can a TimeZone
be Copy
? Well, both fixed offset and POSIX time
zones could be Copy
. There's no huge challenge in that. (Internally, a POSIX
time zone is not currently Copy
because there's no reason for it given what
we're about to discuss.) The main challenge is the TZif-backed time zone. TZif
formatted data can contain an arbitrary number of time zone transitions. There
is just no way to avoid some kind of dynamic memory allocation.
Since we need dynamic memory allocation, there is really only one way to
make it Copy
at this point: introduce some kind of caching mechanism with
a small Copy
identifier that lets us "look up" the time zone in some kind
of global or thread-local cache. I thought a lot about how I might wire this
together, and I could not come up with a satisfactory design. I believe
garbage collection is the main challenge, but also synchronization overhead
for accessing a time zone is likely also a problem. The nice benefit of the
TimeZone
type as it exists now is that it's just data. While getting a
TimeZone
from a TimeZoneDatabase
might require synchronization and possibly
even I/O if the cache for it was invalidated, a TimeZone
itself requires no
synchronization whatsoever to use it. It is just an Arc
internally to make it
pointer-sized.
Some datetime libraries have multiple different zone aware datetime types. Jiff
opts to have just one, and embeds support for the different types of time zones
that most people will ever need into that one type. Jiff does this via the
TimeZone
type, which can be a fixed offset, a POSIX time zone or TZif-backed
(usually from the IANA Time Zone Database).
The short summary is that the use cases for leap second support are rather limited, and the effect they have on overall API complexity is quite large. That is, I believe they would make the API of Jiff more complicated than the value they bring to the domain.
A standard work-around for part of the leap second problem---and usually the
one people care about---is to use custom TZif data that describes when each of
the leap seconds occurs. In effect, you can treat TAI as its own time zone.
This enables callers to compute accurate durations of time that span a leap
second (positive or negative). And indeed, so long as you build that TZif
data, Jiff supports this via TimeZone::tzif
.
I wrote a lot more about leap seconds on the issue tracker.
The omission of std::time::Duration
from the jiff 0.1.0
release was quite
intentional. Firstly, a Duration
is an absolute time, meaning there is no way
to distinguish between 3600 seconds
and 1 hour
. Secondly, a Duration
is
unsigned, which makes some API interactions awkward.
It is likely that there will eventually be at least some integration points
with std::time::Duration
. This issue discusses some
of the challenges.
The ZonedDifference
and ZonedRound
are builders for expressing the
parameters to the functions Zoned::{since, until}
and Zoned::round
,
respectively. For example, this:
use jiff::{civil::date, ToSpan};
let zdt1 = date(2024, 7, 16).at(22, 3, 23, 0).intz("America/New_York")?;
let zdt2 = date(2024, 8, 16).at(22, 3, 59, 0).intz("America/New_York")?;
assert_eq!(zdt1.until(&zdt2)?, 744.hours().seconds(36));
# Ok::<(), Box<dyn std::error::Error>>(())
Is equivalent to:
use jiff::{civil::date, ToSpan, ZonedDifference};
let zdt1 = date(2024, 7, 16).at(22, 3, 23, 0).intz("America/New_York")?;
let zdt2 = date(2024, 8, 16).at(22, 3, 59, 0).intz("America/New_York")?;
assert_eq!(zdt1.until(ZonedDifference::new(&zdt2))?, 744.hours().seconds(36));
# Ok::<(), Box<dyn std::error::Error>>(())
The point of this is that ZonedDifference
permits specifying additional
configuration. For example, rounding the span returned:
use jiff::{civil::date, ToSpan, Unit, ZonedDifference};
let zdt1 = date(2024, 7, 16).at(22, 3, 23, 0).intz("America/New_York")?;
let zdt2 = date(2024, 8, 16).at(22, 3, 59, 0).intz("America/New_York")?;
assert_eq!(
zdt1.until(ZonedDifference::new(&zdt2).smallest(Unit::Minute))?,
744.hours(),
);
# Ok::<(), Box<dyn std::error::Error>>(())
By using a dedicated type to represent the parameters, we can enable ergonomic
uses of the API for common cases (by using From<&Zoned> for ZonedDifference
trait implementations) while still permitting callers to provide additional
configuration.
An alternative API would be to remove the additional parameters to
Zoned::until
, and instead require callers to do span rounding themselves
explicitly. But this is more verbose and requires repeating the correct
zoned datetime to indicate how to interpret non-uniform units. For example,
instead of this:
use jiff::{civil::date, ToSpan, Unit, ZonedDifference};
let zdt1 = date(2024, 7, 16).at(22, 3, 23, 0).intz("America/New_York")?;
let zdt2 = date(2024, 8, 16).at(22, 3, 59, 0).intz("America/New_York")?;
let diff = ZonedDifference::new(&zdt2)
.largest(Unit::Month)
.smallest(Unit::Minute);
assert_eq!(zdt1.until(diff)?, 1.month());
# Ok::<(), Box<dyn std::error::Error>>(())
One would need to do this:
use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
let zdt1 = date(2024, 7, 16).at(22, 3, 23, 0).intz("America/New_York")?;
let zdt2 = date(2024, 8, 16).at(22, 3, 59, 0).intz("America/New_York")?;
let span = zdt1.until(&zdt2)?;
let rounded = span.round(
SpanRound::new()
.largest(Unit::Month)
.smallest(Unit::Minute)
.relative(&zdt1)
.mode(RoundMode::Trunc),
)?;
assert_eq!(rounded, 1.month());
# Ok::<(), Box<dyn std::error::Error>>(())
This is somewhat fiddly and easy to get wrong. Moreover, the Zoned::until
API, when rounding is enabled, will automatically use RoundMode::Trunc
, since
this is what usually expects when computing the span between two datetimes. But
span rounding uses RoundMode::HalfExpand
by default, corresponding to how you
were likely taught to round in school. (Rounds to the nearest unit, with ties
breaking away from zero.)
Similar reasoning applies to other "parameter builder" types like
civil::DateTimeDifference
as well.
The main reason why is because of the existence of std::time::Instant
. While
that doesn't in and of itself prevent Jiff from using the name Instant
, it
creates a naming clash with something that is similar but different. Namely,
a Jiff Timestamp
corresponds to a time from your system clock, where as an
Instant
represents monotonic time. The system clock might change or even
go backwards, where as a monotonic instant will always produce time that is
greater than or equal to a previous time.
An Instant
is, for example, something you might use to measure the time
something takes in a program. Like in capturing a measurement for a benchmark.
Conversely, a Timestamp
is something you use to represent time as represented
by the system. In particular, a Timestamp
is like a std::time::SystemTime
and not a std::time::Instant
.
While Temporal uses the name Instant
for their equivalent of Jiff's
Timestamp
type, using the name Instant
in Jiff would likely result in
serious confusion and conflicts in names when someone wants to use both an
Instant
and a Timestamp
in the same namespace.