jiff/
lib.rs

1/*!
2Jiff is a datetime library for Rust that encourages you to jump into the pit
3of success. The focus of this library is providing high level datetime
4primitives that are difficult to misuse and have reasonable performance.
5
6Jiff takes enormous inspiration from [Temporal], which is a [TC39] proposal to
7improve datetime handling in JavaScript.
8
9Here is a quick example that shows how to parse a typical RFC 3339 instant,
10convert it to a zone aware datetime, add a span of time and losslessly print
11it:
12
13```
14use jiff::{Timestamp, ToSpan};
15
16let time: Timestamp = "2024-07-11T01:14:00Z".parse()?;
17let zoned = time.in_tz("America/New_York")?.checked_add(1.month().hours(2))?;
18assert_eq!(zoned.to_string(), "2024-08-10T23:14:00-04:00[America/New_York]");
19// Or, if you want an RFC3339 formatted string:
20assert_eq!(zoned.timestamp().to_string(), "2024-08-11T03:14:00Z");
21
22# Ok::<(), Box<dyn std::error::Error>>(())
23```
24
25[TC39]: https://tc39.es/
26[Temporal]: https://tc39.es/proposal-temporal/docs/index.html
27
28# Overview
29
30The primary type in this crate is [`Zoned`]. A `Zoned` value is a datetime that
31corresponds to a precise instant in time in a particular geographic region.
32Users of this crate may find it helpful to think of a `Zoned` as a triple of
33the following components:
34
35* A [`Timestamp`] is a 96-bit integer of nanoseconds since the [Unix epoch].
36A timestamp is a precise instant in time.
37* A [`civil::DateTime`] is an inexact calendar date and clock time. The terms
38"civil", "local", "plain" and "naive" are all used in various places to
39describe this same concept.
40* A [`tz::TimeZone`] is a set of rules for determining the civil time, via an
41offset from [UTC], in a particular geographic region.
42
43All three of these components are used to provide convenient high level
44operations on `Zoned` such as computing durations, adding durations and
45rounding.
46
47A [`Span`] is this crate's primary duration type. It mixes calendar and clock
48units into a single type. Jiff also provides [`SignedDuration`], which is like
49[`std::time::Duration`], but signed. Users should default to a `Span` for
50representing durations when using Jiff.
51
52[Unix epoch]: https://en.wikipedia.org/wiki/Unix_time
53[UTC]: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
54
55The remainder of this documentation is organized as follows:
56
57* [Features](#features) gives a very brief summary of the features Jiff does
58and does not support.
59* [Usage](#usage) shows how to add Jiff to your Rust project.
60* [Examples](#examples) shows a small cookbook of programs for common tasks.
61* [Crate features](#crate-features) documents the Cargo features that can be
62enabled or disabled for this crate.
63
64Also, the `_documentation` sub-module serves to provide longer form
65documentation:
66
67* [Comparison with other Rust datetime crates](crate::_documentation::comparison)
68* [The API design rationale for Jiff](crate::_documentation::design)
69* [Platform support](crate::_documentation::platform)
70* [CHANGELOG](crate::_documentation::changelog)
71
72# Features
73
74Here is a non-exhaustive list of the things that Jiff supports:
75
76* Automatic and seamless integration with your system's copy of the
77[IANA Time Zone Database]. When a platform doesn't have a time zone database,
78Jiff automatically embeds a copy of it.
79* A separation of datetime types between absolute times ([`Timestamp`] and
80[`Zoned`]) and civil times ([`civil::DateTime`]).
81* Nanosecond precision.
82* Time zone and daylight saving time aware arithmetic.
83* The primary duration type, [`Span`], mixes calendar and clock
84units to provide an all-in-one human friendly experience that is time zone
85aware.
86* An "absolute" duration type, [`SignedDuration`], is like
87[`std::time::Duration`] but signed.
88* Datetime rounding.
89* Span rounding, including calendar units and including taking time zones into
90account.
91* Formatting and parsing datetimes via a Temporal-specified hybrid format
92that takes the best parts of [RFC 3339], [RFC 9557] and [ISO 8601]. This
93includes lossless round tripping of zone aware datetimes and durations.
94* Formatting and parsing according to [RFC 2822].
95* Formatting and parsing via routines similar to [`strftime`] and [`strptime`].
96* Formatting and parsing durations via a bespoke
97["friendly" format](crate::fmt::friendly), with Serde support, that is meant
98to service the [`humantime`](https://docs.rs/humantime) use cases.
99* Opt-in Serde integration.
100* Full support for dealing with ambiguous civil datetimes.
101* Protection against deserializing datetimes in the future with an offset
102different than what is possible with your copy of the Time Zone Database.
103(This is done via [`tz::OffsetConflict`].)
104* APIs that panic by design are clearly documented as such and few in number.
105Otherwise, all operations that can fail, including because of overflow,
106return a `Result`.
107
108Here is also a list of things that Jiff doesn't currently support, along with
109a link to a relevant issue (if one exists).
110
111* [Leap seconds]. (Jiff will automatically constrain times like `23:59:60` to
112`23:59:59`.)
113* Time scales other than Unix.
114* [Calendars other than Gregorian].
115* [Localization].
116* [Changing the representation size, precision or limits on the minimum and
117maximum datetime values.][cppchrono]
118* [Jiff aims to have reasonable performance and may not be capable of doing the
119fastest possible thing.][perf]
120
121At present, it is recommended to use the [`icu`] crate via [`jiff-icu`] for
122localization and non-Gregorian use cases.
123
124[Leap seconds]: https://github.com/BurntSushi/jiff/issues/7
125[Calendars other than Gregorian]: https://github.com/BurntSushi/jiff/issues/6
126[Localization]: https://github.com/BurntSushi/jiff/issues/4
127[cppchrono]: https://github.com/BurntSushi/jiff/issues/3
128[perf]: https://github.com/BurntSushi/jiff/issues/17
129[`icu`]: https://docs.rs/icu
130[`jiff-icu`]: https://docs.rs/jiff-icu
131
132Please file an issue if you can think of more (substantial) things to add to
133the above list.
134
135[IANA Time Zone Database]: https://en.wikipedia.org/wiki/Tz_database
136[RFC 3339]: https://www.rfc-editor.org/rfc/rfc3339
137[RFC 9557]: https://www.rfc-editor.org/rfc/rfc9557.html
138[ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
139
140# Usage
141
142Jiff is [on crates.io](https://crates.io/crates/jiff) and can be
143used by adding `jiff` to your dependencies in your project's `Cargo.toml`.
144Or more simply, just run `cargo add jiff`.
145
146Here is a complete example that creates a new Rust project, adds a dependency
147on `jiff`, creates the source code for a simple datetime program and then runs
148it.
149
150First, create the project in a new directory:
151
152```text
153$ cargo new jiff-example
154$ cd jiff-example
155```
156
157Second, add a dependency on `jiff`:
158
159```text
160$ cargo add jiff
161```
162
163Third, edit `src/main.rs`. Delete what's there and replace it with this:
164
165```
166use jiff::{Unit, Zoned};
167
168fn main() -> Result<(), jiff::Error> {
169    let now = Zoned::now().round(Unit::Second)?;
170    println!("{now}");
171    Ok(())
172}
173```
174
175Fourth, run it with `cargo run`:
176
177```text
178$ cargo run
179   Compiling jiff v0.2.0 (/home/andrew/rust/jiff)
180   Compiling jiff-play v0.2.0 (/home/andrew/tmp/scratch/rust/jiff-play)
181    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.37s
182     Running `target/debug/jiff-play`
1832024-07-10T19:54:20-04:00[America/New_York]
184```
185
186The first time you run the program will show more output like above. But
187subsequent runs shouldn't have to re-compile the dependencies.
188
189# Examples
190
191* [Get the current time in your system's time zone](#get-the-current-time-in-your-systems-time-zone)
192* [Print the current time rounded to the nearest second](#print-the-current-time-rounded-to-the-nearest-second)
193* [Print today's date at a specific time](#print-todays-date-at-a-specific-time)
194* [Print the current Unix timestamp](#print-the-current-unix-timestamp)
195* [Print the datetime for a timestamp](#print-the-datetime-for-a-timestamp)
196* [Create a zoned datetime from civil time](#create-a-zoned-datetime-from-civil-time)
197* [Change an instant from one time zone to another](#change-an-instant-from-one-time-zone-to-another)
198* [Find the duration between two zoned datetimes](#find-the-duration-between-two-zoned-datetimes)
199* [Add a duration to a zoned datetime](#add-a-duration-to-a-zoned-datetime)
200* [Dealing with ambiguity](#dealing-with-ambiguity)
201* [Parsing a span](#parsing-a-span)
202* [Parsing an RFC 2822 datetime string](#parsing-an-rfc-2822-datetime-string)
203* [Using `strftime` and `strptime` for formatting and parsing](#using-strftime-and-strptime-for-formatting-and-parsing)
204* [Serializing and deserializing integer timestamps with Serde](#serializing-and-deserializing-integer-timestamps-with-serde)
205
206### Get the current time in your system's time zone
207
208The [`Zoned::now`] returns your system's time and also attempts
209to automatically find your system's default time zone via
210[`tz::TimeZone::system`]:
211
212```
213use jiff::Zoned;
214
215let now = Zoned::now();
216println!("{now}");
217// Output: 2024-07-10T17:09:28.168146054-04:00[America/New_York]
218```
219
220### Print the current time rounded to the nearest second
221
222This uses the [`Zoned::round`] API to round a zoned datetime to the nearest
223second. This is useful, for example, if you don't care about fractional
224seconds:
225
226```
227use jiff::{Unit, Zoned};
228
229let now = Zoned::now().round(Unit::Second)?;
230println!("{now}");
231// Output: 2024-07-10T17:09:28-04:00[America/New_York]
232# Ok::<(), Box<dyn std::error::Error>>(())
233```
234
235### Print today's date at a specific time
236
237Let's say you want to get the current date at 2pm. Here's one way of doing it
238that makes use of [`Zoned::with`]:
239
240```
241use jiff::Zoned;
242
243let zdt = Zoned::now().with()
244    .hour(14)
245    .minute(0)
246    .second(0)
247    .subsec_nanosecond(0)
248    .build()?;
249println!("{zdt}");
250// Output: 2024-07-12T14:00:00-04:00[America/New_York]
251# Ok::<(), Box<dyn std::error::Error>>(())
252```
253
254Or, if the time is known to be valid, you can use the infallibe
255[`civil::time`](civil::time()) convenience constructor:
256
257```
258use jiff::{civil::time, Zoned};
259
260let zdt = Zoned::now().with().time(time(14, 0, 0, 0)).build()?;
261println!("{zdt}");
262// Output: 2024-07-12T14:00:00-04:00[America/New_York]
263# Ok::<(), Box<dyn std::error::Error>>(())
264```
265
266You can eliminate the possibility of a panic at runtime by using `time` in
267a `const` block:
268
269```
270use jiff::{civil::time, Zoned};
271
272let zdt = Zoned::now().with().time(const { time(14, 0, 0, 0) }).build()?;
273println!("{zdt}");
274// Output: 2024-07-12T14:00:00-04:00[America/New_York]
275# Ok::<(), Box<dyn std::error::Error>>(())
276```
277
278### Print the current Unix timestamp
279
280This prints a Unix timestamp as the number of seconds since the Unix epoch
281via [`Timestamp::now`]:
282
283```
284use jiff::Timestamp;
285
286let now = Timestamp::now();
287println!("{}", now.as_second());
288// Output: 1720646365
289```
290
291Or print the current timestamp to nanosecond precision (which is the maximum
292supported by Jiff):
293
294```
295use jiff::Timestamp;
296
297let now = Timestamp::now();
298println!("{}", now.as_nanosecond());
299// Output: 1720646414218901664
300```
301
302### Print the datetime for a timestamp
303
304This example shows how to convert a Unix timestamp, in milliseconds, to
305a zoned datetime in the system's current time zone. This utilizes the
306[`Timestamp::from_millisecond`] constructor, [`tz::TimeZone::system`] to get
307the default time zone and the [`Timestamp::to_zoned`] routine to convert a
308timestamp to a zoned datetime.
309
310```
311use jiff::{tz::TimeZone, Timestamp};
312
313let ts = Timestamp::from_millisecond(1_720_646_365_567)?;
314let zdt = ts.to_zoned(TimeZone::system());
315println!("{zdt}");
316// Output: 2024-07-10T17:19:25.567-04:00[America/New_York]
317// Or if you just want the RFC 3339 time without bothering with time zones:
318assert_eq!(ts.to_string(), "2024-07-10T21:19:25.567Z");
319
320# Ok::<(), Box<dyn std::error::Error>>(())
321```
322
323### Create a zoned datetime from civil time
324
325This example demonstrates the convenience constructor, [`civil::date`],
326for a [`civil::Date`]. And use the [`civil::Date::at`] method to create
327a [`civil::DateTime`]. Once we have a civil datetime, we can use
328[`civil::DateTime::in_tz`] to do a time zone lookup and convert it to a precise
329instant in time:
330
331```
332use jiff::civil::date;
333
334let zdt = date(2023, 12, 31).at(18, 30, 0, 0).in_tz("America/New_York")?;
335assert_eq!(zdt.to_string(), "2023-12-31T18:30:00-05:00[America/New_York]");
336
337# Ok::<(), Box<dyn std::error::Error>>(())
338```
339
340Note that [`civil::date`] should only be used for inputs that are known to be
341correct since it panics for an invalid date. If your date isn't known to be
342valid, then use the fallible [`civil::Date::new`] constructor.
343
344### Change an instant from one time zone to another
345
346This shows how to find the civil time, in New York, when World War 1 ended:
347
348```
349use jiff::civil::date;
350
351let zdt1 = date(1918, 11, 11).at(11, 0, 0, 0).in_tz("Europe/Paris")?;
352let zdt2 = zdt1.in_tz("America/New_York")?;
353assert_eq!(
354    zdt2.to_string(),
355    "1918-11-11T06:00:00-05:00[America/New_York]",
356);
357
358# Ok::<(), Box<dyn std::error::Error>>(())
359```
360
361### Find the duration between two zoned datetimes
362
363This shows how to compute a span between two zoned datetimes. This utilizes
364a [`Zoned`]'s implementation for `Sub`, permitting one to subtract two zoned
365datetimes via the `-` operator:
366
367```
368use jiff::civil::date;
369
370let zdt1 = date(2020, 8, 26).at(6, 27, 0, 0).in_tz("America/New_York")?;
371let zdt2 = date(2023, 12, 31).at(18, 30, 0, 0).in_tz("America/New_York")?;
372let span = &zdt2 - &zdt1;
373assert_eq!(format!("{span:#}"), "29341h 3m");
374
375# Ok::<(), Box<dyn std::error::Error>>(())
376```
377
378The above returns no units bigger than hours because it makes the operation
379reversible in all cases. But if you don't need reversibility (i.e., adding the
380span returned to `zdt1` gives you `zdt2`), then you can ask for bigger units
381via [`Zoned::until`] to make the span more comprehensible:
382
383```
384use jiff::{civil::date, Unit};
385
386let zdt1 = date(2020, 8, 26).at(6, 27, 0, 0).in_tz("America/New_York")?;
387let zdt2 = date(2023, 12, 31).at(18, 30, 0, 0).in_tz("America/New_York")?;
388let span = zdt1.until((Unit::Year, &zdt2))?;
389assert_eq!(format!("{span:#}"), "3y 4mo 5d 12h 3m");
390
391# Ok::<(), Box<dyn std::error::Error>>(())
392```
393
394### Add a duration to a zoned datetime
395
396This example shows how one can add a [`Span`] to a [`Zoned`] via
397[`Zoned::checked_add`] to get a new `Zoned` value. We utilize the [`ToSpan`]
398trait for convenience construction of `Span` values.
399
400```
401use jiff::{civil::date, ToSpan};
402
403let zdt1 = date(2020, 8, 26).at(6, 27, 0, 0).in_tz("America/New_York")?;
404let span = 3.years().months(4).days(5).hours(12).minutes(3);
405let zdt2 = zdt1.checked_add(span)?;
406assert_eq!(zdt2.to_string(), "2023-12-31T18:30:00-05:00[America/New_York]");
407
408# Ok::<(), Box<dyn std::error::Error>>(())
409```
410
411As with [`civil::date`], the [`ToSpan`] trait should only be used with inputs
412that are known to be valid. If you aren't sure whether the inputs are valid,
413then use [`Span::new`] and its fallible mutators like [`Span::try_years`].
414
415### Dealing with ambiguity
416
417In some cases, civil datetimes either don't exist in a particular time zone or
418are repeated. By default, Jiff automatically uses the
419[`tz::Disambiguation::Compatible`] strategy for choosing an instant in all
420cases:
421
422```
423use jiff::civil::date;
424
425// 2:30 on 2024-03-10 in New York didn't exist. It's a "gap."
426// The compatible strategy selects the datetime after the gap.
427let zdt = date(2024, 3, 10).at(2, 30, 0, 0).in_tz("America/New_York")?;
428assert_eq!(zdt.to_string(), "2024-03-10T03:30:00-04:00[America/New_York]");
429
430// 1:30 on 2024-11-03 in New York appeared twice. It's a "fold."
431// The compatible strategy selects the datetime before the fold.
432let zdt = date(2024, 11, 3).at(1, 30, 0, 0).in_tz("America/New_York")?;
433assert_eq!(zdt.to_string(), "2024-11-03T01:30:00-04:00[America/New_York]");
434
435# Ok::<(), Box<dyn std::error::Error>>(())
436```
437
438For more control over disambiguation, see
439[`tz::TimeZone::to_ambiguous_zoned`]. Or
440[`fmt::temporal::DateTimeParser::disambiguation`]
441if you're parsing zoned datetimes.
442
443### Parsing a span
444
445Jiff supports parsing ISO 8601 duration strings:
446
447```
448use jiff::Span;
449
450let span: Span = "P5y1w10dT5h59m".parse()?;
451let expected = Span::new().years(5).weeks(1).days(10).hours(5).minutes(59);
452assert_eq!(span, expected.fieldwise());
453
454# Ok::<(), Box<dyn std::error::Error>>(())
455```
456
457The same format is used for serializing and deserializing `Span` values when
458the `serde` feature is enabled.
459
460Jiff also supports a bespoke ["friendly" format](crate::fmt::friendly) as
461well:
462
463```
464use jiff::Span;
465
466let expected = Span::new().years(5).weeks(1).days(10).hours(5).minutes(59);
467let span: Span = "5 years, 1 week, 10 days, 5 hours, 59 minutes".parse()?;
468assert_eq!(span, expected.fieldwise());
469let span: Span = "5yrs 1wk 10d 5hrs 59mins".parse()?;
470assert_eq!(span, expected.fieldwise());
471let span: Span = "5y 1w 10d 5h 59m".parse()?;
472assert_eq!(span, expected.fieldwise());
473
474# Ok::<(), Box<dyn std::error::Error>>(())
475```
476
477### Parsing an RFC 2822 datetime string
478
479While you probably shouldn't pick [RFC 2822] as a format for new things, it is
480sometimes necessary to use it when something else requires it (like HTTP
481or email). Parsing and printing of RFC 2822 datetimes is done via the
482[`fmt::rfc2822`] module:
483
484```
485use jiff::fmt::rfc2822;
486
487let zdt1 = rfc2822::parse("Thu, 29 Feb 2024 05:34 -0500")?;
488let zdt2 = zdt1.in_tz("Australia/Tasmania")?;
489assert_eq!(rfc2822::to_string(&zdt2)?, "Thu, 29 Feb 2024 21:34:00 +1100");
490let zdt3 = zdt1.in_tz("Asia/Kolkata")?;
491assert_eq!(rfc2822::to_string(&zdt3)?, "Thu, 29 Feb 2024 16:04:00 +0530");
492
493# Ok::<(), Box<dyn std::error::Error>>(())
494```
495
496[RFC 2822]: https://datatracker.ietf.org/doc/html/rfc2822
497
498### Using `strftime` and `strptime` for formatting and parsing
499
500Jiff has support for the C style [`strftime`] and [`strptime`] functions for
501formatting and parsing datetime types. All of Jiff's datetime types having a
502`strptime` constructor for parsing, and a `strftime` method for formatting.
503For example, this shows how to use [`Zoned::strptime`] to parsed a string in
504a "odd" custom format into a zoned datetime:
505
506```
507use jiff::Zoned;
508
509let zdt = Zoned::strptime(
510    "%A, %B %d, %Y at %I:%M%p %Q",
511    "Monday, July 15, 2024 at 5:30pm US/Eastern",
512)?;
513assert_eq!(zdt.to_string(), "2024-07-15T17:30:00-04:00[US/Eastern]");
514
515# Ok::<(), Box<dyn std::error::Error>>(())
516```
517
518And this shows how to use [`Zoned::strftime`] to format a zoned datetime.
519Note the use of `%Z`, which will print a time zone abbreviation (when one is
520available) instead of an offset (`%Z` can't be used for parsing):
521
522```
523use jiff::civil::date;
524
525let zdt = date(2024, 7, 15).at(17, 30, 59, 0).in_tz("Australia/Tasmania")?;
526// %-I instead of %I means no padding.
527let string = zdt.strftime("%A, %B %d, %Y at %-I:%M%P %Z").to_string();
528assert_eq!(string, "Monday, July 15, 2024 at 5:30pm AEST");
529
530# Ok::<(), Box<dyn std::error::Error>>(())
531```
532
533However, time zone abbreviations aren't parsable because they are ambiguous.
534For example, `CST` can stand for `Central Standard Time`, `Cuba Standard Time`
535or `China Standard Time`. Instead, it is recommended to use `%Q` to format an
536IANA time zone identifier (which can be parsed, as shown above):
537
538```
539use jiff::civil::date;
540
541let zdt = date(2024, 7, 15).at(17, 30, 59, 0).in_tz("Australia/Tasmania")?;
542// %-I instead of %I means no padding.
543let string = zdt.strftime("%A, %B %d, %Y at %-I:%M%P %Q").to_string();
544assert_eq!(string, "Monday, July 15, 2024 at 5:30pm Australia/Tasmania");
545
546# Ok::<(), Box<dyn std::error::Error>>(())
547```
548
549See the [`fmt::strtime`] module documentation for supported conversion
550specifiers and other APIs.
551
552[`strftime`]: https://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html
553[`strptime`]: https://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
554
555### Serializing and deserializing integer timestamps with Serde
556
557Sometimes you need to interact with external services that use integer timestamps
558instead of something more civilized like RFC 3339. Since [`Timestamp`]'s
559Serde integration uses RFC 3339, you'll need to override the default. While
560you could hand-write this, Jiff provides convenience routines that do this
561for you. But you do need to wire it up via [Serde's `with` attribute]:
562
563```
564use jiff::Timestamp;
565
566#[derive(Debug, serde::Deserialize, serde::Serialize)]
567struct Record {
568    #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
569    timestamp: Timestamp,
570}
571
572let json = r#"{"timestamp":1517644800}"#;
573let got: Record = serde_json::from_str(&json)?;
574assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
575assert_eq!(serde_json::to_string(&got)?, json);
576
577# Ok::<(), Box<dyn std::error::Error>>(())
578```
579
580If you need to support optional timestamps via `Option<Timestamp>`, then use
581`jiff::fmt::serde::timestamp::second::optional` instead.
582
583For more, see the [`fmt::serde`] sub-module. (This requires enabling Jiff's
584`serde` crate feature.)
585
586[Serde's `with` attribute]: https://serde.rs/field-attrs.html#with
587
588# Crate features
589
590### Ecosystem features
591
592* **std** (enabled by default) -
593  When enabled, Jiff will depend on Rust's standard library. This is needed
594  for things that require interacting with your system, such as reading
595  `/usr/share/zoneinfo` on Unix systems for time zone information, or for
596  finding your system's default time zone. But if you don't need that (or can
597  bundle the Time Zone Database), then Jiff has nearly full functionality
598  without `std` enabled, excepting things like `std::error::Error` trait
599  implementations and a global time zone database (which is required for
600  things like [`Timestamp::in_tz`] to work).
601* **alloc** (enabled by default) -
602  When enabled, Jiff will depend on the `alloc` crate. In particular, this
603  enables functionality that requires or greatly benefits from dynamic memory
604  allocation. If you can enable this, it is strongly encouraged that you do so.
605  Without it, only fixed time zones are supported and error messages are
606  significantly degraded. Also, the sizes of some types get bigger. If you
607  have use cases for Jiff in a no-std and no-alloc context, I would love
608  feedback on the issue tracker about your use cases.
609* **logging** -
610  When enabled, the `log` crate is used to emit messages where appropriate.
611  Generally speaking, this is reserved for system interaction points, such as
612  finding the system copy of the Time Zone Database or finding the system's
613  default time zone.
614* **serde** -
615  When enabled, all of the datetime and span types in Jiff implement
616  serde's `Serialize` and `Deserialize` traits. The format used is specified by
617  Temporal, but it's a mix of the "best" parts of RFC 3339, RFC 9557 and
618  ISO 8601. See the [`fmt::temporal`] module for more details on the format
619  used.
620* **js** -
621  On _only_ the `wasm32-unknown-unknown` and `wasm64-unknown-unknown` targets,
622  the `js` feature will add dependencies on `js-sys` and `wasm-bindgen`.
623  These dependencies are used to determine the current datetime and time
624  zone from the web browser. On these targets without the `js` feature
625  enabled, getting the current datetime will panic (because that's what
626  `std::time::SystemTime::now()` does), and it won't be possible to determine
627  the time zone. This feature is disabled by default because not all uses
628  of `wasm{32,64}-unknown-unknown` are in a web context, although _many_ are
629  (for example, when using `wasm-pack`). Only binary, tests and benchmarks
630  should enable this feature. See
631  [Platform support](crate::_documentation::platform) for more details.
632
633### Time zone features
634
635* **tz-system** (enabled by default) -
636  When enabled, Jiff will include code that attempts to determine the "system"
637  time zone. For example, on Unix systems, this is usually determined by
638  looking at the symlink information on `/etc/localtime`. But in general, it's
639  very platform specific and heuristic oriented. On some platforms, this may
640  require extra dependencies. (For example, `windows-sys` on Windows.)
641* **tz-fat** (enabled by default) -
642  When enabled, Jiff will "fatten" time zone data with extra transitions to
643  make time zone lookups faster. This may result in increased heap memory
644  (when loading time zones from `/usr/share/zoneinfo`) or increased binary
645  size (when using the `jiff-static` proc macros). Note that this doesn't add
646  more transitions than are likely already in `/usr/share/zoneinfo`, depending
647  on how it was generated.
648* **tzdb-bundle-always** -
649  When enabled, Jiff will forcefully depend on the `jiff-tzdb` crate, which
650  embeds an entire copy of the Time Zone Database. You should avoid this unless
651  you have a specific need for it, since it is better to rely on your system's
652  copy of time zone information. (Which may be updated multiple times per
653  year.)
654* **tzdb-bundle-platform** (enabled by default) -
655  When enabled, Jiff will depend on `jiff-tzdb` only for platforms where it is
656  known that there is no canonical copy of the Time Zone Database. For example,
657  Windows.
658* **tzdb-zoneinfo** (enabled by default) -
659  When enabled, Jiff will attempt to look for your system's copy of the Time
660  Zone Database.
661* **tzdb-concatenated** (enabled by default) -
662  When enabled, Jiff will attempt to look for a system copy of the
663  [Concatenated Time Zone Database]. This is primarily meant for reading time
664  zone information on Android platforms. The `ANDROID_ROOT` and `ANDROID_DATA`
665  environment variables (with sensible default fallbacks) are used to construct
666  candidate paths to look for this database. For more on this, see the
667  [Android section of the platform support documentation](crate::_documentation::platform#android).
668* **static** -
669  When enabled, new procedural macros will be added to the `tz` sub-module for
670  creating static `TimeZone` values at compile-time. This adds a dependency on
671  [`jiff-static`] and [`jiff-tzdb`]. `jiff-static` defines the macros, and Jiff
672  re-exports them. This also enables `static-tz`.
673* **static-tz** -
674  When enabled, a `jiff::tz::include` procedural macro will become available.
675  This takes a TZif file path, like `/usr/share/zoneinfo/Israel`, as input and
676  returns a `TimeZone` value at compile time.
677
678### Performance features
679
680* **perf-inline** (enabled by default) -
681  When enabled, a number of `inline(always)` annotations are used inside of
682  Jiff to improve performance. This can especially impact formatting and
683  parsing of datetimes. If the extra performance isn't needed or if you want
684  to prioritize smaller binary sizes and shorter compilation times over
685  runtime performance, then it can be useful to disable this feature.
686
687[`jiff-static`]: https://docs.rs/jiff-static
688[`jiff-tzdb`]: https://docs.rs/jiff-tzdb
689[Concatenated Time Zone Database]: https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/util/ZoneInfoDB.java
690*/
691
692#![no_std]
693// Lots of rustdoc links break when disabling default features because docs
694// aren't written conditionally.
695#![cfg_attr(
696    all(
697        feature = "std",
698        feature = "serde",
699        feature = "static",
700        feature = "tzdb-zoneinfo"
701    ),
702    deny(rustdoc::broken_intra_doc_links)
703)]
704// These are just too annoying to squash otherwise.
705#![cfg_attr(
706    not(all(
707        feature = "std",
708        feature = "tzdb-zoneinfo",
709        feature = "tzdb-concatenated",
710        feature = "tz-system",
711    )),
712    allow(dead_code, unused_imports)
713)]
714// No clue why this thing is still unstable because it's pretty amazing. This
715// adds Cargo feature annotations to items in the rustdoc output. Which is
716// sadly hugely beneficial for this crate due to the number of features.
717#![cfg_attr(docsrs, feature(doc_auto_cfg))]
718// We generally want all types to impl Debug.
719#![warn(missing_debug_implementations)]
720// Document ALL THE THINGS!
721#![deny(missing_docs)]
722// See: https://github.com/rust-lang/rust/pull/121364
723#![allow(unknown_lints, ambiguous_negative_literals)]
724// See: https://github.com/rust-lang/rust/pull/121364
725#![doc(test(attr(allow(unknown_lints, ambiguous_negative_literals))))]
726
727// It should be possible to support other pointer widths, but this library
728// hasn't been tested nor thought about much in contexts with pointers less
729// than 32 bits.
730//
731// If you need support for 8-bit or 16-bit, please submit a bug report at
732// https://github.com/BurntSushi/jiff
733#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
734compile_error!("jiff currently not supported on non-{32,64}");
735
736#[cfg(any(test, feature = "std"))]
737extern crate std;
738
739#[cfg(any(test, feature = "alloc"))]
740extern crate alloc;
741
742pub use crate::{
743    error::Error,
744    signed_duration::{SignedDuration, SignedDurationRound},
745    span::{
746        Span, SpanArithmetic, SpanCompare, SpanFieldwise, SpanRelativeTo,
747        SpanRound, SpanTotal, ToSpan, Unit,
748    },
749    timestamp::{
750        Timestamp, TimestampArithmetic, TimestampDifference,
751        TimestampDisplayWithOffset, TimestampRound, TimestampSeries,
752    },
753    util::round::mode::RoundMode,
754    zoned::{Zoned, ZonedArithmetic, ZonedDifference, ZonedRound, ZonedWith},
755};
756
757#[macro_use]
758mod logging;
759
760pub mod civil;
761mod duration;
762mod error;
763pub mod fmt;
764#[cfg(feature = "std")]
765mod now;
766#[doc(hidden)]
767pub mod shared;
768mod signed_duration;
769mod span;
770mod timestamp;
771pub mod tz;
772mod util;
773mod zoned;
774
775/// Longer form documentation for Jiff.
776pub mod _documentation {
777    #[doc = include_str!("../COMPARE.md")]
778    pub mod comparison {}
779    #[doc = include_str!("../DESIGN.md")]
780    pub mod design {}
781    #[doc = include_str!("../PLATFORM.md")]
782    pub mod platform {}
783    #[doc = include_str!("../CHANGELOG.md")]
784    pub mod changelog {}
785}
786
787#[cfg(test)]
788mod tests {
789    use super::*;
790
791    #[cfg(feature = "std")]
792    #[test]
793    fn now_works() {
794        let _ = crate::logging::Logger::init();
795
796        let zdt = Zoned::now();
797        std::println!("{zdt}");
798    }
799
800    #[cfg(feature = "std")]
801    #[test]
802    fn ranges() {
803        use crate::util::t;
804
805        dbg!((t::SpanYears::MIN, t::SpanYears::MAX));
806        dbg!((t::SpanMonths::MIN, t::SpanMonths::MAX));
807        dbg!((t::SpanWeeks::MIN, t::SpanWeeks::MAX));
808        dbg!((t::SpanDays::MIN, t::SpanDays::MAX));
809        dbg!((t::SpanHours::MIN, t::SpanHours::MAX));
810        dbg!((t::SpanMinutes::MIN, t::SpanMinutes::MAX));
811        dbg!((t::SpanSeconds::MIN, t::SpanSeconds::MAX));
812        dbg!((t::SpanMilliseconds::MIN, t::SpanMilliseconds::MAX));
813        dbg!((t::SpanMicroseconds::MIN, t::SpanMicroseconds::MAX));
814        dbg!((t::SpanNanoseconds::MIN, t::SpanNanoseconds::MAX));
815        dbg!((t::UnixSeconds::MIN, t::UnixSeconds::MAX));
816        dbg!((t::UnixEpochDay::MIN, t::UnixEpochDay::MAX));
817    }
818}