Skip to main content

jiff/
zoned.rs

1use core::time::Duration as UnsignedDuration;
2
3use crate::{
4    civil::{
5        Date, DateTime, DateTimeRound, DateTimeWith, Era, ISOWeekDate, Time,
6        Weekday,
7    },
8    duration::{Duration, SDuration},
9    error::{zoned::Error as E, Error, ErrorContext},
10    fmt::{
11        self,
12        temporal::{self, DEFAULT_DATETIME_PARSER},
13    },
14    tz::{AmbiguousOffset, Disambiguation, Offset, OffsetConflict, TimeZone},
15    util::{
16        rangeint::{RInto, TryRFrom},
17        round::increment,
18        t::{self, ZonedDayNanoseconds, C},
19    },
20    RoundMode, SignedDuration, Span, SpanRound, Timestamp, Unit,
21};
22
23/// A time zone aware instant in time.
24///
25/// A `Zoned` value can be thought of as the combination of following types,
26/// all rolled into one:
27///
28/// * A [`Timestamp`] for indicating the precise instant in time.
29/// * A [`DateTime`] for indicating the "civil" calendar date and clock time.
30/// * A [`TimeZone`] for indicating how to apply time zone transitions while
31/// performing arithmetic.
32///
33/// In particular, a `Zoned` is specifically designed for dealing with
34/// datetimes in a time zone aware manner. Here are some highlights:
35///
36/// * Arithmetic automatically adjusts for daylight saving time (DST), using
37/// the rules defined by [RFC 5545].
38/// * Creating new `Zoned` values from other `Zoned` values via [`Zoned::with`]
39/// by changing clock time (e.g., `02:30`) can do so without worrying that the
40/// time will be invalid due to DST transitions.
41/// * An approximate superset of the [`DateTime`] API is offered on `Zoned`,
42/// but where each of its operations take time zone into account when
43/// appropriate. For example, [`DateTime::start_of_day`] always returns a
44/// datetime set to midnight, but [`Zoned::start_of_day`] returns the first
45/// instant of a day, which might not be midnight if there is a time zone
46/// transition at midnight.
47/// * When using a `Zoned`, it is easy to switch between civil datetime (the
48/// day you see on the calendar and the time you see on the clock) and Unix
49/// time (a precise instant in time). Indeed, a `Zoned` can be losslessy
50/// converted to any other datetime type in this crate: [`Timestamp`],
51/// [`DateTime`], [`Date`] and [`Time`].
52/// * A `Zoned` value can be losslessly serialized and deserialized, via
53/// [serde], by adhering to [RFC 8536]. An example of a serialized zoned
54/// datetime is `2024-07-04T08:39:00-04:00[America/New_York]`.
55/// * Since a `Zoned` stores a [`TimeZone`] itself, multiple time zone aware
56/// operations can be chained together without repeatedly specifying the time
57/// zone.
58///
59/// [RFC 5545]: https://datatracker.ietf.org/doc/html/rfc5545
60/// [RFC 8536]: https://datatracker.ietf.org/doc/html/rfc8536
61/// [serde]: https://serde.rs/
62///
63/// # Parsing and printing
64///
65/// The `Zoned` type provides convenient trait implementations of
66/// [`std::str::FromStr`] and [`std::fmt::Display`]:
67///
68/// ```
69/// use jiff::Zoned;
70///
71/// let zdt: Zoned = "2024-06-19 15:22[America/New_York]".parse()?;
72/// // Notice that the second component and the offset have both been added.
73/// assert_eq!(zdt.to_string(), "2024-06-19T15:22:00-04:00[America/New_York]");
74///
75/// // While in the above case the datetime is unambiguous, in some cases, it
76/// // can be ambiguous. In these cases, an offset is required to correctly
77/// // roundtrip a zoned datetime. For example, on 2024-11-03 in New York, the
78/// // 1 o'clock hour was repeated twice, corresponding to the end of daylight
79/// // saving time.
80/// //
81/// // So because of the ambiguity, this time could be in offset -04 (the first
82/// // time 1 o'clock is on the clock) or it could be -05 (the second time
83/// // 1 o'clock is on the clock, corresponding to the end of DST).
84/// //
85/// // By default, parsing uses a "compatible" strategy for resolving all cases
86/// // of ambiguity: in forward transitions (gaps), the later time is selected.
87/// // And in backward transitions (folds), the earlier time is selected.
88/// let zdt: Zoned = "2024-11-03 01:30[America/New_York]".parse()?;
89/// // As we can see, since this was a fold, the earlier time was selected
90/// // because the -04 offset is the first time 1 o'clock appears on the clock.
91/// assert_eq!(zdt.to_string(), "2024-11-03T01:30:00-04:00[America/New_York]");
92/// // But if we changed the offset and re-serialized, the only thing that
93/// // changes is, indeed, the offset. This demonstrates that the offset is
94/// // key to ensuring lossless serialization.
95/// let zdt = zdt.with().offset(jiff::tz::offset(-5)).build()?;
96/// assert_eq!(zdt.to_string(), "2024-11-03T01:30:00-05:00[America/New_York]");
97///
98/// # Ok::<(), Box<dyn std::error::Error>>(())
99/// ```
100///
101/// A `Zoned` can also be parsed from just a time zone aware date (but the
102/// time zone annotation is still required). In this case, the time is set to
103/// midnight:
104///
105/// ```
106/// use jiff::Zoned;
107///
108/// let zdt: Zoned = "2024-06-19[America/New_York]".parse()?;
109/// assert_eq!(zdt.to_string(), "2024-06-19T00:00:00-04:00[America/New_York]");
110/// // ... although it isn't always midnight, in the case of a time zone
111/// // transition at midnight!
112/// let zdt: Zoned = "2015-10-18[America/Sao_Paulo]".parse()?;
113/// assert_eq!(zdt.to_string(), "2015-10-18T01:00:00-02:00[America/Sao_Paulo]");
114///
115/// # Ok::<(), Box<dyn std::error::Error>>(())
116/// ```
117///
118/// For more information on the specific format supported, see the
119/// [`fmt::temporal`](crate::fmt::temporal) module documentation.
120///
121/// # Leap seconds
122///
123/// Jiff does not support leap seconds. Jiff behaves as if they don't exist.
124/// The only exception is that if one parses a datetime with a second component
125/// of `60`, then it is automatically constrained to `59`:
126///
127/// ```
128/// use jiff::{civil::date, Zoned};
129///
130/// let zdt: Zoned = "2016-12-31 23:59:60[Australia/Tasmania]".parse()?;
131/// assert_eq!(zdt.datetime(), date(2016, 12, 31).at(23, 59, 59, 0));
132///
133/// # Ok::<(), Box<dyn std::error::Error>>(())
134/// ```
135///
136/// # Comparisons
137///
138/// The `Zoned` type provides both `Eq` and `Ord` trait implementations to
139/// facilitate easy comparisons. When a zoned datetime `zdt1` occurs before a
140/// zoned datetime `zdt2`, then `zdt1 < zdt2`. For example:
141///
142/// ```
143/// use jiff::civil::date;
144///
145/// let zdt1 = date(2024, 3, 11).at(1, 25, 15, 0).in_tz("America/New_York")?;
146/// let zdt2 = date(2025, 1, 31).at(0, 30, 0, 0).in_tz("America/New_York")?;
147/// assert!(zdt1 < zdt2);
148///
149/// # Ok::<(), Box<dyn std::error::Error>>(())
150/// ```
151///
152/// Note that `Zoned` comparisons only consider the precise instant in time.
153/// The civil datetime or even the time zone are completely ignored. So it's
154/// possible for a zoned datetime to be less than another even if it's civil
155/// datetime is bigger:
156///
157/// ```
158/// use jiff::civil::date;
159///
160/// let zdt1 = date(2024, 7, 4).at(12, 0, 0, 0).in_tz("America/New_York")?;
161/// let zdt2 = date(2024, 7, 4).at(11, 0, 0, 0).in_tz("America/Los_Angeles")?;
162/// assert!(zdt1 < zdt2);
163/// // But if we only compare civil datetime, the result is flipped:
164/// assert!(zdt1.datetime() > zdt2.datetime());
165///
166/// # Ok::<(), Box<dyn std::error::Error>>(())
167/// ```
168///
169/// The same applies for equality as well. Two `Zoned` values are equal, even
170/// if they have different time zones, when the instant in time is identical:
171///
172/// ```
173/// use jiff::civil::date;
174///
175/// let zdt1 = date(2024, 7, 4).at(12, 0, 0, 0).in_tz("America/New_York")?;
176/// let zdt2 = date(2024, 7, 4).at(9, 0, 0, 0).in_tz("America/Los_Angeles")?;
177/// assert_eq!(zdt1, zdt2);
178///
179/// # Ok::<(), Box<dyn std::error::Error>>(())
180/// ```
181///
182/// (Note that this is different from
183/// [Temporal's `ZonedDateTime.equals`][temporal-equals] comparison, which will
184/// take time zone into account for equality. This is because `Eq` and `Ord`
185/// trait implementations must be consistent in Rust. If you need Temporal's
186/// behavior, then use `zdt1 == zdt2 && zdt1.time_zone() == zdt2.time_zone()`.)
187///
188/// [temporal-equals]: https://tc39.es/proposal-temporal/docs/zoneddatetime.html#equals
189///
190/// # Arithmetic
191///
192/// This type provides routines for adding and subtracting spans of time, as
193/// well as computing the span of time between two `Zoned` values. These
194/// operations take time zones into account.
195///
196/// For adding or subtracting spans of time, one can use any of the following
197/// routines:
198///
199/// * [`Zoned::checked_add`] or [`Zoned::checked_sub`] for checked
200/// arithmetic.
201/// * [`Zoned::saturating_add`] or [`Zoned::saturating_sub`] for
202/// saturating arithmetic.
203///
204/// Additionally, checked arithmetic is available via the `Add` and `Sub`
205/// trait implementations. When the result overflows, a panic occurs.
206///
207/// ```
208/// use jiff::{civil::date, ToSpan};
209///
210/// let start = date(2024, 2, 25).at(15, 45, 0, 0).in_tz("America/New_York")?;
211/// // `Zoned` doesn't implement `Copy`, so you'll want to use `&start` instead
212/// // of `start` if you want to keep using it after arithmetic.
213/// let one_week_later = start + 1.weeks();
214/// assert_eq!(one_week_later.datetime(), date(2024, 3, 3).at(15, 45, 0, 0));
215///
216/// # Ok::<(), Box<dyn std::error::Error>>(())
217/// ```
218///
219/// One can compute the span of time between two zoned datetimes using either
220/// [`Zoned::until`] or [`Zoned::since`]. It's also possible to subtract
221/// two `Zoned` values directly via a `Sub` trait implementation:
222///
223/// ```
224/// use jiff::{civil::date, ToSpan};
225///
226/// let zdt1 = date(2024, 5, 3).at(23, 30, 0, 0).in_tz("America/New_York")?;
227/// let zdt2 = date(2024, 2, 25).at(7, 0, 0, 0).in_tz("America/New_York")?;
228/// assert_eq!(zdt1 - zdt2, 1647.hours().minutes(30).fieldwise());
229///
230/// # Ok::<(), Box<dyn std::error::Error>>(())
231/// ```
232///
233/// The `until` and `since` APIs are polymorphic and allow re-balancing and
234/// rounding the span returned. For example, the default largest unit is hours
235/// (as exemplified above), but we can ask for bigger units:
236///
237/// ```
238/// use jiff::{civil::date, ToSpan, Unit};
239///
240/// let zdt1 = date(2024, 5, 3).at(23, 30, 0, 0).in_tz("America/New_York")?;
241/// let zdt2 = date(2024, 2, 25).at(7, 0, 0, 0).in_tz("America/New_York")?;
242/// assert_eq!(
243///     zdt1.since((Unit::Year, &zdt2))?,
244///     2.months().days(7).hours(16).minutes(30).fieldwise(),
245/// );
246///
247/// # Ok::<(), Box<dyn std::error::Error>>(())
248/// ```
249///
250/// Or even round the span returned:
251///
252/// ```
253/// use jiff::{civil::date, RoundMode, ToSpan, Unit, ZonedDifference};
254///
255/// let zdt1 = date(2024, 5, 3).at(23, 30, 0, 0).in_tz("America/New_York")?;
256/// let zdt2 = date(2024, 2, 25).at(7, 0, 0, 0).in_tz("America/New_York")?;
257/// assert_eq!(
258///     zdt1.since(
259///         ZonedDifference::new(&zdt2)
260///             .smallest(Unit::Day)
261///             .largest(Unit::Year),
262///     )?,
263///     2.months().days(7).fieldwise(),
264/// );
265/// // `ZonedDifference` uses truncation as a rounding mode by default,
266/// // but you can set the rounding mode to break ties away from zero:
267/// assert_eq!(
268///     zdt1.since(
269///         ZonedDifference::new(&zdt2)
270///             .smallest(Unit::Day)
271///             .largest(Unit::Year)
272///             .mode(RoundMode::HalfExpand),
273///     )?,
274///     // Rounds up to 8 days.
275///     2.months().days(8).fieldwise(),
276/// );
277///
278/// # Ok::<(), Box<dyn std::error::Error>>(())
279/// ```
280///
281/// # Rounding
282///
283/// A `Zoned` can be rounded based on a [`ZonedRound`] configuration of
284/// smallest units, rounding increment and rounding mode. Here's an example
285/// showing how to round to the nearest third hour:
286///
287/// ```
288/// use jiff::{civil::date, Unit, ZonedRound};
289///
290/// let zdt = date(2024, 6, 19)
291///     .at(16, 27, 29, 999_999_999)
292///     .in_tz("America/New_York")?;
293/// assert_eq!(
294///     zdt.round(ZonedRound::new().smallest(Unit::Hour).increment(3))?,
295///     date(2024, 6, 19).at(15, 0, 0, 0).in_tz("America/New_York")?,
296/// );
297/// // Or alternatively, make use of the `From<(Unit, i64)> for ZonedRound`
298/// // trait implementation:
299/// assert_eq!(
300///     zdt.round((Unit::Hour, 3))?,
301///     date(2024, 6, 19).at(15, 0, 0, 0).in_tz("America/New_York")?,
302/// );
303///
304/// # Ok::<(), Box<dyn std::error::Error>>(())
305/// ```
306///
307/// See [`Zoned::round`] for more details.
308#[derive(Clone)]
309pub struct Zoned {
310    inner: ZonedInner,
311}
312
313/// The representation of a `Zoned`.
314///
315/// This uses 4 different things: a timestamp, a datetime, an offset and a
316/// time zone. This in turn makes `Zoned` a bit beefy (40 bytes on x86-64),
317/// but I think this is probably the right trade off. (At time of writing,
318/// 2024-07-04.)
319///
320/// Technically speaking, the only essential fields here are timestamp and time
321/// zone. The datetime and offset can both be unambiguously _computed_ from the
322/// combination of a timestamp and a time zone. Indeed, just the timestamp and
323/// the time zone was my initial representation. But as I developed the API of
324/// this type, it became clearer that we should probably store the datetime and
325/// offset as well.
326///
327/// The main issue here is that in order to compute the datetime from a
328/// timestamp and a time zone, you need to do two things:
329///
330/// 1. First, compute the offset. This means doing a binary search on the TZif
331/// data for the transition (or closest transition) matching the timestamp.
332/// 2. Second, use the offset (from UTC) to convert the timestamp into a civil
333/// datetime. This involves a "Unix time to Unix epoch days" conversion that
334/// requires some heavy arithmetic.
335///
336/// So if we don't store the datetime or offset, then we need to compute them
337/// any time we need them. And the Temporal design really pushes heavily in
338/// favor of treating the "instant in time" and "civil datetime" as two sides
339/// to the same coin. That means users are very encouraged to just use whatever
340/// they need. So if we are always computing the offset and datetime whenever
341/// we need them, we're potentially punishing users for working with civil
342/// datetimes. It just doesn't feel like the right trade-off.
343///
344/// Instead, my idea here is that, ultimately, `Zoned` is meant to provide
345/// a one-stop shop for "doing the right thing." Presenting that unified
346/// abstraction comes with costs. And that if we want to expose cheaper ways
347/// of performing at least some of the operations on `Zoned` by making fewer
348/// assumptions, then we should probably endeavor to do that by exposing a
349/// lower level API. I'm not sure what that would look like, so I think it
350/// should be driven by use cases.
351///
352/// Some other things I considered:
353///
354/// * Use `Zoned(Arc<ZonedInner>)` to make `Zoned` pointer-sized. But I didn't
355/// like this because it implies creating any new `Zoned` value requires an
356/// allocation. Since a `TimeZone` internally uses an `Arc`, all it requires
357/// today is a chunky memcpy and an atomic ref count increment.
358/// * Use `OnceLock` shenanigans for the datetime and offset fields. This would
359/// make `Zoned` even beefier and I wasn't totally clear how much this would
360/// save us. And it would impose some (probably small) cost on every datetime
361/// or offset access.
362/// * Use a radically different design that permits a `Zoned` to be `Copy`.
363/// I personally find it deeply annoying that `Zoned` is both the "main"
364/// datetime type in Jiff and also the only one that doesn't implement `Copy`.
365/// I explored some designs, but I couldn't figure out how to make it work in
366/// a satisfying way. The main issue here is `TimeZone`. A `TimeZone` is a huge
367/// chunk of data and the ergonomics of the `Zoned` API require being able to
368/// access a `TimeZone` without the caller providing it explicitly. So to me,
369/// the only real alternative here is to use some kind of integer handle into
370/// a global time zone database. But now you all of a sudden need to worry
371/// about synchronization for every time zone access and plausibly also garbage
372/// collection. And this also complicates matters for using custom time zone
373/// databases. So I ultimately came down on "Zoned is not Copy" as the least
374/// awful choice. *heavy sigh*
375#[derive(Clone)]
376struct ZonedInner {
377    timestamp: Timestamp,
378    datetime: DateTime,
379    offset: Offset,
380    time_zone: TimeZone,
381}
382
383impl Zoned {
384    /// Returns the current system time in this system's time zone.
385    ///
386    /// If the system's time zone could not be found, then
387    /// [`TimeZone::unknown`] is used instead. When this happens, a `WARN`
388    /// level log message will be emitted. (To see it, one will need to install
389    /// a logger that is compatible with the `log` crate and enable Jiff's
390    /// `logging` Cargo feature.)
391    ///
392    /// To create a `Zoned` value for the current time in a particular
393    /// time zone other than the system default time zone, use
394    /// `Timestamp::now().to_zoned(time_zone)`. In particular, using
395    /// [`Timestamp::now`] avoids the work required to fetch the system time
396    /// zone if you did `Zoned::now().with_time_zone(time_zone)`.
397    ///
398    /// # Panics
399    ///
400    /// This panics if the system clock is set to a time value outside of the
401    /// range `-009999-01-01T00:00:00Z..=9999-12-31T11:59:59.999999999Z`. The
402    /// justification here is that it is reasonable to expect the system clock
403    /// to be set to a somewhat sane, if imprecise, value.
404    ///
405    /// If you want to get the current Unix time fallibly, use
406    /// [`Zoned::try_from`] with a `std::time::SystemTime` as input.
407    ///
408    /// This may also panic when `SystemTime::now()` itself panics. The most
409    /// common context in which this happens is on the `wasm32-unknown-unknown`
410    /// target. If you're using that target in the context of the web (for
411    /// example, via `wasm-pack`), and you're an application, then you should
412    /// enable Jiff's `js` feature. This will automatically instruct Jiff in
413    /// this very specific circumstance to execute JavaScript code to determine
414    /// the current time from the web browser.
415    ///
416    /// # Example
417    ///
418    /// ```
419    /// use jiff::{Timestamp, Zoned};
420    ///
421    /// assert!(Zoned::now().timestamp() > Timestamp::UNIX_EPOCH);
422    /// ```
423    #[cfg(feature = "std")]
424    #[inline]
425    pub fn now() -> Zoned {
426        Zoned::try_from(crate::now::system_time())
427            .expect("system time is valid")
428    }
429
430    /// Creates a new `Zoned` value from a specific instant in a particular
431    /// time zone. The time zone determines how to render the instant in time
432    /// into civil time. (Also known as "clock," "wall," "local" or "naive"
433    /// time.)
434    ///
435    /// To create a new zoned datetime from another with a particular field
436    /// value, use the methods on [`ZonedWith`] via [`Zoned::with`].
437    ///
438    /// # Construction from civil time
439    ///
440    /// A `Zoned` value can also be created from a civil time via the following
441    /// methods:
442    ///
443    /// * [`DateTime::in_tz`] does a Time Zone Database lookup given a time
444    /// zone name string.
445    /// * [`DateTime::to_zoned`] accepts a `TimeZone`.
446    /// * [`Date::in_tz`] does a Time Zone Database lookup given a time zone
447    /// name string and attempts to use midnight as the clock time.
448    /// * [`Date::to_zoned`] accepts a `TimeZone` and attempts to use midnight
449    /// as the clock time.
450    ///
451    /// Whenever one is converting from civil time to a zoned
452    /// datetime, it is possible for the civil time to be ambiguous.
453    /// That is, it might be a clock reading that could refer to
454    /// multiple possible instants in time, or it might be a clock
455    /// reading that never exists. The above routines will use a
456    /// [`Disambiguation::Compatible`]
457    /// strategy to automatically resolve these corner cases.
458    ///
459    /// If one wants to control how ambiguity is resolved (including
460    /// by returning an error), use [`TimeZone::to_ambiguous_zoned`]
461    /// and select the desired strategy via a method on
462    /// [`AmbiguousZoned`](crate::tz::AmbiguousZoned).
463    ///
464    /// # Example: What was the civil time in Tasmania at the Unix epoch?
465    ///
466    /// ```
467    /// use jiff::{tz::TimeZone, Timestamp, Zoned};
468    ///
469    /// let tz = TimeZone::get("Australia/Tasmania")?;
470    /// let zdt = Zoned::new(Timestamp::UNIX_EPOCH, tz);
471    /// assert_eq!(
472    ///     zdt.to_string(),
473    ///     "1970-01-01T11:00:00+11:00[Australia/Tasmania]",
474    /// );
475    ///
476    /// # Ok::<(), Box<dyn std::error::Error>>(())
477    /// ```
478    ///
479    /// # Example: What was the civil time in New York when World War 1 ended?
480    ///
481    /// ```
482    /// use jiff::civil::date;
483    ///
484    /// let zdt1 = date(1918, 11, 11).at(11, 0, 0, 0).in_tz("Europe/Paris")?;
485    /// let zdt2 = zdt1.in_tz("America/New_York")?;
486    /// assert_eq!(
487    ///     zdt2.to_string(),
488    ///     "1918-11-11T06:00:00-05:00[America/New_York]",
489    /// );
490    ///
491    /// # Ok::<(), Box<dyn std::error::Error>>(())
492    /// ```
493    #[inline]
494    pub fn new(timestamp: Timestamp, time_zone: TimeZone) -> Zoned {
495        let offset = time_zone.to_offset(timestamp);
496        let datetime = offset.to_datetime(timestamp);
497        let inner = ZonedInner { timestamp, datetime, offset, time_zone };
498        Zoned { inner }
499    }
500
501    /// A crate internal constructor for building a `Zoned` from its
502    /// constituent parts.
503    ///
504    /// This should basically never be exposed, because it can be quite tricky
505    /// to get the parts correct.
506    ///
507    /// See `civil::DateTime::to_zoned` for a use case for this routine. (Why
508    /// do you think? Perf!)
509    #[inline]
510    pub(crate) fn from_parts(
511        timestamp: Timestamp,
512        time_zone: TimeZone,
513        offset: Offset,
514        datetime: DateTime,
515    ) -> Zoned {
516        let inner = ZonedInner { timestamp, datetime, offset, time_zone };
517        Zoned { inner }
518    }
519
520    /// Create a builder for constructing a new `Zoned` from the fields of
521    /// this zoned datetime.
522    ///
523    /// See the methods on [`ZonedWith`] for the different ways one can set
524    /// the fields of a new `Zoned`.
525    ///
526    /// Note that this doesn't support changing the time zone. If you want a
527    /// `Zoned` value of the same instant but in a different time zone, use
528    /// [`Zoned::in_tz`] or [`Zoned::with_time_zone`]. If you want a `Zoned`
529    /// value of the same civil datetime (assuming it isn't ambiguous) but in
530    /// a different time zone, then use [`Zoned::datetime`] followed by
531    /// [`DateTime::in_tz`] or [`DateTime::to_zoned`].
532    ///
533    /// # Example
534    ///
535    /// The builder ensures one can chain together the individual components
536    /// of a zoned datetime without it failing at an intermediate step. For
537    /// example, if you had a date of `2024-10-31T00:00:00[America/New_York]`
538    /// and wanted to change both the day and the month, and each setting was
539    /// validated independent of the other, you would need to be careful to set
540    /// the day first and then the month. In some cases, you would need to set
541    /// the month first and then the day!
542    ///
543    /// But with the builder, you can set values in any order:
544    ///
545    /// ```
546    /// use jiff::civil::date;
547    ///
548    /// let zdt1 = date(2024, 10, 31).at(0, 0, 0, 0).in_tz("America/New_York")?;
549    /// let zdt2 = zdt1.with().month(11).day(30).build()?;
550    /// assert_eq!(
551    ///     zdt2,
552    ///     date(2024, 11, 30).at(0, 0, 0, 0).in_tz("America/New_York")?,
553    /// );
554    ///
555    /// let zdt1 = date(2024, 4, 30).at(0, 0, 0, 0).in_tz("America/New_York")?;
556    /// let zdt2 = zdt1.with().day(31).month(7).build()?;
557    /// assert_eq!(
558    ///     zdt2,
559    ///     date(2024, 7, 31).at(0, 0, 0, 0).in_tz("America/New_York")?,
560    /// );
561    ///
562    /// # Ok::<(), Box<dyn std::error::Error>>(())
563    /// ```
564    #[inline]
565    pub fn with(&self) -> ZonedWith {
566        ZonedWith::new(self.clone())
567    }
568
569    /// Return a new zoned datetime with precisely the same instant in a
570    /// different time zone.
571    ///
572    /// The zoned datetime returned is guaranteed to have an equivalent
573    /// [`Timestamp`]. However, its civil [`DateTime`] may be different.
574    ///
575    /// # Example: What was the civil time in New York when World War 1 ended?
576    ///
577    /// ```
578    /// use jiff::{civil::date, tz::TimeZone};
579    ///
580    /// let from = TimeZone::get("Europe/Paris")?;
581    /// let to = TimeZone::get("America/New_York")?;
582    /// let zdt1 = date(1918, 11, 11).at(11, 0, 0, 0).to_zoned(from)?;
583    /// // Switch zdt1 to a different time zone, but keeping the same instant
584    /// // in time. The civil time changes, but not the instant!
585    /// let zdt2 = zdt1.with_time_zone(to);
586    /// assert_eq!(
587    ///     zdt2.to_string(),
588    ///     "1918-11-11T06:00:00-05:00[America/New_York]",
589    /// );
590    ///
591    /// # Ok::<(), Box<dyn std::error::Error>>(())
592    /// ```
593    #[inline]
594    pub fn with_time_zone(&self, time_zone: TimeZone) -> Zoned {
595        Zoned::new(self.timestamp(), time_zone)
596    }
597
598    /// Return a new zoned datetime with precisely the same instant in a
599    /// different time zone.
600    ///
601    /// The zoned datetime returned is guaranteed to have an equivalent
602    /// [`Timestamp`]. However, its civil [`DateTime`] may be different.
603    ///
604    /// The name given is resolved to a [`TimeZone`] by using the default
605    /// [`TimeZoneDatabase`](crate::tz::TimeZoneDatabase) created by
606    /// [`tz::db`](crate::tz::db). Indeed, this is a convenience function for
607    /// [`DateTime::to_zoned`] where the time zone database lookup is done
608    /// automatically.
609    ///
610    /// # Errors
611    ///
612    /// This returns an error when the given time zone name could not be found
613    /// in the default time zone database.
614    ///
615    /// # Example: What was the civil time in New York when World War 1 ended?
616    ///
617    /// ```
618    /// use jiff::civil::date;
619    ///
620    /// let zdt1 = date(1918, 11, 11).at(11, 0, 0, 0).in_tz("Europe/Paris")?;
621    /// // Switch zdt1 to a different time zone, but keeping the same instant
622    /// // in time. The civil time changes, but not the instant!
623    /// let zdt2 = zdt1.in_tz("America/New_York")?;
624    /// assert_eq!(
625    ///     zdt2.to_string(),
626    ///     "1918-11-11T06:00:00-05:00[America/New_York]",
627    /// );
628    ///
629    /// # Ok::<(), Box<dyn std::error::Error>>(())
630    /// ```
631    #[inline]
632    pub fn in_tz(&self, name: &str) -> Result<Zoned, Error> {
633        let tz = crate::tz::db().get(name)?;
634        Ok(self.with_time_zone(tz))
635    }
636
637    /// Returns the time zone attached to this [`Zoned`] value.
638    ///
639    /// A time zone is more than just an offset. A time zone is a series of
640    /// rules for determining the civil time for a corresponding instant.
641    /// Indeed, a zoned datetime uses its time zone to perform zone-aware
642    /// arithmetic, rounding and serialization.
643    ///
644    /// # Example
645    ///
646    /// ```
647    /// use jiff::Zoned;
648    ///
649    /// let zdt: Zoned = "2024-07-03 14:31[america/new_york]".parse()?;
650    /// assert_eq!(zdt.time_zone().iana_name(), Some("America/New_York"));
651    ///
652    /// # Ok::<(), Box<dyn std::error::Error>>(())
653    /// ```
654    #[inline]
655    pub fn time_zone(&self) -> &TimeZone {
656        &self.inner.time_zone
657    }
658
659    /// Returns the year for this zoned datetime.
660    ///
661    /// The value returned is guaranteed to be in the range `-9999..=9999`.
662    ///
663    /// # Example
664    ///
665    /// ```
666    /// use jiff::civil::date;
667    ///
668    /// let zdt1 = date(2024, 3, 9).at(7, 30, 0, 0).in_tz("America/New_York")?;
669    /// assert_eq!(zdt1.year(), 2024);
670    ///
671    /// let zdt2 = date(-2024, 3, 9).at(7, 30, 0, 0).in_tz("America/New_York")?;
672    /// assert_eq!(zdt2.year(), -2024);
673    ///
674    /// let zdt3 = date(0, 3, 9).at(7, 30, 0, 0).in_tz("America/New_York")?;
675    /// assert_eq!(zdt3.year(), 0);
676    ///
677    /// # Ok::<(), Box<dyn std::error::Error>>(())
678    /// ```
679    #[inline]
680    pub fn year(&self) -> i16 {
681        self.date().year()
682    }
683
684    /// Returns the year and its era.
685    ///
686    /// This crate specifically allows years to be negative or `0`, where as
687    /// years written for the Gregorian calendar are always positive and
688    /// greater than `0`. In the Gregorian calendar, the era labels `BCE` and
689    /// `CE` are used to disambiguate between years less than or equal to `0`
690    /// and years greater than `0`, respectively.
691    ///
692    /// The crate is designed this way so that years in the latest era (that
693    /// is, `CE`) are aligned with years in this crate.
694    ///
695    /// The year returned is guaranteed to be in the range `1..=10000`.
696    ///
697    /// # Example
698    ///
699    /// ```
700    /// use jiff::civil::{Era, date};
701    ///
702    /// let zdt = date(2024, 10, 3).at(7, 30, 0, 0).in_tz("America/New_York")?;
703    /// assert_eq!(zdt.era_year(), (2024, Era::CE));
704    ///
705    /// let zdt = date(1, 10, 3).at(7, 30, 0, 0).in_tz("America/New_York")?;
706    /// assert_eq!(zdt.era_year(), (1, Era::CE));
707    ///
708    /// let zdt = date(0, 10, 3).at(7, 30, 0, 0).in_tz("America/New_York")?;
709    /// assert_eq!(zdt.era_year(), (1, Era::BCE));
710    ///
711    /// let zdt = date(-1, 10, 3).at(7, 30, 0, 0).in_tz("America/New_York")?;
712    /// assert_eq!(zdt.era_year(), (2, Era::BCE));
713    ///
714    /// let zdt = date(-10, 10, 3).at(7, 30, 0, 0).in_tz("America/New_York")?;
715    /// assert_eq!(zdt.era_year(), (11, Era::BCE));
716    ///
717    /// let zdt = date(-9_999, 10, 3).at(7, 30, 0, 0).in_tz("America/New_York")?;
718    /// assert_eq!(zdt.era_year(), (10_000, Era::BCE));
719    ///
720    /// # Ok::<(), Box<dyn std::error::Error>>(())
721    /// ```
722    #[inline]
723    pub fn era_year(&self) -> (i16, Era) {
724        self.date().era_year()
725    }
726
727    /// Returns the month for this zoned datetime.
728    ///
729    /// The value returned is guaranteed to be in the range `1..=12`.
730    ///
731    /// # Example
732    ///
733    /// ```
734    /// use jiff::civil::date;
735    ///
736    /// let zdt = date(2024, 3, 9).at(7, 30, 0, 0).in_tz("America/New_York")?;
737    /// assert_eq!(zdt.month(), 3);
738    ///
739    /// # Ok::<(), Box<dyn std::error::Error>>(())
740    /// ```
741    #[inline]
742    pub fn month(&self) -> i8 {
743        self.date().month()
744    }
745
746    /// Returns the day for this zoned datetime.
747    ///
748    /// The value returned is guaranteed to be in the range `1..=31`.
749    ///
750    /// # Example
751    ///
752    /// ```
753    /// use jiff::civil::date;
754    ///
755    /// let zdt = date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?;
756    /// assert_eq!(zdt.day(), 29);
757    ///
758    /// # Ok::<(), Box<dyn std::error::Error>>(())
759    /// ```
760    #[inline]
761    pub fn day(&self) -> i8 {
762        self.date().day()
763    }
764
765    /// Returns the "hour" component of this zoned datetime.
766    ///
767    /// The value returned is guaranteed to be in the range `0..=23`.
768    ///
769    /// # Example
770    ///
771    /// ```
772    /// use jiff::civil::date;
773    ///
774    /// let zdt = date(2000, 1, 2)
775    ///     .at(3, 4, 5, 123_456_789)
776    ///     .in_tz("America/New_York")?;
777    /// assert_eq!(zdt.hour(), 3);
778    ///
779    /// # Ok::<(), Box<dyn std::error::Error>>(())
780    /// ```
781    #[inline]
782    pub fn hour(&self) -> i8 {
783        self.time().hour()
784    }
785
786    /// Returns the "minute" component of this zoned datetime.
787    ///
788    /// The value returned is guaranteed to be in the range `0..=59`.
789    ///
790    /// # Example
791    ///
792    /// ```
793    /// use jiff::civil::date;
794    ///
795    /// let zdt = date(2000, 1, 2)
796    ///     .at(3, 4, 5, 123_456_789)
797    ///     .in_tz("America/New_York")?;
798    /// assert_eq!(zdt.minute(), 4);
799    ///
800    /// # Ok::<(), Box<dyn std::error::Error>>(())
801    /// ```
802    #[inline]
803    pub fn minute(&self) -> i8 {
804        self.time().minute()
805    }
806
807    /// Returns the "second" component of this zoned datetime.
808    ///
809    /// The value returned is guaranteed to be in the range `0..=59`.
810    ///
811    /// # Example
812    ///
813    /// ```
814    /// use jiff::civil::date;
815    ///
816    /// let zdt = date(2000, 1, 2)
817    ///     .at(3, 4, 5, 123_456_789)
818    ///     .in_tz("America/New_York")?;
819    /// assert_eq!(zdt.second(), 5);
820    ///
821    /// # Ok::<(), Box<dyn std::error::Error>>(())
822    /// ```
823    #[inline]
824    pub fn second(&self) -> i8 {
825        self.time().second()
826    }
827
828    /// Returns the "millisecond" component of this zoned datetime.
829    ///
830    /// The value returned is guaranteed to be in the range `0..=999`.
831    ///
832    /// # Example
833    ///
834    /// ```
835    /// use jiff::civil::date;
836    ///
837    /// let zdt = date(2000, 1, 2)
838    ///     .at(3, 4, 5, 123_456_789)
839    ///     .in_tz("America/New_York")?;
840    /// assert_eq!(zdt.millisecond(), 123);
841    ///
842    /// # Ok::<(), Box<dyn std::error::Error>>(())
843    /// ```
844    #[inline]
845    pub fn millisecond(&self) -> i16 {
846        self.time().millisecond()
847    }
848
849    /// Returns the "microsecond" component of this zoned datetime.
850    ///
851    /// The value returned is guaranteed to be in the range `0..=999`.
852    ///
853    /// # Example
854    ///
855    /// ```
856    /// use jiff::civil::date;
857    ///
858    /// let zdt = date(2000, 1, 2)
859    ///     .at(3, 4, 5, 123_456_789)
860    ///     .in_tz("America/New_York")?;
861    /// assert_eq!(zdt.microsecond(), 456);
862    ///
863    /// # Ok::<(), Box<dyn std::error::Error>>(())
864    /// ```
865    #[inline]
866    pub fn microsecond(&self) -> i16 {
867        self.time().microsecond()
868    }
869
870    /// Returns the "nanosecond" component of this zoned datetime.
871    ///
872    /// The value returned is guaranteed to be in the range `0..=999`.
873    ///
874    /// # Example
875    ///
876    /// ```
877    /// use jiff::civil::date;
878    ///
879    /// let zdt = date(2000, 1, 2)
880    ///     .at(3, 4, 5, 123_456_789)
881    ///     .in_tz("America/New_York")?;
882    /// assert_eq!(zdt.nanosecond(), 789);
883    ///
884    /// # Ok::<(), Box<dyn std::error::Error>>(())
885    /// ```
886    #[inline]
887    pub fn nanosecond(&self) -> i16 {
888        self.time().nanosecond()
889    }
890
891    /// Returns the fractional nanosecond for this `Zoned` value.
892    ///
893    /// If you want to set this value on `Zoned`, then use
894    /// [`ZonedWith::subsec_nanosecond`] via [`Zoned::with`].
895    ///
896    /// The value returned is guaranteed to be in the range `0..=999_999_999`.
897    ///
898    /// Note that this returns the fractional second associated with the civil
899    /// time on this `Zoned` value. This is distinct from the fractional
900    /// second on the underlying timestamp. A timestamp, for example, may be
901    /// negative to indicate time before the Unix epoch. But a civil datetime
902    /// can only have a negative year, while the remaining values are all
903    /// semantically positive. See the examples below for how this can manifest
904    /// in practice.
905    ///
906    /// # Example
907    ///
908    /// This shows the relationship between constructing a `Zoned` value
909    /// with routines like `with().millisecond()` and accessing the entire
910    /// fractional part as a nanosecond:
911    ///
912    /// ```
913    /// use jiff::civil::date;
914    ///
915    /// let zdt1 = date(2000, 1, 2)
916    ///     .at(3, 4, 5, 123_456_789)
917    ///     .in_tz("America/New_York")?;
918    /// assert_eq!(zdt1.subsec_nanosecond(), 123_456_789);
919    ///
920    /// let zdt2 = zdt1.with().millisecond(333).build()?;
921    /// assert_eq!(zdt2.subsec_nanosecond(), 333_456_789);
922    ///
923    /// # Ok::<(), Box<dyn std::error::Error>>(())
924    /// ```
925    ///
926    /// # Example: nanoseconds from a timestamp
927    ///
928    /// This shows how the fractional nanosecond part of a `Zoned` value
929    /// manifests from a specific timestamp.
930    ///
931    /// ```
932    /// use jiff::Timestamp;
933    ///
934    /// // 1,234 nanoseconds after the Unix epoch.
935    /// let zdt = Timestamp::new(0, 1_234)?.in_tz("UTC")?;
936    /// assert_eq!(zdt.subsec_nanosecond(), 1_234);
937    /// // N.B. The timestamp's fractional second and the civil datetime's
938    /// // fractional second happen to be equal here:
939    /// assert_eq!(zdt.timestamp().subsec_nanosecond(), 1_234);
940    ///
941    /// # Ok::<(), Box<dyn std::error::Error>>(())
942    /// ```
943    ///
944    /// # Example: fractional seconds can differ between timestamps and civil time
945    ///
946    /// This shows how a timestamp can have a different fractional second
947    /// value than its corresponding `Zoned` value because of how the sign
948    /// is handled:
949    ///
950    /// ```
951    /// use jiff::{civil, Timestamp};
952    ///
953    /// // 1,234 nanoseconds before the Unix epoch.
954    /// let zdt = Timestamp::new(0, -1_234)?.in_tz("UTC")?;
955    /// // The timestamp's fractional second is what was given:
956    /// assert_eq!(zdt.timestamp().subsec_nanosecond(), -1_234);
957    /// // But the civil datetime's fractional second is equal to
958    /// // `1_000_000_000 - 1_234`. This is because civil datetimes
959    /// // represent times in strictly positive values, like it
960    /// // would read on a clock.
961    /// assert_eq!(zdt.subsec_nanosecond(), 999998766);
962    /// // Looking at the other components of the time value might help.
963    /// assert_eq!(zdt.hour(), 23);
964    /// assert_eq!(zdt.minute(), 59);
965    /// assert_eq!(zdt.second(), 59);
966    ///
967    /// # Ok::<(), Box<dyn std::error::Error>>(())
968    /// ```
969    #[inline]
970    pub fn subsec_nanosecond(&self) -> i32 {
971        self.time().subsec_nanosecond()
972    }
973
974    /// Returns the weekday corresponding to this zoned datetime.
975    ///
976    /// # Example
977    ///
978    /// ```
979    /// use jiff::civil::{Weekday, date};
980    ///
981    /// // The Unix epoch was on a Thursday.
982    /// let zdt = date(1970, 1, 1).at(7, 30, 0, 0).in_tz("America/New_York")?;
983    /// assert_eq!(zdt.weekday(), Weekday::Thursday);
984    /// // One can also get the weekday as an offset in a variety of schemes.
985    /// assert_eq!(zdt.weekday().to_monday_zero_offset(), 3);
986    /// assert_eq!(zdt.weekday().to_monday_one_offset(), 4);
987    /// assert_eq!(zdt.weekday().to_sunday_zero_offset(), 4);
988    /// assert_eq!(zdt.weekday().to_sunday_one_offset(), 5);
989    ///
990    /// # Ok::<(), Box<dyn std::error::Error>>(())
991    /// ```
992    #[inline]
993    pub fn weekday(&self) -> Weekday {
994        self.date().weekday()
995    }
996
997    /// Returns the ordinal day of the year that this zoned datetime resides
998    /// in.
999    ///
1000    /// For leap years, this always returns a value in the range `1..=366`.
1001    /// Otherwise, the value is in the range `1..=365`.
1002    ///
1003    /// # Example
1004    ///
1005    /// ```
1006    /// use jiff::civil::date;
1007    ///
1008    /// let zdt = date(2006, 8, 24).at(7, 30, 0, 0).in_tz("America/New_York")?;
1009    /// assert_eq!(zdt.day_of_year(), 236);
1010    ///
1011    /// let zdt = date(2023, 12, 31).at(7, 30, 0, 0).in_tz("America/New_York")?;
1012    /// assert_eq!(zdt.day_of_year(), 365);
1013    ///
1014    /// let zdt = date(2024, 12, 31).at(7, 30, 0, 0).in_tz("America/New_York")?;
1015    /// assert_eq!(zdt.day_of_year(), 366);
1016    ///
1017    /// # Ok::<(), Box<dyn std::error::Error>>(())
1018    /// ```
1019    #[inline]
1020    pub fn day_of_year(&self) -> i16 {
1021        self.date().day_of_year()
1022    }
1023
1024    /// Returns the ordinal day of the year that this zoned datetime resides
1025    /// in, but ignores leap years.
1026    ///
1027    /// That is, the range of possible values returned by this routine is
1028    /// `1..=365`, even if this date resides in a leap year. If this date is
1029    /// February 29, then this routine returns `None`.
1030    ///
1031    /// The value `365` always corresponds to the last day in the year,
1032    /// December 31, even for leap years.
1033    ///
1034    /// # Example
1035    ///
1036    /// ```
1037    /// use jiff::civil::date;
1038    ///
1039    /// let zdt = date(2006, 8, 24).at(7, 30, 0, 0).in_tz("America/New_York")?;
1040    /// assert_eq!(zdt.day_of_year_no_leap(), Some(236));
1041    ///
1042    /// let zdt = date(2023, 12, 31).at(7, 30, 0, 0).in_tz("America/New_York")?;
1043    /// assert_eq!(zdt.day_of_year_no_leap(), Some(365));
1044    ///
1045    /// let zdt = date(2024, 12, 31).at(7, 30, 0, 0).in_tz("America/New_York")?;
1046    /// assert_eq!(zdt.day_of_year_no_leap(), Some(365));
1047    ///
1048    /// let zdt = date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?;
1049    /// assert_eq!(zdt.day_of_year_no_leap(), None);
1050    ///
1051    /// # Ok::<(), Box<dyn std::error::Error>>(())
1052    /// ```
1053    #[inline]
1054    pub fn day_of_year_no_leap(&self) -> Option<i16> {
1055        self.date().day_of_year_no_leap()
1056    }
1057
1058    /// Returns the beginning of the day, corresponding to `00:00:00` civil
1059    /// time, that this datetime resides in.
1060    ///
1061    /// While in nearly all cases the time returned will be `00:00:00`, it is
1062    /// possible for the time to be different from midnight if there is a time
1063    /// zone transition at midnight.
1064    ///
1065    /// # Example
1066    ///
1067    /// ```
1068    /// use jiff::{civil::date, Zoned};
1069    ///
1070    /// let zdt = date(2015, 10, 18).at(12, 0, 0, 0).in_tz("America/New_York")?;
1071    /// assert_eq!(
1072    ///     zdt.start_of_day()?.to_string(),
1073    ///     "2015-10-18T00:00:00-04:00[America/New_York]",
1074    /// );
1075    ///
1076    /// # Ok::<(), Box<dyn std::error::Error>>(())
1077    /// ```
1078    ///
1079    /// # Example: start of day may not be midnight
1080    ///
1081    /// In some time zones, gap transitions may begin at midnight. This implies
1082    /// that `00:xx:yy` does not exist on a clock in that time zone for that
1083    /// day.
1084    ///
1085    /// ```
1086    /// use jiff::{civil::date, Zoned};
1087    ///
1088    /// let zdt = date(2015, 10, 18).at(12, 0, 0, 0).in_tz("America/Sao_Paulo")?;
1089    /// assert_eq!(
1090    ///     zdt.start_of_day()?.to_string(),
1091    ///     // not midnight!
1092    ///     "2015-10-18T01:00:00-02:00[America/Sao_Paulo]",
1093    /// );
1094    ///
1095    /// # Ok::<(), Box<dyn std::error::Error>>(())
1096    /// ```
1097    ///
1098    /// # Example: error because of overflow
1099    ///
1100    /// In some cases, it's possible for `Zoned` value to be able to represent
1101    /// an instant in time later in the day for a particular time zone, but not
1102    /// earlier in the day. This can only occur near the minimum datetime value
1103    /// supported by Jiff.
1104    ///
1105    /// ```
1106    /// use jiff::{civil::date, tz::{TimeZone, Offset}, Zoned};
1107    ///
1108    /// // While -9999-01-03T04:00:00+25:59:59 is representable as a Zoned
1109    /// // value, the start of the corresponding day is not!
1110    /// let tz = TimeZone::fixed(Offset::MAX);
1111    /// let zdt = date(-9999, 1, 3).at(4, 0, 0, 0).to_zoned(tz.clone())?;
1112    /// assert!(zdt.start_of_day().is_err());
1113    /// // The next day works fine since -9999-01-04T00:00:00+25:59:59 is
1114    /// // representable.
1115    /// let zdt = date(-9999, 1, 4).at(15, 0, 0, 0).to_zoned(tz)?;
1116    /// assert_eq!(
1117    ///     zdt.start_of_day()?.datetime(),
1118    ///     date(-9999, 1, 4).at(0, 0, 0, 0),
1119    /// );
1120    ///
1121    /// # Ok::<(), Box<dyn std::error::Error>>(())
1122    /// ```
1123    #[inline]
1124    pub fn start_of_day(&self) -> Result<Zoned, Error> {
1125        self.datetime().start_of_day().to_zoned(self.time_zone().clone())
1126    }
1127
1128    /// Returns the end of the day, corresponding to `23:59:59.999999999` civil
1129    /// time, that this datetime resides in.
1130    ///
1131    /// While in nearly all cases the time returned will be
1132    /// `23:59:59.999999999`, it is possible for the time to be different if
1133    /// there is a time zone transition covering that time.
1134    ///
1135    /// # Example
1136    ///
1137    /// ```
1138    /// use jiff::civil::date;
1139    ///
1140    /// let zdt = date(2024, 7, 3)
1141    ///     .at(7, 30, 10, 123_456_789)
1142    ///     .in_tz("America/New_York")?;
1143    /// assert_eq!(
1144    ///     zdt.end_of_day()?,
1145    ///     date(2024, 7, 3)
1146    ///         .at(23, 59, 59, 999_999_999)
1147    ///         .in_tz("America/New_York")?,
1148    /// );
1149    ///
1150    /// # Ok::<(), Box<dyn std::error::Error>>(())
1151    /// ```
1152    ///
1153    /// # Example: error because of overflow
1154    ///
1155    /// In some cases, it's possible for `Zoned` value to be able to represent
1156    /// an instant in time earlier in the day for a particular time zone, but
1157    /// not later in the day. This can only occur near the maximum datetime
1158    /// value supported by Jiff.
1159    ///
1160    /// ```
1161    /// use jiff::{civil::date, tz::{TimeZone, Offset}, Zoned};
1162    ///
1163    /// // While 9999-12-30T01:30-04 is representable as a Zoned
1164    /// // value, the start of the corresponding day is not!
1165    /// let tz = TimeZone::get("America/New_York")?;
1166    /// let zdt = date(9999, 12, 30).at(1, 30, 0, 0).to_zoned(tz.clone())?;
1167    /// assert!(zdt.end_of_day().is_err());
1168    /// // The previous day works fine since 9999-12-29T23:59:59.999999999-04
1169    /// // is representable.
1170    /// let zdt = date(9999, 12, 29).at(1, 30, 0, 0).to_zoned(tz.clone())?;
1171    /// assert_eq!(
1172    ///     zdt.end_of_day()?,
1173    ///     date(9999, 12, 29)
1174    ///         .at(23, 59, 59, 999_999_999)
1175    ///         .in_tz("America/New_York")?,
1176    /// );
1177    ///
1178    /// # Ok::<(), Box<dyn std::error::Error>>(())
1179    /// ```
1180    #[inline]
1181    pub fn end_of_day(&self) -> Result<Zoned, Error> {
1182        let end_of_civil_day = self.datetime().end_of_day();
1183        let ambts = self.time_zone().to_ambiguous_timestamp(end_of_civil_day);
1184        // I'm not sure if there are any real world cases where this matters,
1185        // but this is basically the reverse of `compatible`, so we write
1186        // it out ourselves. Basically, if the last civil datetime is in a
1187        // gap, then we want the earlier instant since the later instant must
1188        // necessarily be in the next day. And if the last civil datetime is
1189        // in a fold, then we want the later instant since both the earlier
1190        // and later instants are in the same calendar day and the later one
1191        // must be, well, later. In contrast, compatible mode takes the later
1192        // instant in a gap and the earlier instant in a fold. So we flip that
1193        // here.
1194        let offset = match ambts.offset() {
1195            AmbiguousOffset::Unambiguous { offset } => offset,
1196            AmbiguousOffset::Gap { after, .. } => after,
1197            AmbiguousOffset::Fold { after, .. } => after,
1198        };
1199        offset
1200            .to_timestamp(end_of_civil_day)
1201            .map(|ts| ts.to_zoned(self.time_zone().clone()))
1202    }
1203
1204    /// Returns the first date of the month that this zoned datetime resides
1205    /// in.
1206    ///
1207    /// In most cases, the time in the zoned datetime returned remains
1208    /// unchanged. In some cases, the time may change if the time
1209    /// on the previous date was unambiguous (always true, since a
1210    /// `Zoned` is a precise instant in time) and the same clock time
1211    /// on the returned zoned datetime is ambiguous. In this case, the
1212    /// [`Disambiguation::Compatible`]
1213    /// strategy will be used to turn it into a precise instant. If you want to
1214    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1215    /// to get the civil datetime, then use [`DateTime::first_of_month`],
1216    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1217    /// disambiguation strategy.
1218    ///
1219    /// # Example
1220    ///
1221    /// ```
1222    /// use jiff::civil::date;
1223    ///
1224    /// let zdt = date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?;
1225    /// assert_eq!(
1226    ///     zdt.first_of_month()?,
1227    ///     date(2024, 2, 1).at(7, 30, 0, 0).in_tz("America/New_York")?,
1228    /// );
1229    ///
1230    /// # Ok::<(), Box<dyn std::error::Error>>(())
1231    /// ```
1232    #[inline]
1233    pub fn first_of_month(&self) -> Result<Zoned, Error> {
1234        self.datetime().first_of_month().to_zoned(self.time_zone().clone())
1235    }
1236
1237    /// Returns the last date of the month that this zoned datetime resides in.
1238    ///
1239    /// In most cases, the time in the zoned datetime returned remains
1240    /// unchanged. In some cases, the time may change if the time
1241    /// on the previous date was unambiguous (always true, since a
1242    /// `Zoned` is a precise instant in time) and the same clock time
1243    /// on the returned zoned datetime is ambiguous. In this case, the
1244    /// [`Disambiguation::Compatible`]
1245    /// strategy will be used to turn it into a precise instant. If you want to
1246    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1247    /// to get the civil datetime, then use [`DateTime::last_of_month`],
1248    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1249    /// disambiguation strategy.
1250    ///
1251    /// # Example
1252    ///
1253    /// ```
1254    /// use jiff::civil::date;
1255    ///
1256    /// let zdt = date(2024, 2, 5).at(7, 30, 0, 0).in_tz("America/New_York")?;
1257    /// assert_eq!(
1258    ///     zdt.last_of_month()?,
1259    ///     date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?,
1260    /// );
1261    ///
1262    /// # Ok::<(), Box<dyn std::error::Error>>(())
1263    /// ```
1264    #[inline]
1265    pub fn last_of_month(&self) -> Result<Zoned, Error> {
1266        self.datetime().last_of_month().to_zoned(self.time_zone().clone())
1267    }
1268
1269    /// Returns the ordinal number of the last day in the month in which this
1270    /// zoned datetime resides.
1271    ///
1272    /// This is phrased as "the ordinal number of the last day" instead of "the
1273    /// number of days" because some months may be missing days due to time
1274    /// zone transitions. However, this is extraordinarily rare.
1275    ///
1276    /// This is guaranteed to always return one of the following values,
1277    /// depending on the year and the month: 28, 29, 30 or 31.
1278    ///
1279    /// # Example
1280    ///
1281    /// ```
1282    /// use jiff::civil::date;
1283    ///
1284    /// let zdt = date(2024, 2, 10).at(7, 30, 0, 0).in_tz("America/New_York")?;
1285    /// assert_eq!(zdt.days_in_month(), 29);
1286    ///
1287    /// let zdt = date(2023, 2, 10).at(7, 30, 0, 0).in_tz("America/New_York")?;
1288    /// assert_eq!(zdt.days_in_month(), 28);
1289    ///
1290    /// let zdt = date(2024, 8, 15).at(7, 30, 0, 0).in_tz("America/New_York")?;
1291    /// assert_eq!(zdt.days_in_month(), 31);
1292    ///
1293    /// # Ok::<(), Box<dyn std::error::Error>>(())
1294    /// ```
1295    ///
1296    /// # Example: count of days in month
1297    ///
1298    /// In `Pacific/Apia`, December 2011 did not have a December 30. Instead,
1299    /// the calendar [skipped from December 29 right to December 31][samoa].
1300    ///
1301    /// If you really do need the count of days in a month in a time zone
1302    /// aware fashion, then it's possible to achieve through arithmetic:
1303    ///
1304    /// ```
1305    /// use jiff::{civil::date, RoundMode, ToSpan, Unit, ZonedDifference};
1306    ///
1307    /// let first_of_month = date(2011, 12, 1).in_tz("Pacific/Apia")?;
1308    /// assert_eq!(first_of_month.days_in_month(), 31);
1309    /// let one_month_later = first_of_month.checked_add(1.month())?;
1310    ///
1311    /// let options = ZonedDifference::new(&one_month_later)
1312    ///     .largest(Unit::Hour)
1313    ///     .smallest(Unit::Hour)
1314    ///     .mode(RoundMode::HalfExpand);
1315    /// let span = first_of_month.until(options)?;
1316    /// let days = ((span.get_hours() as f64) / 24.0).round() as i64;
1317    /// // Try the above in a different time zone, like America/New_York, and
1318    /// // you'll get 31 here.
1319    /// assert_eq!(days, 30);
1320    ///
1321    /// # Ok::<(), Box<dyn std::error::Error>>(())
1322    /// ```
1323    ///
1324    /// [samoa]: https://en.wikipedia.org/wiki/Time_in_Samoa#2011_time_zone_change
1325    #[inline]
1326    pub fn days_in_month(&self) -> i8 {
1327        self.date().days_in_month()
1328    }
1329
1330    /// Returns the first date of the year that this zoned datetime resides in.
1331    ///
1332    /// In most cases, the time in the zoned datetime returned remains
1333    /// unchanged. In some cases, the time may change if the time
1334    /// on the previous date was unambiguous (always true, since a
1335    /// `Zoned` is a precise instant in time) and the same clock time
1336    /// on the returned zoned datetime is ambiguous. In this case, the
1337    /// [`Disambiguation::Compatible`]
1338    /// strategy will be used to turn it into a precise instant. If you want to
1339    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1340    /// to get the civil datetime, then use [`DateTime::first_of_year`],
1341    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1342    /// disambiguation strategy.
1343    ///
1344    /// # Example
1345    ///
1346    /// ```
1347    /// use jiff::civil::date;
1348    ///
1349    /// let zdt = date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?;
1350    /// assert_eq!(
1351    ///     zdt.first_of_year()?,
1352    ///     date(2024, 1, 1).at(7, 30, 0, 0).in_tz("America/New_York")?,
1353    /// );
1354    ///
1355    /// # Ok::<(), Box<dyn std::error::Error>>(())
1356    /// ```
1357    #[inline]
1358    pub fn first_of_year(&self) -> Result<Zoned, Error> {
1359        self.datetime().first_of_year().to_zoned(self.time_zone().clone())
1360    }
1361
1362    /// Returns the last date of the year that this zoned datetime resides in.
1363    ///
1364    /// In most cases, the time in the zoned datetime returned remains
1365    /// unchanged. In some cases, the time may change if the time
1366    /// on the previous date was unambiguous (always true, since a
1367    /// `Zoned` is a precise instant in time) and the same clock time
1368    /// on the returned zoned datetime is ambiguous. In this case, the
1369    /// [`Disambiguation::Compatible`]
1370    /// strategy will be used to turn it into a precise instant. If you want to
1371    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1372    /// to get the civil datetime, then use [`DateTime::last_of_year`],
1373    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1374    /// disambiguation strategy.
1375    ///
1376    /// # Example
1377    ///
1378    /// ```
1379    /// use jiff::civil::date;
1380    ///
1381    /// let zdt = date(2024, 2, 5).at(7, 30, 0, 0).in_tz("America/New_York")?;
1382    /// assert_eq!(
1383    ///     zdt.last_of_year()?,
1384    ///     date(2024, 12, 31).at(7, 30, 0, 0).in_tz("America/New_York")?,
1385    /// );
1386    ///
1387    /// # Ok::<(), Box<dyn std::error::Error>>(())
1388    /// ```
1389    #[inline]
1390    pub fn last_of_year(&self) -> Result<Zoned, Error> {
1391        self.datetime().last_of_year().to_zoned(self.time_zone().clone())
1392    }
1393
1394    /// Returns the ordinal number of the last day in the year in which this
1395    /// zoned datetime resides.
1396    ///
1397    /// This is phrased as "the ordinal number of the last day" instead of "the
1398    /// number of days" because some years may be missing days due to time
1399    /// zone transitions. However, this is extraordinarily rare.
1400    ///
1401    /// This is guaranteed to always return either `365` or `366`.
1402    ///
1403    /// # Example
1404    ///
1405    /// ```
1406    /// use jiff::civil::date;
1407    ///
1408    /// let zdt = date(2024, 7, 10).at(7, 30, 0, 0).in_tz("America/New_York")?;
1409    /// assert_eq!(zdt.days_in_year(), 366);
1410    ///
1411    /// let zdt = date(2023, 7, 10).at(7, 30, 0, 0).in_tz("America/New_York")?;
1412    /// assert_eq!(zdt.days_in_year(), 365);
1413    ///
1414    /// # Ok::<(), Box<dyn std::error::Error>>(())
1415    /// ```
1416    #[inline]
1417    pub fn days_in_year(&self) -> i16 {
1418        self.date().days_in_year()
1419    }
1420
1421    /// Returns true if and only if the year in which this zoned datetime
1422    /// resides is a leap year.
1423    ///
1424    /// # Example
1425    ///
1426    /// ```
1427    /// use jiff::civil::date;
1428    ///
1429    /// let zdt = date(2024, 1, 1).at(7, 30, 0, 0).in_tz("America/New_York")?;
1430    /// assert!(zdt.in_leap_year());
1431    ///
1432    /// let zdt = date(2023, 12, 31).at(7, 30, 0, 0).in_tz("America/New_York")?;
1433    /// assert!(!zdt.in_leap_year());
1434    ///
1435    /// # Ok::<(), Box<dyn std::error::Error>>(())
1436    /// ```
1437    #[inline]
1438    pub fn in_leap_year(&self) -> bool {
1439        self.date().in_leap_year()
1440    }
1441
1442    /// Returns the zoned datetime with a date immediately following this one.
1443    ///
1444    /// In most cases, the time in the zoned datetime returned remains
1445    /// unchanged. In some cases, the time may change if the time
1446    /// on the previous date was unambiguous (always true, since a
1447    /// `Zoned` is a precise instant in time) and the same clock time
1448    /// on the returned zoned datetime is ambiguous. In this case, the
1449    /// [`Disambiguation::Compatible`]
1450    /// strategy will be used to turn it into a precise instant. If you want to
1451    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1452    /// to get the civil datetime, then use [`DateTime::tomorrow`],
1453    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1454    /// disambiguation strategy.
1455    ///
1456    /// # Errors
1457    ///
1458    /// This returns an error when one day following this zoned datetime would
1459    /// exceed the maximum `Zoned` value.
1460    ///
1461    /// # Example
1462    ///
1463    /// ```
1464    /// use jiff::{civil::date, Timestamp};
1465    ///
1466    /// let zdt = date(2024, 2, 28).at(7, 30, 0, 0).in_tz("America/New_York")?;
1467    /// assert_eq!(
1468    ///     zdt.tomorrow()?,
1469    ///     date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?,
1470    /// );
1471    ///
1472    /// // The max doesn't have a tomorrow.
1473    /// assert!(Timestamp::MAX.in_tz("America/New_York")?.tomorrow().is_err());
1474    ///
1475    /// # Ok::<(), Box<dyn std::error::Error>>(())
1476    /// ```
1477    ///
1478    /// # Example: ambiguous datetimes are automatically resolved
1479    ///
1480    /// ```
1481    /// use jiff::{civil::date, Timestamp};
1482    ///
1483    /// let zdt = date(2024, 3, 9).at(2, 30, 0, 0).in_tz("America/New_York")?;
1484    /// assert_eq!(
1485    ///     zdt.tomorrow()?,
1486    ///     date(2024, 3, 10).at(3, 30, 0, 0).in_tz("America/New_York")?,
1487    /// );
1488    ///
1489    /// # Ok::<(), Box<dyn std::error::Error>>(())
1490    /// ```
1491    #[inline]
1492    pub fn tomorrow(&self) -> Result<Zoned, Error> {
1493        self.datetime().tomorrow()?.to_zoned(self.time_zone().clone())
1494    }
1495
1496    /// Returns the zoned datetime with a date immediately preceding this one.
1497    ///
1498    /// In most cases, the time in the zoned datetime returned remains
1499    /// unchanged. In some cases, the time may change if the time
1500    /// on the previous date was unambiguous (always true, since a
1501    /// `Zoned` is a precise instant in time) and the same clock time
1502    /// on the returned zoned datetime is ambiguous. In this case, the
1503    /// [`Disambiguation::Compatible`]
1504    /// strategy will be used to turn it into a precise instant. If you want to
1505    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1506    /// to get the civil datetime, then use [`DateTime::yesterday`],
1507    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1508    /// disambiguation strategy.
1509    ///
1510    /// # Errors
1511    ///
1512    /// This returns an error when one day preceding this zoned datetime would
1513    /// be less than the minimum `Zoned` value.
1514    ///
1515    /// # Example
1516    ///
1517    /// ```
1518    /// use jiff::{civil::date, Timestamp};
1519    ///
1520    /// let zdt = date(2024, 3, 1).at(7, 30, 0, 0).in_tz("America/New_York")?;
1521    /// assert_eq!(
1522    ///     zdt.yesterday()?,
1523    ///     date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?,
1524    /// );
1525    ///
1526    /// // The min doesn't have a yesterday.
1527    /// assert!(Timestamp::MIN.in_tz("America/New_York")?.yesterday().is_err());
1528    ///
1529    /// # Ok::<(), Box<dyn std::error::Error>>(())
1530    /// ```
1531    ///
1532    /// # Example: ambiguous datetimes are automatically resolved
1533    ///
1534    /// ```
1535    /// use jiff::{civil::date, Timestamp};
1536    ///
1537    /// let zdt = date(2024, 11, 4).at(1, 30, 0, 0).in_tz("America/New_York")?;
1538    /// assert_eq!(
1539    ///     zdt.yesterday()?.to_string(),
1540    ///     // Consistent with the "compatible" disambiguation strategy, the
1541    ///     // "first" 1 o'clock hour is selected. You can tell this because
1542    ///     // the offset is -04, which corresponds to DST time in New York.
1543    ///     // The second 1 o'clock hour would have offset -05.
1544    ///     "2024-11-03T01:30:00-04:00[America/New_York]",
1545    /// );
1546    ///
1547    /// # Ok::<(), Box<dyn std::error::Error>>(())
1548    /// ```
1549    #[inline]
1550    pub fn yesterday(&self) -> Result<Zoned, Error> {
1551        self.datetime().yesterday()?.to_zoned(self.time_zone().clone())
1552    }
1553
1554    /// Returns the "nth" weekday from the beginning or end of the month in
1555    /// which this zoned datetime resides.
1556    ///
1557    /// The `nth` parameter can be positive or negative. A positive value
1558    /// computes the "nth" weekday from the beginning of the month. A negative
1559    /// value computes the "nth" weekday from the end of the month. So for
1560    /// example, use `-1` to "find the last weekday" in this date's month.
1561    ///
1562    /// In most cases, the time in the zoned datetime returned remains
1563    /// unchanged. In some cases, the time may change if the time
1564    /// on the previous date was unambiguous (always true, since a
1565    /// `Zoned` is a precise instant in time) and the same clock time
1566    /// on the returned zoned datetime is ambiguous. In this case, the
1567    /// [`Disambiguation::Compatible`]
1568    /// strategy will be used to turn it into a precise instant. If you want to
1569    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1570    /// to get the civil datetime, then use [`DateTime::nth_weekday_of_month`],
1571    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1572    /// disambiguation strategy.
1573    ///
1574    /// # Errors
1575    ///
1576    /// This returns an error when `nth` is `0`, or if it is `5` or `-5` and
1577    /// there is no 5th weekday from the beginning or end of the month. This
1578    /// could also return an error if the corresponding datetime could not be
1579    /// represented as an instant for this `Zoned`'s time zone. (This can only
1580    /// happen close the boundaries of an [`Timestamp`].)
1581    ///
1582    /// # Example
1583    ///
1584    /// This shows how to get the nth weekday in a month, starting from the
1585    /// beginning of the month:
1586    ///
1587    /// ```
1588    /// use jiff::civil::{Weekday, date};
1589    ///
1590    /// let zdt = date(2017, 3, 1).at(7, 30, 0, 0).in_tz("America/New_York")?;
1591    /// let second_friday = zdt.nth_weekday_of_month(2, Weekday::Friday)?;
1592    /// assert_eq!(
1593    ///     second_friday,
1594    ///     date(2017, 3, 10).at(7, 30, 0, 0).in_tz("America/New_York")?,
1595    /// );
1596    ///
1597    /// # Ok::<(), Box<dyn std::error::Error>>(())
1598    /// ```
1599    ///
1600    /// This shows how to do the reverse of the above. That is, the nth _last_
1601    /// weekday in a month:
1602    ///
1603    /// ```
1604    /// use jiff::civil::{Weekday, date};
1605    ///
1606    /// let zdt = date(2024, 3, 1).at(7, 30, 0, 0).in_tz("America/New_York")?;
1607    /// let last_thursday = zdt.nth_weekday_of_month(-1, Weekday::Thursday)?;
1608    /// assert_eq!(
1609    ///     last_thursday,
1610    ///     date(2024, 3, 28).at(7, 30, 0, 0).in_tz("America/New_York")?,
1611    /// );
1612    ///
1613    /// let second_last_thursday = zdt.nth_weekday_of_month(
1614    ///     -2,
1615    ///     Weekday::Thursday,
1616    /// )?;
1617    /// assert_eq!(
1618    ///     second_last_thursday,
1619    ///     date(2024, 3, 21).at(7, 30, 0, 0).in_tz("America/New_York")?,
1620    /// );
1621    ///
1622    /// # Ok::<(), Box<dyn std::error::Error>>(())
1623    /// ```
1624    ///
1625    /// This routine can return an error if there isn't an `nth` weekday
1626    /// for this month. For example, March 2024 only has 4 Mondays:
1627    ///
1628    /// ```
1629    /// use jiff::civil::{Weekday, date};
1630    ///
1631    /// let zdt = date(2024, 3, 25).at(7, 30, 0, 0).in_tz("America/New_York")?;
1632    /// let fourth_monday = zdt.nth_weekday_of_month(4, Weekday::Monday)?;
1633    /// assert_eq!(
1634    ///     fourth_monday,
1635    ///     date(2024, 3, 25).at(7, 30, 0, 0).in_tz("America/New_York")?,
1636    /// );
1637    /// // There is no 5th Monday.
1638    /// assert!(zdt.nth_weekday_of_month(5, Weekday::Monday).is_err());
1639    /// // Same goes for counting backwards.
1640    /// assert!(zdt.nth_weekday_of_month(-5, Weekday::Monday).is_err());
1641    ///
1642    /// # Ok::<(), Box<dyn std::error::Error>>(())
1643    /// ```
1644    #[inline]
1645    pub fn nth_weekday_of_month(
1646        &self,
1647        nth: i8,
1648        weekday: Weekday,
1649    ) -> Result<Zoned, Error> {
1650        self.datetime()
1651            .nth_weekday_of_month(nth, weekday)?
1652            .to_zoned(self.time_zone().clone())
1653    }
1654
1655    /// Returns the "nth" weekday from this zoned datetime, not including
1656    /// itself.
1657    ///
1658    /// The `nth` parameter can be positive or negative. A positive value
1659    /// computes the "nth" weekday starting at the day after this date and
1660    /// going forwards in time. A negative value computes the "nth" weekday
1661    /// starting at the day before this date and going backwards in time.
1662    ///
1663    /// For example, if this zoned datetime's weekday is a Sunday and the first
1664    /// Sunday is asked for (that is, `zdt.nth_weekday(1, Weekday::Sunday)`),
1665    /// then the result is a week from this zoned datetime corresponding to the
1666    /// following Sunday.
1667    ///
1668    /// In most cases, the time in the zoned datetime returned remains
1669    /// unchanged. In some cases, the time may change if the time
1670    /// on the previous date was unambiguous (always true, since a
1671    /// `Zoned` is a precise instant in time) and the same clock time
1672    /// on the returned zoned datetime is ambiguous. In this case, the
1673    /// [`Disambiguation::Compatible`]
1674    /// strategy will be used to turn it into a precise instant. If you want to
1675    /// use a different disambiguation strategy, then use [`Zoned::datetime`]
1676    /// to get the civil datetime, then use [`DateTime::nth_weekday`],
1677    /// then use [`TimeZone::to_ambiguous_zoned`] and apply your preferred
1678    /// disambiguation strategy.
1679    ///
1680    /// # Errors
1681    ///
1682    /// This returns an error when `nth` is `0`, or if it would otherwise
1683    /// result in a date that overflows the minimum/maximum values of
1684    /// `Zoned`.
1685    ///
1686    /// # Example
1687    ///
1688    /// This example shows how to find the "nth" weekday going forwards in
1689    /// time:
1690    ///
1691    /// ```
1692    /// use jiff::civil::{Weekday, date};
1693    ///
1694    /// // Use a Sunday in March as our start date.
1695    /// let zdt = date(2024, 3, 10).at(7, 30, 0, 0).in_tz("America/New_York")?;
1696    /// assert_eq!(zdt.weekday(), Weekday::Sunday);
1697    ///
1698    /// // The first next Monday is tomorrow!
1699    /// let next_monday = zdt.nth_weekday(1, Weekday::Monday)?;
1700    /// assert_eq!(
1701    ///     next_monday,
1702    ///     date(2024, 3, 11).at(7, 30, 0, 0).in_tz("America/New_York")?,
1703    /// );
1704    ///
1705    /// // But the next Sunday is a week away, because this doesn't
1706    /// // include the current weekday.
1707    /// let next_sunday = zdt.nth_weekday(1, Weekday::Sunday)?;
1708    /// assert_eq!(
1709    ///     next_sunday,
1710    ///     date(2024, 3, 17).at(7, 30, 0, 0).in_tz("America/New_York")?,
1711    /// );
1712    ///
1713    /// // "not this Thursday, but next Thursday"
1714    /// let next_next_thursday = zdt.nth_weekday(2, Weekday::Thursday)?;
1715    /// assert_eq!(
1716    ///     next_next_thursday,
1717    ///     date(2024, 3, 21).at(7, 30, 0, 0).in_tz("America/New_York")?,
1718    /// );
1719    ///
1720    /// # Ok::<(), Box<dyn std::error::Error>>(())
1721    /// ```
1722    ///
1723    /// This example shows how to find the "nth" weekday going backwards in
1724    /// time:
1725    ///
1726    /// ```
1727    /// use jiff::civil::{Weekday, date};
1728    ///
1729    /// // Use a Sunday in March as our start date.
1730    /// let zdt = date(2024, 3, 10).at(7, 30, 0, 0).in_tz("America/New_York")?;
1731    /// assert_eq!(zdt.weekday(), Weekday::Sunday);
1732    ///
1733    /// // "last Saturday" was yesterday!
1734    /// let last_saturday = zdt.nth_weekday(-1, Weekday::Saturday)?;
1735    /// assert_eq!(
1736    ///     last_saturday,
1737    ///     date(2024, 3, 9).at(7, 30, 0, 0).in_tz("America/New_York")?,
1738    /// );
1739    ///
1740    /// // "last Sunday" was a week ago.
1741    /// let last_sunday = zdt.nth_weekday(-1, Weekday::Sunday)?;
1742    /// assert_eq!(
1743    ///     last_sunday,
1744    ///     date(2024, 3, 3).at(7, 30, 0, 0).in_tz("America/New_York")?,
1745    /// );
1746    ///
1747    /// // "not last Thursday, but the one before"
1748    /// let prev_prev_thursday = zdt.nth_weekday(-2, Weekday::Thursday)?;
1749    /// assert_eq!(
1750    ///     prev_prev_thursday,
1751    ///     date(2024, 2, 29).at(7, 30, 0, 0).in_tz("America/New_York")?,
1752    /// );
1753    ///
1754    /// # Ok::<(), Box<dyn std::error::Error>>(())
1755    /// ```
1756    ///
1757    /// This example shows that overflow results in an error in either
1758    /// direction:
1759    ///
1760    /// ```
1761    /// use jiff::{civil::Weekday, Timestamp};
1762    ///
1763    /// let zdt = Timestamp::MAX.in_tz("America/New_York")?;
1764    /// assert_eq!(zdt.weekday(), Weekday::Thursday);
1765    /// assert!(zdt.nth_weekday(1, Weekday::Saturday).is_err());
1766    ///
1767    /// let zdt = Timestamp::MIN.in_tz("America/New_York")?;
1768    /// assert_eq!(zdt.weekday(), Weekday::Monday);
1769    /// assert!(zdt.nth_weekday(-1, Weekday::Sunday).is_err());
1770    ///
1771    /// # Ok::<(), Box<dyn std::error::Error>>(())
1772    /// ```
1773    ///
1774    /// # Example: getting the start of the week
1775    ///
1776    /// Given a date, one can use `nth_weekday` to determine the start of the
1777    /// week in which the date resides in. This might vary based on whether
1778    /// the weeks start on Sunday or Monday. This example shows how to handle
1779    /// both.
1780    ///
1781    /// ```
1782    /// use jiff::civil::{Weekday, date};
1783    ///
1784    /// let zdt = date(2024, 3, 15).at(7, 30, 0, 0).in_tz("America/New_York")?;
1785    /// // For weeks starting with Sunday.
1786    /// let start_of_week = zdt.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?;
1787    /// assert_eq!(
1788    ///     start_of_week,
1789    ///     date(2024, 3, 10).at(7, 30, 0, 0).in_tz("America/New_York")?,
1790    /// );
1791    /// // For weeks starting with Monday.
1792    /// let start_of_week = zdt.tomorrow()?.nth_weekday(-1, Weekday::Monday)?;
1793    /// assert_eq!(
1794    ///     start_of_week,
1795    ///     date(2024, 3, 11).at(7, 30, 0, 0).in_tz("America/New_York")?,
1796    /// );
1797    ///
1798    /// # Ok::<(), Box<dyn std::error::Error>>(())
1799    /// ```
1800    ///
1801    /// In the above example, we first get the date after the current one
1802    /// because `nth_weekday` does not consider itself when counting. This
1803    /// works as expected even at the boundaries of a week:
1804    ///
1805    /// ```
1806    /// use jiff::civil::{Time, Weekday, date};
1807    ///
1808    /// // The start of the week.
1809    /// let zdt = date(2024, 3, 10).at(0, 0, 0, 0).in_tz("America/New_York")?;
1810    /// let start_of_week = zdt.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?;
1811    /// assert_eq!(
1812    ///     start_of_week,
1813    ///     date(2024, 3, 10).at(0, 0, 0, 0).in_tz("America/New_York")?,
1814    /// );
1815    /// // The end of the week.
1816    /// let zdt = date(2024, 3, 16)
1817    ///     .at(23, 59, 59, 999_999_999)
1818    ///     .in_tz("America/New_York")?;
1819    /// let start_of_week = zdt
1820    ///     .tomorrow()?
1821    ///     .nth_weekday(-1, Weekday::Sunday)?
1822    ///     .with().time(Time::midnight()).build()?;
1823    /// assert_eq!(
1824    ///     start_of_week,
1825    ///     date(2024, 3, 10).at(0, 0, 0, 0).in_tz("America/New_York")?,
1826    /// );
1827    ///
1828    /// # Ok::<(), Box<dyn std::error::Error>>(())
1829    /// ```
1830    #[inline]
1831    pub fn nth_weekday(
1832        &self,
1833        nth: i32,
1834        weekday: Weekday,
1835    ) -> Result<Zoned, Error> {
1836        self.datetime()
1837            .nth_weekday(nth, weekday)?
1838            .to_zoned(self.time_zone().clone())
1839    }
1840
1841    /// Returns the precise instant in time referred to by this zoned datetime.
1842    ///
1843    /// # Example
1844    ///
1845    /// ```
1846    /// use jiff::civil::date;
1847    ///
1848    /// let zdt = date(2024, 3, 14).at(18, 45, 0, 0).in_tz("America/New_York")?;
1849    /// assert_eq!(zdt.timestamp().as_second(), 1_710_456_300);
1850    ///
1851    /// # Ok::<(), Box<dyn std::error::Error>>(())
1852    /// ```
1853    #[inline]
1854    pub fn timestamp(&self) -> Timestamp {
1855        self.inner.timestamp
1856    }
1857
1858    /// Returns the civil datetime component of this zoned datetime.
1859    ///
1860    /// # Example
1861    ///
1862    /// ```
1863    /// use jiff::civil::date;
1864    ///
1865    /// let zdt = date(2024, 3, 14).at(18, 45, 0, 0).in_tz("America/New_York")?;
1866    /// assert_eq!(zdt.datetime(), date(2024, 3, 14).at(18, 45, 0, 0));
1867    ///
1868    /// # Ok::<(), Box<dyn std::error::Error>>(())
1869    /// ```
1870    #[inline]
1871    pub fn datetime(&self) -> DateTime {
1872        self.inner.datetime
1873    }
1874
1875    /// Returns the civil date component of this zoned datetime.
1876    ///
1877    /// # Example
1878    ///
1879    /// ```
1880    /// use jiff::civil::date;
1881    ///
1882    /// let zdt = date(2024, 3, 14).at(18, 45, 0, 0).in_tz("America/New_York")?;
1883    /// assert_eq!(zdt.date(), date(2024, 3, 14));
1884    ///
1885    /// # Ok::<(), Box<dyn std::error::Error>>(())
1886    /// ```
1887    #[inline]
1888    pub fn date(&self) -> Date {
1889        self.datetime().date()
1890    }
1891
1892    /// Returns the civil time component of this zoned datetime.
1893    ///
1894    /// # Example
1895    ///
1896    /// ```
1897    /// use jiff::civil::{date, time};
1898    ///
1899    /// let zdt = date(2024, 3, 14).at(18, 45, 0, 0).in_tz("America/New_York")?;
1900    /// assert_eq!(zdt.time(), time(18, 45, 0, 0));
1901    ///
1902    /// # Ok::<(), Box<dyn std::error::Error>>(())
1903    /// ```
1904    #[inline]
1905    pub fn time(&self) -> Time {
1906        self.datetime().time()
1907    }
1908
1909    /// Construct a civil [ISO 8601 week date] from this zoned datetime.
1910    ///
1911    /// The [`ISOWeekDate`] type describes itself in more detail, but in
1912    /// brief, the ISO week date calendar system eschews months in favor of
1913    /// weeks.
1914    ///
1915    /// This routine is equivalent to
1916    /// [`ISOWeekDate::from_date(zdt.date())`](ISOWeekDate::from_date).
1917    ///
1918    /// [ISO 8601 week date]: https://en.wikipedia.org/wiki/ISO_week_date
1919    ///
1920    /// # Example
1921    ///
1922    /// This shows a number of examples demonstrating the conversion from a
1923    /// Gregorian date to an ISO 8601 week date:
1924    ///
1925    /// ```
1926    /// use jiff::civil::{Date, Time, Weekday, date};
1927    ///
1928    /// let zdt = date(1995, 1, 1).at(18, 45, 0, 0).in_tz("US/Eastern")?;
1929    /// let weekdate = zdt.iso_week_date();
1930    /// assert_eq!(weekdate.year(), 1994);
1931    /// assert_eq!(weekdate.week(), 52);
1932    /// assert_eq!(weekdate.weekday(), Weekday::Sunday);
1933    ///
1934    /// let zdt = date(1996, 12, 31).at(18, 45, 0, 0).in_tz("US/Eastern")?;
1935    /// let weekdate = zdt.iso_week_date();
1936    /// assert_eq!(weekdate.year(), 1997);
1937    /// assert_eq!(weekdate.week(), 1);
1938    /// assert_eq!(weekdate.weekday(), Weekday::Tuesday);
1939    ///
1940    /// let zdt = date(2019, 12, 30).at(18, 45, 0, 0).in_tz("US/Eastern")?;
1941    /// let weekdate = zdt.iso_week_date();
1942    /// assert_eq!(weekdate.year(), 2020);
1943    /// assert_eq!(weekdate.week(), 1);
1944    /// assert_eq!(weekdate.weekday(), Weekday::Monday);
1945    ///
1946    /// let zdt = date(2024, 3, 9).at(18, 45, 0, 0).in_tz("US/Eastern")?;
1947    /// let weekdate = zdt.iso_week_date();
1948    /// assert_eq!(weekdate.year(), 2024);
1949    /// assert_eq!(weekdate.week(), 10);
1950    /// assert_eq!(weekdate.weekday(), Weekday::Saturday);
1951    ///
1952    /// # Ok::<(), Box<dyn std::error::Error>>(())
1953    /// ```
1954    #[inline]
1955    pub fn iso_week_date(self) -> ISOWeekDate {
1956        self.date().iso_week_date()
1957    }
1958
1959    /// Returns the time zone offset of this zoned datetime.
1960    ///
1961    /// # Example
1962    ///
1963    /// ```
1964    /// use jiff::civil::date;
1965    ///
1966    /// let zdt = date(2024, 2, 14).at(18, 45, 0, 0).in_tz("America/New_York")?;
1967    /// // -05 because New York is in "standard" time at this point.
1968    /// assert_eq!(zdt.offset(), jiff::tz::offset(-5));
1969    ///
1970    /// let zdt = date(2024, 7, 14).at(18, 45, 0, 0).in_tz("America/New_York")?;
1971    /// // But we get -04 once "summer" or "daylight saving time" starts.
1972    /// assert_eq!(zdt.offset(), jiff::tz::offset(-4));
1973    ///
1974    /// # Ok::<(), Box<dyn std::error::Error>>(())
1975    /// ```
1976    #[inline]
1977    pub fn offset(&self) -> Offset {
1978        self.inner.offset
1979    }
1980
1981    /// Add the given span of time to this zoned datetime. If the sum would
1982    /// overflow the minimum or maximum zoned datetime values, then an error is
1983    /// returned.
1984    ///
1985    /// This operation accepts three different duration types: [`Span`],
1986    /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via
1987    /// `From` trait implementations for the [`ZonedArithmetic`] type.
1988    ///
1989    /// # Properties
1990    ///
1991    /// This routine is _not_ reversible because some additions may
1992    /// be ambiguous. For example, adding `1 month` to the zoned
1993    /// datetime `2024-03-31T00:00:00[America/New_York]` will produce
1994    /// `2024-04-30T00:00:00[America/New_York]` since April has
1995    /// only 30 days in a month. Moreover, subtracting `1 month`
1996    /// from `2024-04-30T00:00:00[America/New_York]` will produce
1997    /// `2024-03-30T00:00:00[America/New_York]`, which is not the date we
1998    /// started with.
1999    ///
2000    /// A similar argument applies for days, since with zoned datetimes,
2001    /// different days can be different lengths.
2002    ///
2003    /// If spans of time are limited to units of hours (or less), then this
2004    /// routine _is_ reversible. This also implies that all operations with a
2005    /// [`SignedDuration`] or a [`std::time::Duration`] are reversible.
2006    ///
2007    /// # Errors
2008    ///
2009    /// If the span added to this zoned datetime would result in a zoned
2010    /// datetime that exceeds the range of a `Zoned`, then this will return an
2011    /// error.
2012    ///
2013    /// # Example
2014    ///
2015    /// This shows a few examples of adding spans of time to various zoned
2016    /// datetimes. We make use of the [`ToSpan`](crate::ToSpan) trait for
2017    /// convenient creation of spans.
2018    ///
2019    /// ```
2020    /// use jiff::{civil::date, ToSpan};
2021    ///
2022    /// let zdt = date(1995, 12, 7)
2023    ///     .at(3, 24, 30, 3_500)
2024    ///     .in_tz("America/New_York")?;
2025    /// let got = zdt.checked_add(20.years().months(4).nanoseconds(500))?;
2026    /// assert_eq!(
2027    ///     got,
2028    ///     date(2016, 4, 7).at(3, 24, 30, 4_000).in_tz("America/New_York")?,
2029    /// );
2030    ///
2031    /// let zdt = date(2019, 1, 31).at(15, 30, 0, 0).in_tz("America/New_York")?;
2032    /// let got = zdt.checked_add(1.months())?;
2033    /// assert_eq!(
2034    ///     got,
2035    ///     date(2019, 2, 28).at(15, 30, 0, 0).in_tz("America/New_York")?,
2036    /// );
2037    ///
2038    /// # Ok::<(), Box<dyn std::error::Error>>(())
2039    /// ```
2040    ///
2041    /// # Example: available via addition operator
2042    ///
2043    /// This routine can be used via the `+` operator. Note though that if it
2044    /// fails, it will result in a panic. Note that we use `&zdt + ...` instead
2045    /// of `zdt + ...` since `Add` is implemented for `&Zoned` and not `Zoned`.
2046    /// This is because `Zoned` is not `Copy`.
2047    ///
2048    /// ```
2049    /// use jiff::{civil::date, ToSpan};
2050    ///
2051    /// let zdt = date(1995, 12, 7)
2052    ///     .at(3, 24, 30, 3_500)
2053    ///     .in_tz("America/New_York")?;
2054    /// let got = &zdt + 20.years().months(4).nanoseconds(500);
2055    /// assert_eq!(
2056    ///     got,
2057    ///     date(2016, 4, 7).at(3, 24, 30, 4_000).in_tz("America/New_York")?,
2058    /// );
2059    ///
2060    /// # Ok::<(), Box<dyn std::error::Error>>(())
2061    /// ```
2062    ///
2063    /// # Example: zone aware arithmetic
2064    ///
2065    /// This example demonstrates the difference between "add 1 day" and
2066    /// "add 24 hours." In the former case, 1 day might not correspond to 24
2067    /// hours if there is a time zone transition in the intervening period.
2068    /// However, adding 24 hours always means adding exactly 24 hours.
2069    ///
2070    /// ```
2071    /// use jiff::{civil::date, ToSpan};
2072    ///
2073    /// let zdt = date(2024, 3, 10).at(0, 0, 0, 0).in_tz("America/New_York")?;
2074    ///
2075    /// let one_day_later = zdt.checked_add(1.day())?;
2076    /// assert_eq!(
2077    ///     one_day_later.to_string(),
2078    ///     "2024-03-11T00:00:00-04:00[America/New_York]",
2079    /// );
2080    ///
2081    /// let twenty_four_hours_later = zdt.checked_add(24.hours())?;
2082    /// assert_eq!(
2083    ///     twenty_four_hours_later.to_string(),
2084    ///     "2024-03-11T01:00:00-04:00[America/New_York]",
2085    /// );
2086    ///
2087    /// # Ok::<(), Box<dyn std::error::Error>>(())
2088    /// ```
2089    ///
2090    /// # Example: automatic disambiguation
2091    ///
2092    /// This example demonstrates what happens when adding a span
2093    /// of time results in an ambiguous zoned datetime. Zone aware
2094    /// arithmetic uses automatic disambiguation corresponding to the
2095    /// [`Disambiguation::Compatible`]
2096    /// strategy for resolving an ambiguous datetime to a precise instant.
2097    /// For example, in the case below, there is a gap in the clocks for 1
2098    /// hour starting at `2024-03-10 02:00:00` in `America/New_York`. The
2099    /// "compatible" strategy chooses the later time in a gap:.
2100    ///
2101    /// ```
2102    /// use jiff::{civil::date, ToSpan};
2103    ///
2104    /// let zdt = date(2024, 3, 9).at(2, 30, 0, 0).in_tz("America/New_York")?;
2105    /// let one_day_later = zdt.checked_add(1.day())?;
2106    /// assert_eq!(
2107    ///     one_day_later.to_string(),
2108    ///     "2024-03-10T03:30:00-04:00[America/New_York]",
2109    /// );
2110    ///
2111    /// # Ok::<(), Box<dyn std::error::Error>>(())
2112    /// ```
2113    ///
2114    /// And this example demonstrates the "compatible" strategy when arithmetic
2115    /// results in an ambiguous datetime in a fold. In this case, we make use
2116    /// of the fact that the 1 o'clock hour was repeated on `2024-11-03`.
2117    ///
2118    /// ```
2119    /// use jiff::{civil::date, ToSpan};
2120    ///
2121    /// let zdt = date(2024, 11, 2).at(1, 30, 0, 0).in_tz("America/New_York")?;
2122    /// let one_day_later = zdt.checked_add(1.day())?;
2123    /// assert_eq!(
2124    ///     one_day_later.to_string(),
2125    ///     // This corresponds to the first iteration of the 1 o'clock hour,
2126    ///     // i.e., when DST is still in effect. It's the earlier time.
2127    ///     "2024-11-03T01:30:00-04:00[America/New_York]",
2128    /// );
2129    ///
2130    /// # Ok::<(), Box<dyn std::error::Error>>(())
2131    /// ```
2132    ///
2133    /// # Example: negative spans are supported
2134    ///
2135    /// ```
2136    /// use jiff::{civil::date, ToSpan};
2137    ///
2138    /// let zdt = date(2024, 3, 31)
2139    ///     .at(19, 5, 59, 999_999_999)
2140    ///     .in_tz("America/New_York")?;
2141    /// assert_eq!(
2142    ///     zdt.checked_add(-1.months())?,
2143    ///     date(2024, 2, 29).
2144    ///         at(19, 5, 59, 999_999_999)
2145    ///         .in_tz("America/New_York")?,
2146    /// );
2147    ///
2148    /// # Ok::<(), Box<dyn std::error::Error>>(())
2149    /// ```
2150    ///
2151    /// # Example: error on overflow
2152    ///
2153    /// ```
2154    /// use jiff::{civil::date, ToSpan};
2155    ///
2156    /// let zdt = date(2024, 3, 31).at(13, 13, 13, 13).in_tz("America/New_York")?;
2157    /// assert!(zdt.checked_add(9000.years()).is_err());
2158    /// assert!(zdt.checked_add(-19000.years()).is_err());
2159    ///
2160    /// # Ok::<(), Box<dyn std::error::Error>>(())
2161    /// ```
2162    ///
2163    /// # Example: adding absolute durations
2164    ///
2165    /// This shows how to add signed and unsigned absolute durations to a
2166    /// `Zoned`.
2167    ///
2168    /// ```
2169    /// use std::time::Duration;
2170    ///
2171    /// use jiff::{civil::date, SignedDuration};
2172    ///
2173    /// let zdt = date(2024, 2, 29).at(0, 0, 0, 0).in_tz("US/Eastern")?;
2174    ///
2175    /// let dur = SignedDuration::from_hours(25);
2176    /// assert_eq!(
2177    ///     zdt.checked_add(dur)?,
2178    ///     date(2024, 3, 1).at(1, 0, 0, 0).in_tz("US/Eastern")?,
2179    /// );
2180    /// assert_eq!(
2181    ///     zdt.checked_add(-dur)?,
2182    ///     date(2024, 2, 27).at(23, 0, 0, 0).in_tz("US/Eastern")?,
2183    /// );
2184    ///
2185    /// let dur = Duration::from_secs(25 * 60 * 60);
2186    /// assert_eq!(
2187    ///     zdt.checked_add(dur)?,
2188    ///     date(2024, 3, 1).at(1, 0, 0, 0).in_tz("US/Eastern")?,
2189    /// );
2190    /// // One cannot negate an unsigned duration,
2191    /// // but you can subtract it!
2192    /// assert_eq!(
2193    ///     zdt.checked_sub(dur)?,
2194    ///     date(2024, 2, 27).at(23, 0, 0, 0).in_tz("US/Eastern")?,
2195    /// );
2196    ///
2197    /// # Ok::<(), Box<dyn std::error::Error>>(())
2198    /// ```
2199    #[inline]
2200    pub fn checked_add<A: Into<ZonedArithmetic>>(
2201        &self,
2202        duration: A,
2203    ) -> Result<Zoned, Error> {
2204        let duration: ZonedArithmetic = duration.into();
2205        duration.checked_add(self)
2206    }
2207
2208    #[inline]
2209    fn checked_add_span(&self, span: Span) -> Result<Zoned, Error> {
2210        let span_calendar = span.only_calendar();
2211        // If our duration only consists of "time" (hours, minutes, etc), then
2212        // we can short-circuit and do timestamp math. This also avoids dealing
2213        // with ambiguity and time zone bullshit.
2214        if span_calendar.is_zero() {
2215            return self
2216                .timestamp()
2217                .checked_add(span)
2218                .map(|ts| ts.to_zoned(self.time_zone().clone()))
2219                .context(E::AddTimestamp);
2220        }
2221        let span_time = span.only_time();
2222        let dt = self
2223            .datetime()
2224            .checked_add(span_calendar)
2225            .context(E::AddDateTime)?;
2226
2227        let tz = self.time_zone();
2228        let mut ts = tz
2229            .to_ambiguous_timestamp(dt)
2230            .compatible()
2231            .context(E::ConvertDateTimeToTimestamp)?;
2232        ts = ts.checked_add(span_time).context(E::AddTimestamp)?;
2233        Ok(ts.to_zoned(tz.clone()))
2234    }
2235
2236    #[inline]
2237    fn checked_add_duration(
2238        &self,
2239        duration: SignedDuration,
2240    ) -> Result<Zoned, Error> {
2241        self.timestamp()
2242            .checked_add(duration)
2243            .map(|ts| ts.to_zoned(self.time_zone().clone()))
2244    }
2245
2246    /// This routine is identical to [`Zoned::checked_add`] with the
2247    /// duration negated.
2248    ///
2249    /// # Errors
2250    ///
2251    /// This has the same error conditions as [`Zoned::checked_add`].
2252    ///
2253    /// # Example
2254    ///
2255    /// This routine can be used via the `-` operator. Note though that if it
2256    /// fails, it will result in a panic. Note that we use `&zdt - ...` instead
2257    /// of `zdt - ...` since `Sub` is implemented for `&Zoned` and not `Zoned`.
2258    /// This is because `Zoned` is not `Copy`.
2259    ///
2260    /// ```
2261    /// use std::time::Duration;
2262    ///
2263    /// use jiff::{civil::date, SignedDuration, ToSpan};
2264    ///
2265    /// let zdt = date(1995, 12, 7)
2266    ///     .at(3, 24, 30, 3_500)
2267    ///     .in_tz("America/New_York")?;
2268    /// let got = &zdt - 20.years().months(4).nanoseconds(500);
2269    /// assert_eq!(
2270    ///     got,
2271    ///     date(1975, 8, 7).at(3, 24, 30, 3_000).in_tz("America/New_York")?,
2272    /// );
2273    ///
2274    /// let dur = SignedDuration::new(24 * 60 * 60, 500);
2275    /// assert_eq!(
2276    ///     &zdt - dur,
2277    ///     date(1995, 12, 6).at(3, 24, 30, 3_000).in_tz("America/New_York")?,
2278    /// );
2279    ///
2280    /// let dur = Duration::new(24 * 60 * 60, 500);
2281    /// assert_eq!(
2282    ///     &zdt - dur,
2283    ///     date(1995, 12, 6).at(3, 24, 30, 3_000).in_tz("America/New_York")?,
2284    /// );
2285    ///
2286    /// # Ok::<(), Box<dyn std::error::Error>>(())
2287    /// ```
2288    #[inline]
2289    pub fn checked_sub<A: Into<ZonedArithmetic>>(
2290        &self,
2291        duration: A,
2292    ) -> Result<Zoned, Error> {
2293        let duration: ZonedArithmetic = duration.into();
2294        duration.checked_neg().and_then(|za| za.checked_add(self))
2295    }
2296
2297    /// This routine is identical to [`Zoned::checked_add`], except the
2298    /// result saturates on overflow. That is, instead of overflow, either
2299    /// [`Timestamp::MIN`] or [`Timestamp::MAX`] (in this `Zoned` value's time
2300    /// zone) is returned.
2301    ///
2302    /// # Properties
2303    ///
2304    /// The properties of this routine are identical to [`Zoned::checked_add`],
2305    /// except that if saturation occurs, then the result is not reversible.
2306    ///
2307    /// # Example
2308    ///
2309    /// ```
2310    /// use jiff::{civil::date, SignedDuration, Timestamp, ToSpan};
2311    ///
2312    /// let zdt = date(2024, 3, 31).at(13, 13, 13, 13).in_tz("America/New_York")?;
2313    /// assert_eq!(Timestamp::MAX, zdt.saturating_add(9000.years()).timestamp());
2314    /// assert_eq!(Timestamp::MIN, zdt.saturating_add(-19000.years()).timestamp());
2315    /// assert_eq!(Timestamp::MAX, zdt.saturating_add(SignedDuration::MAX).timestamp());
2316    /// assert_eq!(Timestamp::MIN, zdt.saturating_add(SignedDuration::MIN).timestamp());
2317    /// assert_eq!(Timestamp::MAX, zdt.saturating_add(std::time::Duration::MAX).timestamp());
2318    ///
2319    /// # Ok::<(), Box<dyn std::error::Error>>(())
2320    /// ```
2321    #[inline]
2322    pub fn saturating_add<A: Into<ZonedArithmetic>>(
2323        &self,
2324        duration: A,
2325    ) -> Zoned {
2326        let duration: ZonedArithmetic = duration.into();
2327        self.checked_add(duration).unwrap_or_else(|_| {
2328            let ts = if duration.is_negative() {
2329                Timestamp::MIN
2330            } else {
2331                Timestamp::MAX
2332            };
2333            ts.to_zoned(self.time_zone().clone())
2334        })
2335    }
2336
2337    /// This routine is identical to [`Zoned::saturating_add`] with the span
2338    /// parameter negated.
2339    ///
2340    /// # Example
2341    ///
2342    /// ```
2343    /// use jiff::{civil::date, SignedDuration, Timestamp, ToSpan};
2344    ///
2345    /// let zdt = date(2024, 3, 31).at(13, 13, 13, 13).in_tz("America/New_York")?;
2346    /// assert_eq!(Timestamp::MIN, zdt.saturating_sub(19000.years()).timestamp());
2347    /// assert_eq!(Timestamp::MAX, zdt.saturating_sub(-9000.years()).timestamp());
2348    /// assert_eq!(Timestamp::MIN, zdt.saturating_sub(SignedDuration::MAX).timestamp());
2349    /// assert_eq!(Timestamp::MAX, zdt.saturating_sub(SignedDuration::MIN).timestamp());
2350    /// assert_eq!(Timestamp::MIN, zdt.saturating_sub(std::time::Duration::MAX).timestamp());
2351    ///
2352    /// # Ok::<(), Box<dyn std::error::Error>>(())
2353    /// ```
2354    #[inline]
2355    pub fn saturating_sub<A: Into<ZonedArithmetic>>(
2356        &self,
2357        duration: A,
2358    ) -> Zoned {
2359        let duration: ZonedArithmetic = duration.into();
2360        let Ok(duration) = duration.checked_neg() else {
2361            return Timestamp::MIN.to_zoned(self.time_zone().clone());
2362        };
2363        self.saturating_add(duration)
2364    }
2365
2366    /// Returns a span representing the elapsed time from this zoned datetime
2367    /// until the given `other` zoned datetime.
2368    ///
2369    /// When `other` occurs before this datetime, then the span returned will
2370    /// be negative.
2371    ///
2372    /// Depending on the input provided, the span returned is rounded. It may
2373    /// also be balanced up to bigger units than the default. By default, the
2374    /// span returned is balanced such that the biggest possible unit is hours.
2375    /// This default is an API guarantee. Users can rely on the default not
2376    /// returning any calendar units in the default configuration.
2377    ///
2378    /// This operation is configured by providing a [`ZonedDifference`]
2379    /// value. Since this routine accepts anything that implements
2380    /// `Into<ZonedDifference>`, once can pass a `&Zoned` directly.
2381    /// One can also pass a `(Unit, &Zoned)`, where `Unit` is treated as
2382    /// [`ZonedDifference::largest`].
2383    ///
2384    /// # Properties
2385    ///
2386    /// It is guaranteed that if the returned span is subtracted from `other`,
2387    /// and if no rounding is requested, and if the largest unit requested
2388    /// is at most `Unit::Hour`, then the original zoned datetime will be
2389    /// returned.
2390    ///
2391    /// This routine is equivalent to `self.since(other).map(|span| -span)`
2392    /// if no rounding options are set. If rounding options are set, then
2393    /// it's equivalent to
2394    /// `self.since(other_without_rounding_options).map(|span| -span)`,
2395    /// followed by a call to [`Span::round`] with the appropriate rounding
2396    /// options set. This is because the negation of a span can result in
2397    /// different rounding results depending on the rounding mode.
2398    ///
2399    /// # Errors
2400    ///
2401    /// An error can occur in some cases when the requested configuration
2402    /// would result in a span that is beyond allowable limits. For example,
2403    /// the nanosecond component of a span cannot represent the span of
2404    /// time between the minimum and maximum zoned datetime supported by Jiff.
2405    /// Therefore, if one requests a span with its largest unit set to
2406    /// [`Unit::Nanosecond`], then it's possible for this routine to fail.
2407    ///
2408    /// An error can also occur if `ZonedDifference` is misconfigured. For
2409    /// example, if the smallest unit provided is bigger than the largest unit.
2410    ///
2411    /// An error can also occur if units greater than `Unit::Hour` are
2412    /// requested _and_ if the time zones in the provided zoned datetimes
2413    /// are distinct. (See [`TimeZone`]'s section on equality for details on
2414    /// how equality is determined.) This error occurs because the length of
2415    /// a day may vary depending on the time zone. To work around this
2416    /// restriction, convert one or both of the zoned datetimes into the same
2417    /// time zone.
2418    ///
2419    /// It is guaranteed that if one provides a datetime with the default
2420    /// [`ZonedDifference`] configuration, then this routine will never
2421    /// fail.
2422    ///
2423    /// # Example
2424    ///
2425    /// ```
2426    /// use jiff::{civil::date, ToSpan};
2427    ///
2428    /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0).in_tz("America/New_York")?;
2429    /// let later = date(2019, 1, 31).at(21, 0, 0, 0).in_tz("America/New_York")?;
2430    /// assert_eq!(
2431    ///     earlier.until(&later)?,
2432    ///     109_031.hours().minutes(30).fieldwise(),
2433    /// );
2434    ///
2435    /// // Flipping the dates is fine, but you'll get a negative span.
2436    /// assert_eq!(
2437    ///     later.until(&earlier)?,
2438    ///     -109_031.hours().minutes(30).fieldwise(),
2439    /// );
2440    ///
2441    /// # Ok::<(), Box<dyn std::error::Error>>(())
2442    /// ```
2443    ///
2444    /// # Example: using bigger units
2445    ///
2446    /// This example shows how to expand the span returned to bigger units.
2447    /// This makes use of a `From<(Unit, &Zoned)> for ZonedDifference`
2448    /// trait implementation.
2449    ///
2450    /// ```
2451    /// use jiff::{civil::date, Unit, ToSpan};
2452    ///
2453    /// let zdt1 = date(1995, 12, 07).at(3, 24, 30, 3500).in_tz("America/New_York")?;
2454    /// let zdt2 = date(2019, 01, 31).at(15, 30, 0, 0).in_tz("America/New_York")?;
2455    ///
2456    /// // The default limits durations to using "hours" as the biggest unit.
2457    /// let span = zdt1.until(&zdt2)?;
2458    /// assert_eq!(span.to_string(), "PT202956H5M29.9999965S");
2459    ///
2460    /// // But we can ask for units all the way up to years.
2461    /// let span = zdt1.until((Unit::Year, &zdt2))?;
2462    /// assert_eq!(format!("{span:#}"), "23y 1mo 24d 12h 5m 29s 999ms 996µs 500ns");
2463    /// # Ok::<(), Box<dyn std::error::Error>>(())
2464    /// ```
2465    ///
2466    /// # Example: rounding the result
2467    ///
2468    /// This shows how one might find the difference between two zoned
2469    /// datetimes and have the result rounded such that sub-seconds are
2470    /// removed.
2471    ///
2472    /// In this case, we need to hand-construct a [`ZonedDifference`]
2473    /// in order to gain full configurability.
2474    ///
2475    /// ```
2476    /// use jiff::{civil::date, Unit, ToSpan, ZonedDifference};
2477    ///
2478    /// let zdt1 = date(1995, 12, 07).at(3, 24, 30, 3500).in_tz("America/New_York")?;
2479    /// let zdt2 = date(2019, 01, 31).at(15, 30, 0, 0).in_tz("America/New_York")?;
2480    ///
2481    /// let span = zdt1.until(
2482    ///     ZonedDifference::from(&zdt2).smallest(Unit::Second),
2483    /// )?;
2484    /// assert_eq!(format!("{span:#}"), "202956h 5m 29s");
2485    ///
2486    /// // We can combine smallest and largest units too!
2487    /// let span = zdt1.until(
2488    ///     ZonedDifference::from(&zdt2)
2489    ///         .smallest(Unit::Second)
2490    ///         .largest(Unit::Year),
2491    /// )?;
2492    /// assert_eq!(span.to_string(), "P23Y1M24DT12H5M29S");
2493    ///
2494    /// # Ok::<(), Box<dyn std::error::Error>>(())
2495    /// ```
2496    ///
2497    /// # Example: units biggers than days inhibit reversibility
2498    ///
2499    /// If you ask for units bigger than hours, then adding the span returned
2500    /// to the `other` zoned datetime is not guaranteed to result in the
2501    /// original zoned datetime. For example:
2502    ///
2503    /// ```
2504    /// use jiff::{civil::date, Unit, ToSpan};
2505    ///
2506    /// let zdt1 = date(2024, 3, 2).at(0, 0, 0, 0).in_tz("America/New_York")?;
2507    /// let zdt2 = date(2024, 5, 1).at(0, 0, 0, 0).in_tz("America/New_York")?;
2508    ///
2509    /// let span = zdt1.until((Unit::Month, &zdt2))?;
2510    /// assert_eq!(span, 1.month().days(29).fieldwise());
2511    /// let maybe_original = zdt2.checked_sub(span)?;
2512    /// // Not the same as the original datetime!
2513    /// assert_eq!(
2514    ///     maybe_original,
2515    ///     date(2024, 3, 3).at(0, 0, 0, 0).in_tz("America/New_York")?,
2516    /// );
2517    ///
2518    /// // But in the default configuration, hours are always the biggest unit
2519    /// // and reversibility is guaranteed.
2520    /// let span = zdt1.until(&zdt2)?;
2521    /// assert_eq!(span.to_string(), "PT1439H");
2522    /// let is_original = zdt2.checked_sub(span)?;
2523    /// assert_eq!(is_original, zdt1);
2524    ///
2525    /// # Ok::<(), Box<dyn std::error::Error>>(())
2526    /// ```
2527    ///
2528    /// This occurs because spans are added as if by adding the biggest units
2529    /// first, and then the smaller units. Because months vary in length,
2530    /// their meaning can change depending on how the span is added. In this
2531    /// case, adding one month to `2024-03-02` corresponds to 31 days, but
2532    /// subtracting one month from `2024-05-01` corresponds to 30 days.
2533    #[inline]
2534    pub fn until<'a, A: Into<ZonedDifference<'a>>>(
2535        &self,
2536        other: A,
2537    ) -> Result<Span, Error> {
2538        let args: ZonedDifference = other.into();
2539        let span = args.until_with_largest_unit(self)?;
2540        if args.rounding_may_change_span() {
2541            span.round(args.round.relative(self))
2542        } else {
2543            Ok(span)
2544        }
2545    }
2546
2547    /// This routine is identical to [`Zoned::until`], but the order of the
2548    /// parameters is flipped.
2549    ///
2550    /// # Errors
2551    ///
2552    /// This has the same error conditions as [`Zoned::until`].
2553    ///
2554    /// # Example
2555    ///
2556    /// This routine can be used via the `-` operator. Since the default
2557    /// configuration is used and because a `Span` can represent the difference
2558    /// between any two possible zoned datetimes, it will never panic. Note
2559    /// that we use `&zdt1 - &zdt2` instead of `zdt1 - zdt2` since `Sub` is
2560    /// implemented for `&Zoned` and not `Zoned`. This is because `Zoned` is
2561    /// not `Copy`.
2562    ///
2563    /// ```
2564    /// use jiff::{civil::date, ToSpan};
2565    ///
2566    /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0).in_tz("America/New_York")?;
2567    /// let later = date(2019, 1, 31).at(21, 0, 0, 0).in_tz("America/New_York")?;
2568    /// assert_eq!(&later - &earlier, 109_031.hours().minutes(30).fieldwise());
2569    ///
2570    /// # Ok::<(), Box<dyn std::error::Error>>(())
2571    /// ```
2572    #[inline]
2573    pub fn since<'a, A: Into<ZonedDifference<'a>>>(
2574        &self,
2575        other: A,
2576    ) -> Result<Span, Error> {
2577        let args: ZonedDifference = other.into();
2578        let span = -args.until_with_largest_unit(self)?;
2579        if args.rounding_may_change_span() {
2580            span.round(args.round.relative(self))
2581        } else {
2582            Ok(span)
2583        }
2584    }
2585
2586    /// Returns an absolute duration representing the elapsed time from this
2587    /// zoned datetime until the given `other` zoned datetime.
2588    ///
2589    /// When `other` occurs before this zoned datetime, then the duration
2590    /// returned will be negative.
2591    ///
2592    /// Unlike [`Zoned::until`], this always returns a duration
2593    /// corresponding to a 96-bit integer of nanoseconds between two
2594    /// zoned datetimes.
2595    ///
2596    /// # Fallibility
2597    ///
2598    /// This routine never panics or returns an error. Since there are no
2599    /// configuration options that can be incorrectly provided, no error is
2600    /// possible when calling this routine. In contrast, [`Zoned::until`]
2601    /// can return an error in some cases due to misconfiguration. But like
2602    /// this routine, [`Zoned::until`] never panics or returns an error in
2603    /// its default configuration.
2604    ///
2605    /// # When should I use this versus [`Zoned::until`]?
2606    ///
2607    /// See the type documentation for [`SignedDuration`] for the section on
2608    /// when one should use [`Span`] and when one should use `SignedDuration`.
2609    /// In short, use `Span` (and therefore `Timestamp::until`) unless you have
2610    /// a specific reason to do otherwise.
2611    ///
2612    /// # Example
2613    ///
2614    /// ```
2615    /// use jiff::{civil::date, SignedDuration};
2616    ///
2617    /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0).in_tz("US/Eastern")?;
2618    /// let later = date(2019, 1, 31).at(21, 0, 0, 0).in_tz("US/Eastern")?;
2619    /// assert_eq!(
2620    ///     earlier.duration_until(&later),
2621    ///     SignedDuration::from_hours(109_031) + SignedDuration::from_mins(30),
2622    /// );
2623    ///
2624    /// // Flipping the dates is fine, but you'll get a negative span.
2625    /// assert_eq!(
2626    ///     later.duration_until(&earlier),
2627    ///     -SignedDuration::from_hours(109_031) + -SignedDuration::from_mins(30),
2628    /// );
2629    ///
2630    /// # Ok::<(), Box<dyn std::error::Error>>(())
2631    /// ```
2632    ///
2633    /// # Example: difference with [`Zoned::until`]
2634    ///
2635    /// The main difference between this routine and `Zoned::until` is that
2636    /// the latter can return units other than a 96-bit integer of nanoseconds.
2637    /// While a 96-bit integer of nanoseconds can be converted into other units
2638    /// like hours, this can only be done for uniform units. (Uniform units are
2639    /// units for which each individual unit always corresponds to the same
2640    /// elapsed time regardless of the datetime it is relative to.) This can't
2641    /// be done for units like years, months or days.
2642    ///
2643    /// ```
2644    /// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit};
2645    ///
2646    /// let zdt1 = date(2024, 3, 10).at(0, 0, 0, 0).in_tz("US/Eastern")?;
2647    /// let zdt2 = date(2024, 3, 11).at(0, 0, 0, 0).in_tz("US/Eastern")?;
2648    ///
2649    /// let span = zdt1.until((Unit::Day, &zdt2))?;
2650    /// assert_eq!(format!("{span:#}"), "1d");
2651    ///
2652    /// let duration = zdt1.duration_until(&zdt2);
2653    /// // This day was only 23 hours long!
2654    /// assert_eq!(duration, SignedDuration::from_hours(23));
2655    /// // There's no way to extract years, months or days from the signed
2656    /// // duration like one might extract hours (because every hour
2657    /// // is the same length). Instead, you actually have to convert
2658    /// // it to a span and then balance it by providing a relative date!
2659    /// let options = SpanRound::new().largest(Unit::Day).relative(&zdt1);
2660    /// let span = Span::try_from(duration)?.round(options)?;
2661    /// assert_eq!(format!("{span:#}"), "1d");
2662    ///
2663    /// # Ok::<(), Box<dyn std::error::Error>>(())
2664    /// ```
2665    ///
2666    /// # Example: getting an unsigned duration
2667    ///
2668    /// If you're looking to find the duration between two zoned datetimes as
2669    /// a [`std::time::Duration`], you'll need to use this method to get a
2670    /// [`SignedDuration`] and then convert it to a `std::time::Duration`:
2671    ///
2672    /// ```
2673    /// use std::time::Duration;
2674    ///
2675    /// use jiff::civil::date;
2676    ///
2677    /// let zdt1 = date(2024, 7, 1).at(0, 0, 0, 0).in_tz("US/Eastern")?;
2678    /// let zdt2 = date(2024, 8, 1).at(0, 0, 0, 0).in_tz("US/Eastern")?;
2679    /// let duration = Duration::try_from(zdt1.duration_until(&zdt2))?;
2680    /// assert_eq!(duration, Duration::from_secs(31 * 24 * 60 * 60));
2681    ///
2682    /// // Note that unsigned durations cannot represent all
2683    /// // possible differences! If the duration would be negative,
2684    /// // then the conversion fails:
2685    /// assert!(Duration::try_from(zdt2.duration_until(&zdt1)).is_err());
2686    ///
2687    /// # Ok::<(), Box<dyn std::error::Error>>(())
2688    /// ```
2689    #[inline]
2690    pub fn duration_until(&self, other: &Zoned) -> SignedDuration {
2691        SignedDuration::zoned_until(self, other)
2692    }
2693
2694    /// This routine is identical to [`Zoned::duration_until`], but the
2695    /// order of the parameters is flipped.
2696    ///
2697    /// # Example
2698    ///
2699    /// ```
2700    /// use jiff::{civil::date, SignedDuration};
2701    ///
2702    /// let earlier = date(2006, 8, 24).at(22, 30, 0, 0).in_tz("US/Eastern")?;
2703    /// let later = date(2019, 1, 31).at(21, 0, 0, 0).in_tz("US/Eastern")?;
2704    /// assert_eq!(
2705    ///     later.duration_since(&earlier),
2706    ///     SignedDuration::from_hours(109_031) + SignedDuration::from_mins(30),
2707    /// );
2708    ///
2709    /// # Ok::<(), Box<dyn std::error::Error>>(())
2710    /// ```
2711    #[inline]
2712    pub fn duration_since(&self, other: &Zoned) -> SignedDuration {
2713        SignedDuration::zoned_until(other, self)
2714    }
2715
2716    /// Rounds this zoned datetime according to the [`ZonedRound`]
2717    /// configuration given.
2718    ///
2719    /// The principal option is [`ZonedRound::smallest`], which allows one to
2720    /// configure the smallest units in the returned zoned datetime. Rounding
2721    /// is what determines whether that unit should keep its current value
2722    /// or whether it should be incremented. Moreover, the amount it should
2723    /// be incremented can be configured via [`ZonedRound::increment`].
2724    /// Finally, the rounding strategy itself can be configured via
2725    /// [`ZonedRound::mode`].
2726    ///
2727    /// Note that this routine is generic and accepts anything that
2728    /// implements `Into<ZonedRound>`. Some notable implementations are:
2729    ///
2730    /// * `From<Unit> for ZonedRound`, which will automatically create a
2731    /// `ZonedRound::new().smallest(unit)` from the unit provided.
2732    /// * `From<(Unit, i64)> for ZonedRound`, which will automatically
2733    /// create a `ZonedRound::new().smallest(unit).increment(number)` from
2734    /// the unit and increment provided.
2735    ///
2736    /// # Errors
2737    ///
2738    /// This returns an error if the smallest unit configured on the given
2739    /// [`ZonedRound`] is bigger than days. An error is also returned if
2740    /// the rounding increment is greater than 1 when the units are days.
2741    /// (Currently, rounding to the nearest week, month or year is not
2742    /// supported.)
2743    ///
2744    /// When the smallest unit is less than days, the rounding increment must
2745    /// divide evenly into the next highest unit after the smallest unit
2746    /// configured (and must not be equivalent to it). For example, if the
2747    /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
2748    /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
2749    /// Namely, any integer that divides evenly into `1,000` nanoseconds since
2750    /// there are `1,000` nanoseconds in the next highest unit (microseconds).
2751    ///
2752    /// This can also return an error in some cases where rounding would
2753    /// require arithmetic that exceeds the maximum zoned datetime value.
2754    ///
2755    /// # Example
2756    ///
2757    /// This is a basic example that demonstrates rounding a zoned datetime
2758    /// to the nearest day. This also demonstrates calling this method with
2759    /// the smallest unit directly, instead of constructing a `ZonedRound`
2760    /// manually.
2761    ///
2762    /// ```
2763    /// use jiff::{civil::date, Unit};
2764    ///
2765    /// // rounds up
2766    /// let zdt = date(2024, 6, 19).at(15, 0, 0, 0).in_tz("America/New_York")?;
2767    /// assert_eq!(
2768    ///     zdt.round(Unit::Day)?,
2769    ///     date(2024, 6, 20).at(0, 0, 0, 0).in_tz("America/New_York")?,
2770    /// );
2771    ///
2772    /// // rounds down
2773    /// let zdt = date(2024, 6, 19).at(10, 0, 0, 0).in_tz("America/New_York")?;
2774    /// assert_eq!(
2775    ///     zdt.round(Unit::Day)?,
2776    ///     date(2024, 6, 19).at(0, 0, 0, 0).in_tz("America/New_York")?,
2777    /// );
2778    ///
2779    /// # Ok::<(), Box<dyn std::error::Error>>(())
2780    /// ```
2781    ///
2782    /// # Example: changing the rounding mode
2783    ///
2784    /// The default rounding mode is [`RoundMode::HalfExpand`], which
2785    /// breaks ties by rounding away from zero. But other modes like
2786    /// [`RoundMode::Trunc`] can be used too:
2787    ///
2788    /// ```
2789    /// use jiff::{civil::date, RoundMode, Unit, Zoned, ZonedRound};
2790    ///
2791    /// let zdt = date(2024, 6, 19).at(15, 0, 0, 0).in_tz("America/New_York")?;
2792    /// assert_eq!(
2793    ///     zdt.round(Unit::Day)?,
2794    ///     date(2024, 6, 20).at(0, 0, 0, 0).in_tz("America/New_York")?,
2795    /// );
2796    /// // The default will round up to the next day for any time past noon (as
2797    /// // shown above), but using truncation rounding will always round down.
2798    /// assert_eq!(
2799    ///     zdt.round(
2800    ///         ZonedRound::new().smallest(Unit::Day).mode(RoundMode::Trunc),
2801    ///     )?,
2802    ///     date(2024, 6, 19).at(0, 0, 0, 0).in_tz("America/New_York")?,
2803    /// );
2804    ///
2805    /// # Ok::<(), Box<dyn std::error::Error>>(())
2806    /// ```
2807    ///
2808    /// # Example: rounding to the nearest 5 minute increment
2809    ///
2810    /// ```
2811    /// use jiff::{civil::date, Unit};
2812    ///
2813    /// // rounds down
2814    /// let zdt = date(2024, 6, 19)
2815    ///     .at(15, 27, 29, 999_999_999)
2816    ///     .in_tz("America/New_York")?;
2817    /// assert_eq!(
2818    ///     zdt.round((Unit::Minute, 5))?,
2819    ///     date(2024, 6, 19).at(15, 25, 0, 0).in_tz("America/New_York")?,
2820    /// );
2821    /// // rounds up
2822    /// let zdt = date(2024, 6, 19)
2823    ///     .at(15, 27, 30, 0)
2824    ///     .in_tz("America/New_York")?;
2825    /// assert_eq!(
2826    ///     zdt.round((Unit::Minute, 5))?,
2827    ///     date(2024, 6, 19).at(15, 30, 0, 0).in_tz("America/New_York")?,
2828    /// );
2829    ///
2830    /// # Ok::<(), Box<dyn std::error::Error>>(())
2831    /// ```
2832    ///
2833    /// # Example: behavior near time zone transitions
2834    ///
2835    /// When rounding this zoned datetime near time zone transitions (such as
2836    /// DST), the "sensible" thing is done by default. Namely, rounding will
2837    /// jump to the closest instant, even if the change in civil clock time is
2838    /// large. For example, when rounding up into a gap, the civil clock time
2839    /// will jump over the gap, but the corresponding change in the instant is
2840    /// as one might expect:
2841    ///
2842    /// ```
2843    /// use jiff::{Unit, Zoned};
2844    ///
2845    /// let zdt1: Zoned = "2024-03-10T01:59:00-05[America/New_York]".parse()?;
2846    /// let zdt2 = zdt1.round(Unit::Hour)?;
2847    /// assert_eq!(
2848    ///     zdt2.to_string(),
2849    ///     "2024-03-10T03:00:00-04:00[America/New_York]",
2850    /// );
2851    ///
2852    /// # Ok::<(), Box<dyn std::error::Error>>(())
2853    /// ```
2854    ///
2855    /// Similarly, when rounding inside a fold, rounding will respect whether
2856    /// it's the first or second time the clock has repeated the hour. For the
2857    /// DST transition in New York on `2024-11-03` from offset `-04` to `-05`,
2858    /// here is an example that rounds the first 1 o'clock hour:
2859    ///
2860    /// ```
2861    /// use jiff::{Unit, Zoned};
2862    ///
2863    /// let zdt1: Zoned = "2024-11-03T01:59:01-04[America/New_York]".parse()?;
2864    /// let zdt2 = zdt1.round(Unit::Minute)?;
2865    /// assert_eq!(
2866    ///     zdt2.to_string(),
2867    ///     "2024-11-03T01:59:00-04:00[America/New_York]",
2868    /// );
2869    ///
2870    /// # Ok::<(), Box<dyn std::error::Error>>(())
2871    /// ```
2872    ///
2873    /// And now the second 1 o'clock hour. Notice how the rounded result stays
2874    /// in the second 1 o'clock hour.
2875    ///
2876    /// ```
2877    /// use jiff::{Unit, Zoned};
2878    ///
2879    /// let zdt1: Zoned = "2024-11-03T01:59:01-05[America/New_York]".parse()?;
2880    /// let zdt2 = zdt1.round(Unit::Minute)?;
2881    /// assert_eq!(
2882    ///     zdt2.to_string(),
2883    ///     "2024-11-03T01:59:00-05:00[America/New_York]",
2884    /// );
2885    ///
2886    /// # Ok::<(), Box<dyn std::error::Error>>(())
2887    /// ```
2888    ///
2889    /// # Example: rounding to nearest day takes length of day into account
2890    ///
2891    /// Some days are shorter than 24 hours, and so rounding down will occur
2892    /// even when the time is past noon:
2893    ///
2894    /// ```
2895    /// use jiff::{Unit, Zoned};
2896    ///
2897    /// let zdt1: Zoned = "2025-03-09T12:15-04[America/New_York]".parse()?;
2898    /// let zdt2 = zdt1.round(Unit::Day)?;
2899    /// assert_eq!(
2900    ///     zdt2.to_string(),
2901    ///     "2025-03-09T00:00:00-05:00[America/New_York]",
2902    /// );
2903    ///
2904    /// // For 23 hour days, 12:30 is the tipping point to round up in the
2905    /// // default rounding configuration:
2906    /// let zdt1: Zoned = "2025-03-09T12:30-04[America/New_York]".parse()?;
2907    /// let zdt2 = zdt1.round(Unit::Day)?;
2908    /// assert_eq!(
2909    ///     zdt2.to_string(),
2910    ///     "2025-03-10T00:00:00-04:00[America/New_York]",
2911    /// );
2912    ///
2913    /// # Ok::<(), Box<dyn std::error::Error>>(())
2914    /// ```
2915    ///
2916    /// And some days are longer than 24 hours, and so rounding _up_ will occur
2917    /// even when the time is before noon:
2918    ///
2919    /// ```
2920    /// use jiff::{Unit, Zoned};
2921    ///
2922    /// let zdt1: Zoned = "2025-11-02T11:45-05[America/New_York]".parse()?;
2923    /// let zdt2 = zdt1.round(Unit::Day)?;
2924    /// assert_eq!(
2925    ///     zdt2.to_string(),
2926    ///     "2025-11-03T00:00:00-05:00[America/New_York]",
2927    /// );
2928    ///
2929    /// // For 25 hour days, 11:30 is the tipping point to round up in the
2930    /// // default rounding configuration. So 11:29 will round down:
2931    /// let zdt1: Zoned = "2025-11-02T11:29-05[America/New_York]".parse()?;
2932    /// let zdt2 = zdt1.round(Unit::Day)?;
2933    /// assert_eq!(
2934    ///     zdt2.to_string(),
2935    ///     "2025-11-02T00:00:00-04:00[America/New_York]",
2936    /// );
2937    ///
2938    /// # Ok::<(), Box<dyn std::error::Error>>(())
2939    /// ```
2940    ///
2941    /// # Example: overflow error
2942    ///
2943    /// This example demonstrates that it's possible for this operation to
2944    /// result in an error from zoned datetime arithmetic overflow.
2945    ///
2946    /// ```
2947    /// use jiff::{Timestamp, Unit};
2948    ///
2949    /// let zdt = Timestamp::MAX.in_tz("America/New_York")?;
2950    /// assert!(zdt.round(Unit::Day).is_err());
2951    ///
2952    /// # Ok::<(), Box<dyn std::error::Error>>(())
2953    /// ```
2954    ///
2955    /// This occurs because rounding to the nearest day for the maximum
2956    /// timestamp would result in rounding up to the next day. But the next day
2957    /// is greater than the maximum, and so this returns an error.
2958    #[inline]
2959    pub fn round<R: Into<ZonedRound>>(
2960        &self,
2961        options: R,
2962    ) -> Result<Zoned, Error> {
2963        let options: ZonedRound = options.into();
2964        options.round(self)
2965    }
2966
2967    /// Return an iterator of periodic zoned datetimes determined by the given
2968    /// span.
2969    ///
2970    /// The given span may be negative, in which case, the iterator will move
2971    /// backwards through time. The iterator won't stop until either the span
2972    /// itself overflows, or it would otherwise exceed the minimum or maximum
2973    /// `Zoned` value.
2974    ///
2975    /// When the given span is positive, the zoned datetimes yielded are
2976    /// monotonically increasing. When the given span is negative, the zoned
2977    /// datetimes yielded as monotonically decreasing. When the given span is
2978    /// zero, then all values yielded are identical and the time series is
2979    /// infinite.
2980    ///
2981    /// # Example: when to check a glucose monitor
2982    ///
2983    /// When my cat had diabetes, my veterinarian installed a glucose monitor
2984    /// and instructed me to scan it about every 5 hours. This example lists
2985    /// all of the times I needed to scan it for the 2 days following its
2986    /// installation:
2987    ///
2988    /// ```
2989    /// use jiff::{civil::datetime, ToSpan};
2990    ///
2991    /// let start = datetime(2023, 7, 15, 16, 30, 0, 0).in_tz("America/New_York")?;
2992    /// let end = start.checked_add(2.days())?;
2993    /// let mut scan_times = vec![];
2994    /// for zdt in start.series(5.hours()).take_while(|zdt| zdt <= end) {
2995    ///     scan_times.push(zdt.datetime());
2996    /// }
2997    /// assert_eq!(scan_times, vec![
2998    ///     datetime(2023, 7, 15, 16, 30, 0, 0),
2999    ///     datetime(2023, 7, 15, 21, 30, 0, 0),
3000    ///     datetime(2023, 7, 16, 2, 30, 0, 0),
3001    ///     datetime(2023, 7, 16, 7, 30, 0, 0),
3002    ///     datetime(2023, 7, 16, 12, 30, 0, 0),
3003    ///     datetime(2023, 7, 16, 17, 30, 0, 0),
3004    ///     datetime(2023, 7, 16, 22, 30, 0, 0),
3005    ///     datetime(2023, 7, 17, 3, 30, 0, 0),
3006    ///     datetime(2023, 7, 17, 8, 30, 0, 0),
3007    ///     datetime(2023, 7, 17, 13, 30, 0, 0),
3008    /// ]);
3009    ///
3010    /// # Ok::<(), Box<dyn std::error::Error>>(())
3011    /// ```
3012    ///
3013    /// # Example: behavior during daylight saving time transitions
3014    ///
3015    /// Even when there is a daylight saving time transition, the time series
3016    /// returned handles it correctly by continuing to move forward.
3017    ///
3018    /// This first example shows what happens when there is a gap in time (it
3019    /// is automatically skipped):
3020    ///
3021    /// ```
3022    /// use jiff::{civil::date, ToSpan};
3023    ///
3024    /// let zdt = date(2025, 3, 9).at(1, 0, 0, 0).in_tz("America/New_York")?;
3025    /// let mut it = zdt.series(30.minutes());
3026    ///
3027    /// assert_eq!(
3028    ///     it.next().map(|zdt| zdt.to_string()),
3029    ///     Some("2025-03-09T01:00:00-05:00[America/New_York]".to_string()),
3030    /// );
3031    /// assert_eq!(
3032    ///     it.next().map(|zdt| zdt.to_string()),
3033    ///     Some("2025-03-09T01:30:00-05:00[America/New_York]".to_string()),
3034    /// );
3035    /// assert_eq!(
3036    ///     it.next().map(|zdt| zdt.to_string()),
3037    ///     Some("2025-03-09T03:00:00-04:00[America/New_York]".to_string()),
3038    /// );
3039    /// assert_eq!(
3040    ///     it.next().map(|zdt| zdt.to_string()),
3041    ///     Some("2025-03-09T03:30:00-04:00[America/New_York]".to_string()),
3042    /// );
3043    ///
3044    /// # Ok::<(), Box<dyn std::error::Error>>(())
3045    /// ```
3046    ///
3047    /// And similarly, when there is a fold in time, the fold is repeated:
3048    ///
3049    /// ```
3050    /// use jiff::{civil::date, ToSpan};
3051    ///
3052    /// let zdt = date(2025, 11, 2).at(0, 30, 0, 0).in_tz("America/New_York")?;
3053    /// let mut it = zdt.series(30.minutes());
3054    ///
3055    /// assert_eq!(
3056    ///     it.next().map(|zdt| zdt.to_string()),
3057    ///     Some("2025-11-02T00:30:00-04:00[America/New_York]".to_string()),
3058    /// );
3059    /// assert_eq!(
3060    ///     it.next().map(|zdt| zdt.to_string()),
3061    ///     Some("2025-11-02T01:00:00-04:00[America/New_York]".to_string()),
3062    /// );
3063    /// assert_eq!(
3064    ///     it.next().map(|zdt| zdt.to_string()),
3065    ///     Some("2025-11-02T01:30:00-04:00[America/New_York]".to_string()),
3066    /// );
3067    /// assert_eq!(
3068    ///     it.next().map(|zdt| zdt.to_string()),
3069    ///     Some("2025-11-02T01:00:00-05:00[America/New_York]".to_string()),
3070    /// );
3071    /// assert_eq!(
3072    ///     it.next().map(|zdt| zdt.to_string()),
3073    ///     Some("2025-11-02T01:30:00-05:00[America/New_York]".to_string()),
3074    /// );
3075    /// assert_eq!(
3076    ///     it.next().map(|zdt| zdt.to_string()),
3077    ///     Some("2025-11-02T02:00:00-05:00[America/New_York]".to_string()),
3078    /// );
3079    ///
3080    /// # Ok::<(), Box<dyn std::error::Error>>(())
3081    /// ```
3082    ///
3083    /// # Example: ensures values are monotonically increasing (or decreasing)
3084    ///
3085    /// Because of odd time zone transitions, it's possible that adding
3086    /// different calendar units to the same zoned datetime will yield the
3087    /// same result. For example, `2011-12-30` did not exist on the clocks
3088    /// in the `Pacific/Apia` time zone. (Because Samoa switched sides of the
3089    /// International Date Line.) This means that adding `1 day` to
3090    /// `2011-12-29` yields the same result as adding `2 days`:
3091    ///
3092    /// ```
3093    /// use jiff::{civil, ToSpan};
3094    ///
3095    /// let zdt = civil::date(2011, 12, 29).in_tz("Pacific/Apia")?;
3096    /// assert_eq!(
3097    ///     zdt.checked_add(1.day())?.to_string(),
3098    ///     "2011-12-31T00:00:00+14:00[Pacific/Apia]",
3099    /// );
3100    /// assert_eq!(
3101    ///     zdt.checked_add(2.days())?.to_string(),
3102    ///     "2011-12-31T00:00:00+14:00[Pacific/Apia]",
3103    /// );
3104    /// assert_eq!(
3105    ///     zdt.checked_add(3.days())?.to_string(),
3106    ///     "2012-01-01T00:00:00+14:00[Pacific/Apia]",
3107    /// );
3108    ///
3109    /// # Ok::<(), Box<dyn std::error::Error>>(())
3110    /// ```
3111    ///
3112    /// This might lead one to believe that `Zoned::series` could emit the
3113    /// same instant twice. But it takes this into account and ensures all
3114    /// values occur after the previous value (or before if the `Span` given
3115    /// is negative):
3116    ///
3117    /// ```
3118    /// use jiff::{civil::date, ToSpan};
3119    ///
3120    /// let zdt = date(2011, 12, 28).in_tz("Pacific/Apia")?;
3121    /// let mut it = zdt.series(1.day());
3122    ///
3123    /// assert_eq!(
3124    ///     it.next().map(|zdt| zdt.to_string()),
3125    ///     Some("2011-12-28T00:00:00-10:00[Pacific/Apia]".to_string()),
3126    /// );
3127    /// assert_eq!(
3128    ///     it.next().map(|zdt| zdt.to_string()),
3129    ///     Some("2011-12-29T00:00:00-10:00[Pacific/Apia]".to_string()),
3130    /// );
3131    /// assert_eq!(
3132    ///     it.next().map(|zdt| zdt.to_string()),
3133    ///     Some("2011-12-31T00:00:00+14:00[Pacific/Apia]".to_string()),
3134    /// );
3135    /// assert_eq!(
3136    ///     it.next().map(|zdt| zdt.to_string()),
3137    ///     Some("2012-01-01T00:00:00+14:00[Pacific/Apia]".to_string()),
3138    /// );
3139    ///
3140    /// # Ok::<(), Box<dyn std::error::Error>>(())
3141    /// ```
3142    ///
3143    /// And similarly for a negative `Span`:
3144    ///
3145    /// ```
3146    /// use jiff::{civil::date, ToSpan};
3147    ///
3148    /// let zdt = date(2012, 1, 1).in_tz("Pacific/Apia")?;
3149    /// let mut it = zdt.series(-1.day());
3150    ///
3151    /// assert_eq!(
3152    ///     it.next().map(|zdt| zdt.to_string()),
3153    ///     Some("2012-01-01T00:00:00+14:00[Pacific/Apia]".to_string()),
3154    /// );
3155    /// assert_eq!(
3156    ///     it.next().map(|zdt| zdt.to_string()),
3157    ///     Some("2011-12-31T00:00:00+14:00[Pacific/Apia]".to_string()),
3158    /// );
3159    /// assert_eq!(
3160    ///     it.next().map(|zdt| zdt.to_string()),
3161    ///     Some("2011-12-29T00:00:00-10:00[Pacific/Apia]".to_string()),
3162    /// );
3163    /// assert_eq!(
3164    ///     it.next().map(|zdt| zdt.to_string()),
3165    ///     Some("2011-12-28T00:00:00-10:00[Pacific/Apia]".to_string()),
3166    /// );
3167    ///
3168    /// # Ok::<(), Box<dyn std::error::Error>>(())
3169    /// ```
3170    ///
3171    /// An exception to this is if a zero `Span` is provided. Then all values
3172    /// emitted are necessarily equivalent:
3173    ///
3174    /// ```
3175    /// use jiff::{civil::date, ToSpan};
3176    ///
3177    /// let zdt = date(2011, 12, 28).in_tz("Pacific/Apia")?;
3178    /// let mut it = zdt.series(0.days());
3179    ///
3180    /// assert_eq!(
3181    ///     it.next().map(|zdt| zdt.to_string()),
3182    ///     Some("2011-12-28T00:00:00-10:00[Pacific/Apia]".to_string()),
3183    /// );
3184    /// assert_eq!(
3185    ///     it.next().map(|zdt| zdt.to_string()),
3186    ///     Some("2011-12-28T00:00:00-10:00[Pacific/Apia]".to_string()),
3187    /// );
3188    ///
3189    /// # Ok::<(), Box<dyn std::error::Error>>(())
3190    /// ```
3191    #[inline]
3192    pub fn series(&self, period: Span) -> ZonedSeries {
3193        ZonedSeries { start: self.clone(), prev: None, period, step: 0 }
3194    }
3195
3196    #[inline]
3197    fn into_parts(self) -> (Timestamp, DateTime, Offset, TimeZone) {
3198        let inner = self.inner;
3199        let ZonedInner { timestamp, datetime, offset, time_zone } = inner;
3200        (timestamp, datetime, offset, time_zone)
3201    }
3202}
3203
3204/// Parsing and formatting using a "printf"-style API.
3205impl Zoned {
3206    /// Parses a zoned datetime in `input` matching the given `format`.
3207    ///
3208    /// The format string uses a "printf"-style API where conversion
3209    /// specifiers can be used as place holders to match components of
3210    /// a datetime. For details on the specifiers supported, see the
3211    /// [`fmt::strtime`] module documentation.
3212    ///
3213    /// # Warning
3214    ///
3215    /// The `strtime` module APIs do not require an IANA time zone identifier
3216    /// to parse a `Zoned`. If one is not used, then if you format a zoned
3217    /// datetime in a time zone like `America/New_York` and then parse it back
3218    /// again, the zoned datetime you get back will be a "fixed offset" zoned
3219    /// datetime. This in turn means it will not perform daylight saving time
3220    /// safe arithmetic.
3221    ///
3222    /// However, the `%Q` directive may be used to both format and parse an
3223    /// IANA time zone identifier. It is strongly recommended to use this
3224    /// directive whenever one is formatting or parsing `Zoned` values.
3225    ///
3226    /// # Errors
3227    ///
3228    /// This returns an error when parsing failed. This might happen because
3229    /// the format string itself was invalid, or because the input didn't match
3230    /// the format string.
3231    ///
3232    /// This also returns an error if there wasn't sufficient information to
3233    /// construct a zoned datetime. For example, if an offset wasn't parsed.
3234    ///
3235    /// # Example
3236    ///
3237    /// This example shows how to parse a zoned datetime:
3238    ///
3239    /// ```
3240    /// use jiff::Zoned;
3241    ///
3242    /// let zdt = Zoned::strptime("%F %H:%M %:Q", "2024-07-14 21:14 US/Eastern")?;
3243    /// assert_eq!(zdt.to_string(), "2024-07-14T21:14:00-04:00[US/Eastern]");
3244    ///
3245    /// # Ok::<(), Box<dyn std::error::Error>>(())
3246    /// ```
3247    #[inline]
3248    pub fn strptime(
3249        format: impl AsRef<[u8]>,
3250        input: impl AsRef<[u8]>,
3251    ) -> Result<Zoned, Error> {
3252        fmt::strtime::parse(format, input).and_then(|tm| tm.to_zoned())
3253    }
3254
3255    /// Formats this zoned datetime according to the given `format`.
3256    ///
3257    /// The format string uses a "printf"-style API where conversion
3258    /// specifiers can be used as place holders to format components of
3259    /// a datetime. For details on the specifiers supported, see the
3260    /// [`fmt::strtime`] module documentation.
3261    ///
3262    /// # Warning
3263    ///
3264    /// The `strtime` module APIs do not require an IANA time zone identifier
3265    /// to parse a `Zoned`. If one is not used, then if you format a zoned
3266    /// datetime in a time zone like `America/New_York` and then parse it back
3267    /// again, the zoned datetime you get back will be a "fixed offset" zoned
3268    /// datetime. This in turn means it will not perform daylight saving time
3269    /// safe arithmetic.
3270    ///
3271    /// However, the `%Q` directive may be used to both format and parse an
3272    /// IANA time zone identifier. It is strongly recommended to use this
3273    /// directive whenever one is formatting or parsing `Zoned` values since
3274    /// it permits correctly round-tripping `Zoned` values.
3275    ///
3276    /// # Errors and panics
3277    ///
3278    /// While this routine itself does not error or panic, using the value
3279    /// returned may result in a panic if formatting fails. See the
3280    /// documentation on [`fmt::strtime::Display`] for more information.
3281    ///
3282    /// To format in a way that surfaces errors without panicking, use either
3283    /// [`fmt::strtime::format`] or [`fmt::strtime::BrokenDownTime::format`].
3284    ///
3285    /// # Example
3286    ///
3287    /// While the output of the Unix `date` command is likely locale specific,
3288    /// this is what it looks like on my system:
3289    ///
3290    /// ```
3291    /// use jiff::civil::date;
3292    ///
3293    /// let zdt = date(2024, 7, 15).at(16, 24, 59, 0).in_tz("America/New_York")?;
3294    /// let string = zdt.strftime("%a %b %e %I:%M:%S %p %Z %Y").to_string();
3295    /// assert_eq!(string, "Mon Jul 15 04:24:59 PM EDT 2024");
3296    ///
3297    /// # Ok::<(), Box<dyn std::error::Error>>(())
3298    /// ```
3299    #[inline]
3300    pub fn strftime<'f, F: 'f + ?Sized + AsRef<[u8]>>(
3301        &self,
3302        format: &'f F,
3303    ) -> fmt::strtime::Display<'f> {
3304        fmt::strtime::Display { fmt: format.as_ref(), tm: self.into() }
3305    }
3306}
3307
3308impl Default for Zoned {
3309    #[inline]
3310    fn default() -> Zoned {
3311        Zoned::new(Timestamp::default(), TimeZone::UTC)
3312    }
3313}
3314
3315/// Converts a `Zoned` datetime into a human readable datetime string.
3316///
3317/// (This `Debug` representation currently emits the same string as the
3318/// `Display` representation, but this is not a guarantee.)
3319///
3320/// Options currently supported:
3321///
3322/// * [`std::fmt::Formatter::precision`] can be set to control the precision
3323/// of the fractional second component.
3324///
3325/// # Example
3326///
3327/// ```
3328/// use jiff::civil::date;
3329///
3330/// let zdt = date(2024, 6, 15).at(7, 0, 0, 123_000_000).in_tz("US/Eastern")?;
3331/// assert_eq!(
3332///     format!("{zdt:.6?}"),
3333///     "2024-06-15T07:00:00.123000-04:00[US/Eastern]",
3334/// );
3335/// // Precision values greater than 9 are clamped to 9.
3336/// assert_eq!(
3337///     format!("{zdt:.300?}"),
3338///     "2024-06-15T07:00:00.123000000-04:00[US/Eastern]",
3339/// );
3340/// // A precision of 0 implies the entire fractional
3341/// // component is always truncated.
3342/// assert_eq!(
3343///     format!("{zdt:.0?}"),
3344///     "2024-06-15T07:00:00-04:00[US/Eastern]",
3345/// );
3346///
3347/// # Ok::<(), Box<dyn std::error::Error>>(())
3348/// ```
3349impl core::fmt::Debug for Zoned {
3350    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3351        core::fmt::Display::fmt(self, f)
3352    }
3353}
3354
3355/// Converts a `Zoned` datetime into a RFC 9557 compliant string.
3356///
3357/// # Formatting options supported
3358///
3359/// * [`std::fmt::Formatter::precision`] can be set to control the precision
3360/// of the fractional second component. When not set, the minimum precision
3361/// required to losslessly render the value is used.
3362///
3363/// # Example
3364///
3365/// This shows the default rendering:
3366///
3367/// ```
3368/// use jiff::civil::date;
3369///
3370/// // No fractional seconds:
3371/// let zdt = date(2024, 6, 15).at(7, 0, 0, 0).in_tz("US/Eastern")?;
3372/// assert_eq!(format!("{zdt}"), "2024-06-15T07:00:00-04:00[US/Eastern]");
3373///
3374/// // With fractional seconds:
3375/// let zdt = date(2024, 6, 15).at(7, 0, 0, 123_000_000).in_tz("US/Eastern")?;
3376/// assert_eq!(format!("{zdt}"), "2024-06-15T07:00:00.123-04:00[US/Eastern]");
3377///
3378/// # Ok::<(), Box<dyn std::error::Error>>(())
3379/// ```
3380///
3381/// # Example: setting the precision
3382///
3383/// ```
3384/// use jiff::civil::date;
3385///
3386/// let zdt = date(2024, 6, 15).at(7, 0, 0, 123_000_000).in_tz("US/Eastern")?;
3387/// assert_eq!(
3388///     format!("{zdt:.6}"),
3389///     "2024-06-15T07:00:00.123000-04:00[US/Eastern]",
3390/// );
3391/// // Precision values greater than 9 are clamped to 9.
3392/// assert_eq!(
3393///     format!("{zdt:.300}"),
3394///     "2024-06-15T07:00:00.123000000-04:00[US/Eastern]",
3395/// );
3396/// // A precision of 0 implies the entire fractional
3397/// // component is always truncated.
3398/// assert_eq!(
3399///     format!("{zdt:.0}"),
3400///     "2024-06-15T07:00:00-04:00[US/Eastern]",
3401/// );
3402///
3403/// # Ok::<(), Box<dyn std::error::Error>>(())
3404/// ```
3405impl core::fmt::Display for Zoned {
3406    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3407        use crate::fmt::StdFmtWrite;
3408
3409        let precision =
3410            f.precision().map(|p| u8::try_from(p).unwrap_or(u8::MAX));
3411        temporal::DateTimePrinter::new()
3412            .precision(precision)
3413            .print_zoned(self, StdFmtWrite(f))
3414            .map_err(|_| core::fmt::Error)
3415    }
3416}
3417
3418/// Parses a zoned timestamp from the Temporal datetime format.
3419///
3420/// See the [`fmt::temporal`](crate::fmt::temporal) for more information on
3421/// the precise format.
3422///
3423/// Note that this is only enabled when the `std` feature
3424/// is enabled because it requires access to a global
3425/// [`TimeZoneDatabase`](crate::tz::TimeZoneDatabase).
3426impl core::str::FromStr for Zoned {
3427    type Err = Error;
3428
3429    fn from_str(string: &str) -> Result<Zoned, Error> {
3430        DEFAULT_DATETIME_PARSER.parse_zoned(string)
3431    }
3432}
3433
3434impl Eq for Zoned {}
3435
3436impl PartialEq for Zoned {
3437    #[inline]
3438    fn eq(&self, rhs: &Zoned) -> bool {
3439        self.timestamp().eq(&rhs.timestamp())
3440    }
3441}
3442
3443impl<'a> PartialEq<Zoned> for &'a Zoned {
3444    #[inline]
3445    fn eq(&self, rhs: &Zoned) -> bool {
3446        (**self).eq(rhs)
3447    }
3448}
3449
3450impl Ord for Zoned {
3451    #[inline]
3452    fn cmp(&self, rhs: &Zoned) -> core::cmp::Ordering {
3453        self.timestamp().cmp(&rhs.timestamp())
3454    }
3455}
3456
3457impl PartialOrd for Zoned {
3458    #[inline]
3459    fn partial_cmp(&self, rhs: &Zoned) -> Option<core::cmp::Ordering> {
3460        Some(self.cmp(rhs))
3461    }
3462}
3463
3464impl<'a> PartialOrd<Zoned> for &'a Zoned {
3465    #[inline]
3466    fn partial_cmp(&self, rhs: &Zoned) -> Option<core::cmp::Ordering> {
3467        (**self).partial_cmp(rhs)
3468    }
3469}
3470
3471impl core::hash::Hash for Zoned {
3472    #[inline]
3473    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
3474        self.timestamp().hash(state);
3475    }
3476}
3477
3478#[cfg(feature = "std")]
3479impl TryFrom<std::time::SystemTime> for Zoned {
3480    type Error = Error;
3481
3482    #[inline]
3483    fn try_from(system_time: std::time::SystemTime) -> Result<Zoned, Error> {
3484        let timestamp = Timestamp::try_from(system_time)?;
3485        Ok(Zoned::new(timestamp, TimeZone::system()))
3486    }
3487}
3488
3489#[cfg(feature = "std")]
3490impl From<Zoned> for std::time::SystemTime {
3491    #[inline]
3492    fn from(time: Zoned) -> std::time::SystemTime {
3493        time.timestamp().into()
3494    }
3495}
3496
3497#[cfg(feature = "std")]
3498impl<'a> From<&'a Zoned> for std::time::SystemTime {
3499    #[inline]
3500    fn from(time: &'a Zoned) -> std::time::SystemTime {
3501        time.timestamp().into()
3502    }
3503}
3504
3505/// Adds a span of time to a zoned datetime.
3506///
3507/// This uses checked arithmetic and panics on overflow. To handle overflow
3508/// without panics, use [`Zoned::checked_add`].
3509///
3510/// Using this implementation will result in consuming the `Zoned` value. Since
3511/// it is not `Copy`, this will prevent further use. If this is undesirable,
3512/// consider using the trait implementation for `&Zoned`, `Zoned::checked_add`
3513/// or cloning the `Zoned` value.
3514impl<'a> core::ops::Add<Span> for Zoned {
3515    type Output = Zoned;
3516
3517    #[inline]
3518    fn add(self, rhs: Span) -> Zoned {
3519        (&self).add(rhs)
3520    }
3521}
3522
3523/// Adds a span of time to a borrowed zoned datetime.
3524///
3525/// This uses checked arithmetic and panics on overflow. To handle overflow
3526/// without panics, use [`Zoned::checked_add`].
3527impl<'a> core::ops::Add<Span> for &'a Zoned {
3528    type Output = Zoned;
3529
3530    #[inline]
3531    fn add(self, rhs: Span) -> Zoned {
3532        self.checked_add(rhs)
3533            .expect("adding span to zoned datetime overflowed")
3534    }
3535}
3536
3537/// Adds a span of time to a zoned datetime in place.
3538///
3539/// This uses checked arithmetic and panics on overflow. To handle overflow
3540/// without panics, use [`Zoned::checked_add`].
3541impl core::ops::AddAssign<Span> for Zoned {
3542    #[inline]
3543    fn add_assign(&mut self, rhs: Span) {
3544        *self = &*self + rhs
3545    }
3546}
3547
3548/// Subtracts a span of time from a zoned datetime.
3549///
3550/// This uses checked arithmetic and panics on overflow. To handle overflow
3551/// without panics, use [`Zoned::checked_sub`].
3552///
3553/// Using this implementation will result in consuming the `Zoned` value. Since
3554/// it is not `Copy`, this will prevent further use. If this is undesirable,
3555/// consider using the trait implementation for `&Zoned`, `Zoned::checked_sub`
3556/// or cloning the `Zoned` value.
3557impl<'a> core::ops::Sub<Span> for Zoned {
3558    type Output = Zoned;
3559
3560    #[inline]
3561    fn sub(self, rhs: Span) -> Zoned {
3562        (&self).sub(rhs)
3563    }
3564}
3565
3566/// Subtracts a span of time from a borrowed zoned datetime.
3567///
3568/// This uses checked arithmetic and panics on overflow. To handle overflow
3569/// without panics, use [`Zoned::checked_sub`].
3570impl<'a> core::ops::Sub<Span> for &'a Zoned {
3571    type Output = Zoned;
3572
3573    #[inline]
3574    fn sub(self, rhs: Span) -> Zoned {
3575        self.checked_sub(rhs)
3576            .expect("subtracting span from zoned datetime overflowed")
3577    }
3578}
3579
3580/// Subtracts a span of time from a zoned datetime in place.
3581///
3582/// This uses checked arithmetic and panics on overflow. To handle overflow
3583/// without panics, use [`Zoned::checked_sub`].
3584impl core::ops::SubAssign<Span> for Zoned {
3585    #[inline]
3586    fn sub_assign(&mut self, rhs: Span) {
3587        *self = &*self - rhs
3588    }
3589}
3590
3591/// Computes the span of time between two zoned datetimes.
3592///
3593/// This will return a negative span when the zoned datetime being subtracted
3594/// is greater.
3595///
3596/// Since this uses the default configuration for calculating a span between
3597/// two zoned datetimes (no rounding and largest units is hours), this will
3598/// never panic or fail in any way. It is guaranteed that the largest non-zero
3599/// unit in the `Span` returned will be hours.
3600///
3601/// To configure the largest unit or enable rounding, use [`Zoned::since`].
3602///
3603/// Using this implementation will result in consuming the `Zoned` value. Since
3604/// it is not `Copy`, this will prevent further use. If this is undesirable,
3605/// consider using the trait implementation for `&Zoned`, `Zoned::since`,
3606/// `Zoned::until` or cloning the `Zoned` value.
3607impl core::ops::Sub for Zoned {
3608    type Output = Span;
3609
3610    #[inline]
3611    fn sub(self, rhs: Zoned) -> Span {
3612        (&self).sub(&rhs)
3613    }
3614}
3615
3616/// Computes the span of time between two borrowed zoned datetimes.
3617///
3618/// This will return a negative span when the zoned datetime being subtracted
3619/// is greater.
3620///
3621/// Since this uses the default configuration for calculating a span between
3622/// two zoned datetimes (no rounding and largest units is hours), this will
3623/// never panic or fail in any way. It is guaranteed that the largest non-zero
3624/// unit in the `Span` returned will be hours.
3625///
3626/// To configure the largest unit or enable rounding, use [`Zoned::since`].
3627impl<'a> core::ops::Sub for &'a Zoned {
3628    type Output = Span;
3629
3630    #[inline]
3631    fn sub(self, rhs: &'a Zoned) -> Span {
3632        self.since(rhs).expect("since never fails when given Zoned")
3633    }
3634}
3635
3636/// Adds a signed duration of time to a zoned datetime.
3637///
3638/// This uses checked arithmetic and panics on overflow. To handle overflow
3639/// without panics, use [`Zoned::checked_add`].
3640///
3641/// Using this implementation will result in consuming the `Zoned` value. Since
3642/// it is not `Copy`, this will prevent further use. If this is undesirable,
3643/// consider using the trait implementation for `&Zoned`, `Zoned::checked_add`
3644/// or cloning the `Zoned` value.
3645impl core::ops::Add<SignedDuration> for Zoned {
3646    type Output = Zoned;
3647
3648    #[inline]
3649    fn add(self, rhs: SignedDuration) -> Zoned {
3650        (&self).add(rhs)
3651    }
3652}
3653
3654/// Adds a signed duration of time to a borrowed zoned datetime.
3655///
3656/// This uses checked arithmetic and panics on overflow. To handle overflow
3657/// without panics, use [`Zoned::checked_add`].
3658impl<'a> core::ops::Add<SignedDuration> for &'a Zoned {
3659    type Output = Zoned;
3660
3661    #[inline]
3662    fn add(self, rhs: SignedDuration) -> Zoned {
3663        self.checked_add(rhs)
3664            .expect("adding signed duration to zoned datetime overflowed")
3665    }
3666}
3667
3668/// Adds a signed duration of time to a zoned datetime in place.
3669///
3670/// This uses checked arithmetic and panics on overflow. To handle overflow
3671/// without panics, use [`Zoned::checked_add`].
3672impl core::ops::AddAssign<SignedDuration> for Zoned {
3673    #[inline]
3674    fn add_assign(&mut self, rhs: SignedDuration) {
3675        *self = &*self + rhs
3676    }
3677}
3678
3679/// Subtracts a signed duration of time from a zoned datetime.
3680///
3681/// This uses checked arithmetic and panics on overflow. To handle overflow
3682/// without panics, use [`Zoned::checked_sub`].
3683///
3684/// Using this implementation will result in consuming the `Zoned` value. Since
3685/// it is not `Copy`, this will prevent further use. If this is undesirable,
3686/// consider using the trait implementation for `&Zoned`, `Zoned::checked_sub`
3687/// or cloning the `Zoned` value.
3688impl core::ops::Sub<SignedDuration> for Zoned {
3689    type Output = Zoned;
3690
3691    #[inline]
3692    fn sub(self, rhs: SignedDuration) -> Zoned {
3693        (&self).sub(rhs)
3694    }
3695}
3696
3697/// Subtracts a signed duration of time from a borrowed zoned datetime.
3698///
3699/// This uses checked arithmetic and panics on overflow. To handle overflow
3700/// without panics, use [`Zoned::checked_sub`].
3701impl<'a> core::ops::Sub<SignedDuration> for &'a Zoned {
3702    type Output = Zoned;
3703
3704    #[inline]
3705    fn sub(self, rhs: SignedDuration) -> Zoned {
3706        self.checked_sub(rhs).expect(
3707            "subtracting signed duration from zoned datetime overflowed",
3708        )
3709    }
3710}
3711
3712/// Subtracts a signed duration of time from a zoned datetime in place.
3713///
3714/// This uses checked arithmetic and panics on overflow. To handle overflow
3715/// without panics, use [`Zoned::checked_sub`].
3716impl core::ops::SubAssign<SignedDuration> for Zoned {
3717    #[inline]
3718    fn sub_assign(&mut self, rhs: SignedDuration) {
3719        *self = &*self - rhs
3720    }
3721}
3722
3723/// Adds an unsigned duration of time to a zoned datetime.
3724///
3725/// This uses checked arithmetic and panics on overflow. To handle overflow
3726/// without panics, use [`Zoned::checked_add`].
3727///
3728/// Using this implementation will result in consuming the `Zoned` value. Since
3729/// it is not `Copy`, this will prevent further use. If this is undesirable,
3730/// consider using the trait implementation for `&Zoned`, `Zoned::checked_add`
3731/// or cloning the `Zoned` value.
3732impl core::ops::Add<UnsignedDuration> for Zoned {
3733    type Output = Zoned;
3734
3735    #[inline]
3736    fn add(self, rhs: UnsignedDuration) -> Zoned {
3737        (&self).add(rhs)
3738    }
3739}
3740
3741/// Adds an unsigned duration of time to a borrowed zoned datetime.
3742///
3743/// This uses checked arithmetic and panics on overflow. To handle overflow
3744/// without panics, use [`Zoned::checked_add`].
3745impl<'a> core::ops::Add<UnsignedDuration> for &'a Zoned {
3746    type Output = Zoned;
3747
3748    #[inline]
3749    fn add(self, rhs: UnsignedDuration) -> Zoned {
3750        self.checked_add(rhs)
3751            .expect("adding unsigned duration to zoned datetime overflowed")
3752    }
3753}
3754
3755/// Adds an unsigned duration of time to a zoned datetime in place.
3756///
3757/// This uses checked arithmetic and panics on overflow. To handle overflow
3758/// without panics, use [`Zoned::checked_add`].
3759impl core::ops::AddAssign<UnsignedDuration> for Zoned {
3760    #[inline]
3761    fn add_assign(&mut self, rhs: UnsignedDuration) {
3762        *self = &*self + rhs
3763    }
3764}
3765
3766/// Subtracts an unsigned duration of time from a zoned datetime.
3767///
3768/// This uses checked arithmetic and panics on overflow. To handle overflow
3769/// without panics, use [`Zoned::checked_sub`].
3770///
3771/// Using this implementation will result in consuming the `Zoned` value. Since
3772/// it is not `Copy`, this will prevent further use. If this is undesirable,
3773/// consider using the trait implementation for `&Zoned`, `Zoned::checked_sub`
3774/// or cloning the `Zoned` value.
3775impl core::ops::Sub<UnsignedDuration> for Zoned {
3776    type Output = Zoned;
3777
3778    #[inline]
3779    fn sub(self, rhs: UnsignedDuration) -> Zoned {
3780        (&self).sub(rhs)
3781    }
3782}
3783
3784/// Subtracts an unsigned duration of time from a borrowed zoned datetime.
3785///
3786/// This uses checked arithmetic and panics on overflow. To handle overflow
3787/// without panics, use [`Zoned::checked_sub`].
3788impl<'a> core::ops::Sub<UnsignedDuration> for &'a Zoned {
3789    type Output = Zoned;
3790
3791    #[inline]
3792    fn sub(self, rhs: UnsignedDuration) -> Zoned {
3793        self.checked_sub(rhs).expect(
3794            "subtracting unsigned duration from zoned datetime overflowed",
3795        )
3796    }
3797}
3798
3799/// Subtracts an unsigned duration of time from a zoned datetime in place.
3800///
3801/// This uses checked arithmetic and panics on overflow. To handle overflow
3802/// without panics, use [`Zoned::checked_sub`].
3803impl core::ops::SubAssign<UnsignedDuration> for Zoned {
3804    #[inline]
3805    fn sub_assign(&mut self, rhs: UnsignedDuration) {
3806        *self = &*self - rhs
3807    }
3808}
3809
3810#[cfg(feature = "serde")]
3811impl serde_core::Serialize for Zoned {
3812    #[inline]
3813    fn serialize<S: serde_core::Serializer>(
3814        &self,
3815        serializer: S,
3816    ) -> Result<S::Ok, S::Error> {
3817        serializer.collect_str(self)
3818    }
3819}
3820
3821#[cfg(feature = "serde")]
3822impl<'de> serde_core::Deserialize<'de> for Zoned {
3823    #[inline]
3824    fn deserialize<D: serde_core::Deserializer<'de>>(
3825        deserializer: D,
3826    ) -> Result<Zoned, D::Error> {
3827        use serde_core::de;
3828
3829        struct ZonedVisitor;
3830
3831        impl<'de> de::Visitor<'de> for ZonedVisitor {
3832            type Value = Zoned;
3833
3834            fn expecting(
3835                &self,
3836                f: &mut core::fmt::Formatter,
3837            ) -> core::fmt::Result {
3838                f.write_str("a zoned datetime string")
3839            }
3840
3841            #[inline]
3842            fn visit_bytes<E: de::Error>(
3843                self,
3844                value: &[u8],
3845            ) -> Result<Zoned, E> {
3846                DEFAULT_DATETIME_PARSER
3847                    .parse_zoned(value)
3848                    .map_err(de::Error::custom)
3849            }
3850
3851            #[inline]
3852            fn visit_str<E: de::Error>(self, value: &str) -> Result<Zoned, E> {
3853                self.visit_bytes(value.as_bytes())
3854            }
3855        }
3856
3857        deserializer.deserialize_str(ZonedVisitor)
3858    }
3859}
3860
3861#[cfg(test)]
3862impl quickcheck::Arbitrary for Zoned {
3863    fn arbitrary(g: &mut quickcheck::Gen) -> Zoned {
3864        let timestamp = Timestamp::arbitrary(g);
3865        let tz = TimeZone::UTC; // TODO: do something better here?
3866        Zoned::new(timestamp, tz)
3867    }
3868
3869    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
3870        let timestamp = self.timestamp();
3871        alloc::boxed::Box::new(
3872            timestamp
3873                .shrink()
3874                .map(|timestamp| Zoned::new(timestamp, TimeZone::UTC)),
3875        )
3876    }
3877}
3878
3879/// An iterator over periodic zoned datetimes, created by [`Zoned::series`].
3880///
3881/// It is exhausted when the next value would exceed the limits of a [`Span`]
3882/// or [`Zoned`] value.
3883///
3884/// This iterator is created by [`Zoned::series`].
3885#[derive(Clone, Debug)]
3886pub struct ZonedSeries {
3887    start: Zoned,
3888    prev: Option<Timestamp>,
3889    period: Span,
3890    step: i64,
3891}
3892
3893impl Iterator for ZonedSeries {
3894    type Item = Zoned;
3895
3896    #[inline]
3897    fn next(&mut self) -> Option<Zoned> {
3898        // This loop is necessary because adding, e.g., `N * 1 day` may not
3899        // always result in a timestamp that is strictly greater than
3900        // `(N-1) * 1 day`. For example, `Pacific/Apia` never had `2011-12-30`
3901        // on their clocks. So adding `1 day` to `2011-12-29` yields the same
3902        // value as adding `2 days` (that is, `2011-12-31`).
3903        //
3904        // This may seem odd, but Temporal has the same behavior (as of
3905        // 2025-10-15):
3906        //
3907        //   >>> zdt = Temporal.ZonedDateTime.from("2011-12-29[Pacific/Apia]")
3908        //   Object { … }
3909        //   >>> zdt.toString()
3910        //   "2011-12-29T00:00:00-10:00[Pacific/Apia]"
3911        //   >>> zdt.add({days: 1}).toString()
3912        //   "2011-12-31T00:00:00+14:00[Pacific/Apia]"
3913        //   >>> zdt.add({days: 2}).toString()
3914        //   "2011-12-31T00:00:00+14:00[Pacific/Apia]"
3915        //
3916        // Since we are generating a time series specifically here, it seems
3917        // weird to yield two results that are equivalent instants in time.
3918        // So we use a loop here to guarantee that every instant yielded is
3919        // always strictly *after* the previous instant yielded.
3920        loop {
3921            let span = self.period.checked_mul(self.step).ok()?;
3922            self.step = self.step.checked_add(1)?;
3923            let zdt = self.start.checked_add(span).ok()?;
3924            if self.prev.map_or(true, |prev| {
3925                if self.period.is_positive() {
3926                    prev < zdt.timestamp()
3927                } else if self.period.is_negative() {
3928                    prev > zdt.timestamp()
3929                } else {
3930                    assert!(self.period.is_zero());
3931                    // In the case of a zero span, the caller has clearly
3932                    // opted into an infinite repeating sequence.
3933                    true
3934                }
3935            }) {
3936                self.prev = Some(zdt.timestamp());
3937                return Some(zdt);
3938            }
3939        }
3940    }
3941}
3942
3943impl core::iter::FusedIterator for ZonedSeries {}
3944
3945/// Options for [`Timestamp::checked_add`] and [`Timestamp::checked_sub`].
3946///
3947/// This type provides a way to ergonomically add one of a few different
3948/// duration types to a [`Timestamp`].
3949///
3950/// The main way to construct values of this type is with its `From` trait
3951/// implementations:
3952///
3953/// * `From<Span> for ZonedArithmetic` adds (or subtracts) the given span
3954/// to the receiver timestamp.
3955/// * `From<SignedDuration> for ZonedArithmetic` adds (or subtracts)
3956/// the given signed duration to the receiver timestamp.
3957/// * `From<std::time::Duration> for ZonedArithmetic` adds (or subtracts)
3958/// the given unsigned duration to the receiver timestamp.
3959///
3960/// # Example
3961///
3962/// ```
3963/// use std::time::Duration;
3964///
3965/// use jiff::{SignedDuration, Timestamp, ToSpan};
3966///
3967/// let ts: Timestamp = "2024-02-28T00:00:00Z".parse()?;
3968/// assert_eq!(
3969///     ts.checked_add(48.hours())?,
3970///     "2024-03-01T00:00:00Z".parse()?,
3971/// );
3972/// assert_eq!(
3973///     ts.checked_add(SignedDuration::from_hours(48))?,
3974///     "2024-03-01T00:00:00Z".parse()?,
3975/// );
3976/// assert_eq!(
3977///     ts.checked_add(Duration::from_secs(48 * 60 * 60))?,
3978///     "2024-03-01T00:00:00Z".parse()?,
3979/// );
3980///
3981/// # Ok::<(), Box<dyn std::error::Error>>(())
3982/// ```
3983#[derive(Clone, Copy, Debug)]
3984pub struct ZonedArithmetic {
3985    duration: Duration,
3986}
3987
3988impl ZonedArithmetic {
3989    #[inline]
3990    fn checked_add(self, zdt: &Zoned) -> Result<Zoned, Error> {
3991        match self.duration.to_signed()? {
3992            SDuration::Span(span) => zdt.checked_add_span(span),
3993            SDuration::Absolute(sdur) => zdt.checked_add_duration(sdur),
3994        }
3995    }
3996
3997    #[inline]
3998    fn checked_neg(self) -> Result<ZonedArithmetic, Error> {
3999        let duration = self.duration.checked_neg()?;
4000        Ok(ZonedArithmetic { duration })
4001    }
4002
4003    #[inline]
4004    fn is_negative(&self) -> bool {
4005        self.duration.is_negative()
4006    }
4007}
4008
4009impl From<Span> for ZonedArithmetic {
4010    fn from(span: Span) -> ZonedArithmetic {
4011        let duration = Duration::from(span);
4012        ZonedArithmetic { duration }
4013    }
4014}
4015
4016impl From<SignedDuration> for ZonedArithmetic {
4017    fn from(sdur: SignedDuration) -> ZonedArithmetic {
4018        let duration = Duration::from(sdur);
4019        ZonedArithmetic { duration }
4020    }
4021}
4022
4023impl From<UnsignedDuration> for ZonedArithmetic {
4024    fn from(udur: UnsignedDuration) -> ZonedArithmetic {
4025        let duration = Duration::from(udur);
4026        ZonedArithmetic { duration }
4027    }
4028}
4029
4030impl<'a> From<&'a Span> for ZonedArithmetic {
4031    fn from(span: &'a Span) -> ZonedArithmetic {
4032        ZonedArithmetic::from(*span)
4033    }
4034}
4035
4036impl<'a> From<&'a SignedDuration> for ZonedArithmetic {
4037    fn from(sdur: &'a SignedDuration) -> ZonedArithmetic {
4038        ZonedArithmetic::from(*sdur)
4039    }
4040}
4041
4042impl<'a> From<&'a UnsignedDuration> for ZonedArithmetic {
4043    fn from(udur: &'a UnsignedDuration) -> ZonedArithmetic {
4044        ZonedArithmetic::from(*udur)
4045    }
4046}
4047
4048/// Options for [`Zoned::since`] and [`Zoned::until`].
4049///
4050/// This type provides a way to configure the calculation of spans between two
4051/// [`Zoned`] values. In particular, both `Zoned::since` and `Zoned::until`
4052/// accept anything that implements `Into<ZonedDifference>`. There are a few
4053/// key trait implementations that make this convenient:
4054///
4055/// * `From<&Zoned> for ZonedDifference` will construct a configuration
4056/// consisting of just the zoned datetime. So for example, `zdt1.since(zdt2)`
4057/// returns the span from `zdt2` to `zdt1`.
4058/// * `From<(Unit, &Zoned)>` is a convenient way to specify the largest units
4059/// that should be present on the span returned. By default, the largest units
4060/// are days. Using this trait implementation is equivalent to
4061/// `ZonedDifference::new(&zdt).largest(unit)`.
4062///
4063/// One can also provide a `ZonedDifference` value directly. Doing so
4064/// is necessary to use the rounding features of calculating a span. For
4065/// example, setting the smallest unit (defaults to [`Unit::Nanosecond`]), the
4066/// rounding mode (defaults to [`RoundMode::Trunc`]) and the rounding increment
4067/// (defaults to `1`). The defaults are selected such that no rounding occurs.
4068///
4069/// Rounding a span as part of calculating it is provided as a convenience.
4070/// Callers may choose to round the span as a distinct step via
4071/// [`Span::round`], but callers may need to provide a reference date
4072/// for rounding larger units. By coupling rounding with routines like
4073/// [`Zoned::since`], the reference date can be set automatically based on
4074/// the input to `Zoned::since`.
4075///
4076/// # Example
4077///
4078/// This example shows how to round a span between two zoned datetimes to the
4079/// nearest half-hour, with ties breaking away from zero.
4080///
4081/// ```
4082/// use jiff::{RoundMode, ToSpan, Unit, Zoned, ZonedDifference};
4083///
4084/// let zdt1 = "2024-03-15 08:14:00.123456789[America/New_York]".parse::<Zoned>()?;
4085/// let zdt2 = "2030-03-22 15:00[America/New_York]".parse::<Zoned>()?;
4086/// let span = zdt1.until(
4087///     ZonedDifference::new(&zdt2)
4088///         .smallest(Unit::Minute)
4089///         .largest(Unit::Year)
4090///         .mode(RoundMode::HalfExpand)
4091///         .increment(30),
4092/// )?;
4093/// assert_eq!(span, 6.years().days(7).hours(7).fieldwise());
4094///
4095/// # Ok::<(), Box<dyn std::error::Error>>(())
4096/// ```
4097#[derive(Clone, Copy, Debug)]
4098pub struct ZonedDifference<'a> {
4099    zoned: &'a Zoned,
4100    round: SpanRound<'static>,
4101}
4102
4103impl<'a> ZonedDifference<'a> {
4104    /// Create a new default configuration for computing the span between the
4105    /// given zoned datetime and some other zoned datetime (specified as the
4106    /// receiver in [`Zoned::since`] or [`Zoned::until`]).
4107    #[inline]
4108    pub fn new(zoned: &'a Zoned) -> ZonedDifference<'a> {
4109        // We use truncation rounding by default since it seems that's
4110        // what is generally expected when computing the difference between
4111        // datetimes.
4112        //
4113        // See: https://github.com/tc39/proposal-temporal/issues/1122
4114        let round = SpanRound::new().mode(RoundMode::Trunc);
4115        ZonedDifference { zoned, round }
4116    }
4117
4118    /// Set the smallest units allowed in the span returned.
4119    ///
4120    /// When a largest unit is not specified and the smallest unit is hours
4121    /// or greater, then the largest unit is automatically set to be equal to
4122    /// the smallest unit.
4123    ///
4124    /// # Errors
4125    ///
4126    /// The smallest units must be no greater than the largest units. If this
4127    /// is violated, then computing a span with this configuration will result
4128    /// in an error.
4129    ///
4130    /// # Example
4131    ///
4132    /// This shows how to round a span between two zoned datetimes to the
4133    /// nearest number of weeks.
4134    ///
4135    /// ```
4136    /// use jiff::{RoundMode, ToSpan, Unit, Zoned, ZonedDifference};
4137    ///
4138    /// let zdt1 = "2024-03-15 08:14[America/New_York]".parse::<Zoned>()?;
4139    /// let zdt2 = "2030-11-22 08:30[America/New_York]".parse::<Zoned>()?;
4140    /// let span = zdt1.until(
4141    ///     ZonedDifference::new(&zdt2)
4142    ///         .smallest(Unit::Week)
4143    ///         .largest(Unit::Week)
4144    ///         .mode(RoundMode::HalfExpand),
4145    /// )?;
4146    /// assert_eq!(format!("{span:#}"), "349w");
4147    ///
4148    /// # Ok::<(), Box<dyn std::error::Error>>(())
4149    /// ```
4150    #[inline]
4151    pub fn smallest(self, unit: Unit) -> ZonedDifference<'a> {
4152        ZonedDifference { round: self.round.smallest(unit), ..self }
4153    }
4154
4155    /// Set the largest units allowed in the span returned.
4156    ///
4157    /// When a largest unit is not specified and the smallest unit is hours
4158    /// or greater, then the largest unit is automatically set to be equal to
4159    /// the smallest unit. Otherwise, when the largest unit is not specified,
4160    /// it is set to hours.
4161    ///
4162    /// Once a largest unit is set, there is no way to change this rounding
4163    /// configuration back to using the "automatic" default. Instead, callers
4164    /// must create a new configuration.
4165    ///
4166    /// # Errors
4167    ///
4168    /// The largest units, when set, must be at least as big as the smallest
4169    /// units (which defaults to [`Unit::Nanosecond`]). If this is violated,
4170    /// then computing a span with this configuration will result in an error.
4171    ///
4172    /// # Example
4173    ///
4174    /// This shows how to round a span between two zoned datetimes to units no
4175    /// bigger than seconds.
4176    ///
4177    /// ```
4178    /// use jiff::{ToSpan, Unit, Zoned, ZonedDifference};
4179    ///
4180    /// let zdt1 = "2024-03-15 08:14[America/New_York]".parse::<Zoned>()?;
4181    /// let zdt2 = "2030-11-22 08:30[America/New_York]".parse::<Zoned>()?;
4182    /// let span = zdt1.until(
4183    ///     ZonedDifference::new(&zdt2).largest(Unit::Second),
4184    /// )?;
4185    /// assert_eq!(span.to_string(), "PT211079760S");
4186    ///
4187    /// # Ok::<(), Box<dyn std::error::Error>>(())
4188    /// ```
4189    #[inline]
4190    pub fn largest(self, unit: Unit) -> ZonedDifference<'a> {
4191        ZonedDifference { round: self.round.largest(unit), ..self }
4192    }
4193
4194    /// Set the rounding mode.
4195    ///
4196    /// This defaults to [`RoundMode::Trunc`] since it's plausible that
4197    /// rounding "up" in the context of computing the span between
4198    /// two zoned datetimes could be surprising in a number of cases. The
4199    /// [`RoundMode::HalfExpand`] mode corresponds to typical rounding you
4200    /// might have learned about in school. But a variety of other rounding
4201    /// modes exist.
4202    ///
4203    /// # Example
4204    ///
4205    /// This shows how to always round "up" towards positive infinity.
4206    ///
4207    /// ```
4208    /// use jiff::{RoundMode, ToSpan, Unit, Zoned, ZonedDifference};
4209    ///
4210    /// let zdt1 = "2024-03-15 08:10[America/New_York]".parse::<Zoned>()?;
4211    /// let zdt2 = "2024-03-15 08:11[America/New_York]".parse::<Zoned>()?;
4212    /// let span = zdt1.until(
4213    ///     ZonedDifference::new(&zdt2)
4214    ///         .smallest(Unit::Hour)
4215    ///         .mode(RoundMode::Ceil),
4216    /// )?;
4217    /// // Only one minute elapsed, but we asked to always round up!
4218    /// assert_eq!(span, 1.hour().fieldwise());
4219    ///
4220    /// // Since `Ceil` always rounds toward positive infinity, the behavior
4221    /// // flips for a negative span.
4222    /// let span = zdt1.since(
4223    ///     ZonedDifference::new(&zdt2)
4224    ///         .smallest(Unit::Hour)
4225    ///         .mode(RoundMode::Ceil),
4226    /// )?;
4227    /// assert_eq!(span, 0.hour().fieldwise());
4228    ///
4229    /// # Ok::<(), Box<dyn std::error::Error>>(())
4230    /// ```
4231    #[inline]
4232    pub fn mode(self, mode: RoundMode) -> ZonedDifference<'a> {
4233        ZonedDifference { round: self.round.mode(mode), ..self }
4234    }
4235
4236    /// Set the rounding increment for the smallest unit.
4237    ///
4238    /// The default value is `1`. Other values permit rounding the smallest
4239    /// unit to the nearest integer increment specified. For example, if the
4240    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
4241    /// `30` would result in rounding in increments of a half hour. That is,
4242    /// the only minute value that could result would be `0` or `30`.
4243    ///
4244    /// # Errors
4245    ///
4246    /// When the smallest unit is less than days, the rounding increment must
4247    /// divide evenly into the next highest unit after the smallest unit
4248    /// configured (and must not be equivalent to it). For example, if the
4249    /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
4250    /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
4251    /// Namely, any integer that divides evenly into `1,000` nanoseconds since
4252    /// there are `1,000` nanoseconds in the next highest unit (microseconds).
4253    ///
4254    /// The error will occur when computing the span, and not when setting
4255    /// the increment here.
4256    ///
4257    /// # Example
4258    ///
4259    /// This shows how to round the span between two zoned datetimes to the
4260    /// nearest 5 minute increment.
4261    ///
4262    /// ```
4263    /// use jiff::{RoundMode, ToSpan, Unit, Zoned, ZonedDifference};
4264    ///
4265    /// let zdt1 = "2024-03-15 08:19[America/New_York]".parse::<Zoned>()?;
4266    /// let zdt2 = "2024-03-15 12:52[America/New_York]".parse::<Zoned>()?;
4267    /// let span = zdt1.until(
4268    ///     ZonedDifference::new(&zdt2)
4269    ///         .smallest(Unit::Minute)
4270    ///         .increment(5)
4271    ///         .mode(RoundMode::HalfExpand),
4272    /// )?;
4273    /// assert_eq!(format!("{span:#}"), "4h 35m");
4274    ///
4275    /// # Ok::<(), Box<dyn std::error::Error>>(())
4276    /// ```
4277    #[inline]
4278    pub fn increment(self, increment: i64) -> ZonedDifference<'a> {
4279        ZonedDifference { round: self.round.increment(increment), ..self }
4280    }
4281
4282    /// Returns true if and only if this configuration could change the span
4283    /// via rounding.
4284    #[inline]
4285    fn rounding_may_change_span(&self) -> bool {
4286        self.round.rounding_may_change_span_ignore_largest()
4287    }
4288
4289    /// Returns the span of time from `dt1` to the datetime in this
4290    /// configuration. The biggest units allowed are determined by the
4291    /// `smallest` and `largest` settings, but defaults to `Unit::Day`.
4292    #[inline]
4293    fn until_with_largest_unit(&self, zdt1: &Zoned) -> Result<Span, Error> {
4294        let zdt2 = self.zoned;
4295
4296        let sign = t::sign(zdt2, zdt1);
4297        if sign == C(0) {
4298            return Ok(Span::new());
4299        }
4300
4301        let largest = self
4302            .round
4303            .get_largest()
4304            .unwrap_or_else(|| self.round.get_smallest().max(Unit::Hour));
4305        if largest < Unit::Day {
4306            return zdt1.timestamp().until((largest, zdt2.timestamp()));
4307        }
4308        if zdt1.time_zone() != zdt2.time_zone() {
4309            return Err(Error::from(E::MismatchTimeZoneUntil { largest }));
4310        }
4311        let tz = zdt1.time_zone();
4312
4313        let (dt1, mut dt2) = (zdt1.datetime(), zdt2.datetime());
4314
4315        let mut day_correct: t::SpanDays = C(0).rinto();
4316        if -sign == dt1.time().until_nanoseconds(dt2.time()).signum() {
4317            day_correct += C(1);
4318        }
4319
4320        let mut mid = dt2
4321            .date()
4322            .checked_add(Span::new().days_ranged(day_correct * -sign))
4323            .context(E::AddDays)?
4324            .to_datetime(dt1.time());
4325        let mut zmid: Zoned = mid
4326            .to_zoned(tz.clone())
4327            .context(E::ConvertIntermediateDatetime)?;
4328        if t::sign(zdt2, &zmid) == -sign {
4329            if sign == C(-1) {
4330                // FIXME
4331                panic!("this should be an error");
4332            }
4333            day_correct += C(1);
4334            mid = dt2
4335                .date()
4336                .checked_add(Span::new().days_ranged(day_correct * -sign))
4337                .context(E::AddDays)?
4338                .to_datetime(dt1.time());
4339            zmid = mid
4340                .to_zoned(tz.clone())
4341                .context(E::ConvertIntermediateDatetime)?;
4342            if t::sign(zdt2, &zmid) == -sign {
4343                // FIXME
4344                panic!("this should be an error too");
4345            }
4346        }
4347        let remainder_nano = zdt2.timestamp().as_nanosecond_ranged()
4348            - zmid.timestamp().as_nanosecond_ranged();
4349        dt2 = mid;
4350
4351        let date_span = dt1.date().until((largest, dt2.date()))?;
4352        Ok(Span::from_invariant_nanoseconds(
4353            Unit::Hour,
4354            remainder_nano.rinto(),
4355        )
4356        .expect("difference between time always fits in span")
4357        .years_ranged(date_span.get_years_ranged())
4358        .months_ranged(date_span.get_months_ranged())
4359        .weeks_ranged(date_span.get_weeks_ranged())
4360        .days_ranged(date_span.get_days_ranged()))
4361    }
4362}
4363
4364impl<'a> From<&'a Zoned> for ZonedDifference<'a> {
4365    #[inline]
4366    fn from(zdt: &'a Zoned) -> ZonedDifference<'a> {
4367        ZonedDifference::new(zdt)
4368    }
4369}
4370
4371impl<'a> From<(Unit, &'a Zoned)> for ZonedDifference<'a> {
4372    #[inline]
4373    fn from((largest, zdt): (Unit, &'a Zoned)) -> ZonedDifference<'a> {
4374        ZonedDifference::new(zdt).largest(largest)
4375    }
4376}
4377
4378/// Options for [`Zoned::round`].
4379///
4380/// This type provides a way to configure the rounding of a zoned datetime. In
4381/// particular, `Zoned::round` accepts anything that implements the
4382/// `Into<ZonedRound>` trait. There are some trait implementations that
4383/// therefore make calling `Zoned::round` in some common cases more
4384/// ergonomic:
4385///
4386/// * `From<Unit> for ZonedRound` will construct a rounding
4387/// configuration that rounds to the unit given. Specifically,
4388/// `ZonedRound::new().smallest(unit)`.
4389/// * `From<(Unit, i64)> for ZonedRound` is like the one above, but also
4390/// specifies the rounding increment for [`ZonedRound::increment`].
4391///
4392/// Note that in the default configuration, no rounding occurs.
4393///
4394/// # Example
4395///
4396/// This example shows how to round a zoned datetime to the nearest second:
4397///
4398/// ```
4399/// use jiff::{civil::date, Unit, Zoned};
4400///
4401/// let zdt: Zoned = "2024-06-20 16:24:59.5[America/New_York]".parse()?;
4402/// assert_eq!(
4403///     zdt.round(Unit::Second)?,
4404///     // The second rounds up and causes minutes to increase.
4405///     date(2024, 6, 20).at(16, 25, 0, 0).in_tz("America/New_York")?,
4406/// );
4407///
4408/// # Ok::<(), Box<dyn std::error::Error>>(())
4409/// ```
4410///
4411/// The above makes use of the fact that `Unit` implements
4412/// `Into<ZonedRound>`. If you want to change the rounding mode to, say,
4413/// truncation, then you'll need to construct a `ZonedRound` explicitly
4414/// since there are no convenience `Into` trait implementations for
4415/// [`RoundMode`].
4416///
4417/// ```
4418/// use jiff::{civil::date, RoundMode, Unit, Zoned, ZonedRound};
4419///
4420/// let zdt: Zoned = "2024-06-20 16:24:59.5[America/New_York]".parse()?;
4421/// assert_eq!(
4422///     zdt.round(
4423///         ZonedRound::new().smallest(Unit::Second).mode(RoundMode::Trunc),
4424///     )?,
4425///     // The second just gets truncated as if it wasn't there.
4426///     date(2024, 6, 20).at(16, 24, 59, 0).in_tz("America/New_York")?,
4427/// );
4428///
4429/// # Ok::<(), Box<dyn std::error::Error>>(())
4430/// ```
4431#[derive(Clone, Copy, Debug)]
4432pub struct ZonedRound {
4433    round: DateTimeRound,
4434}
4435
4436impl ZonedRound {
4437    /// Create a new default configuration for rounding a [`Zoned`].
4438    #[inline]
4439    pub fn new() -> ZonedRound {
4440        ZonedRound { round: DateTimeRound::new() }
4441    }
4442
4443    /// Set the smallest units allowed in the zoned datetime returned after
4444    /// rounding.
4445    ///
4446    /// Any units below the smallest configured unit will be used, along
4447    /// with the rounding increment and rounding mode, to determine
4448    /// the value of the smallest unit. For example, when rounding
4449    /// `2024-06-20T03:25:30[America/New_York]` to the nearest minute, the `30`
4450    /// second unit will result in rounding the minute unit of `25` up to `26`
4451    /// and zeroing out everything below minutes.
4452    ///
4453    /// This defaults to [`Unit::Nanosecond`].
4454    ///
4455    /// # Errors
4456    ///
4457    /// The smallest units must be no greater than [`Unit::Day`]. And when the
4458    /// smallest unit is `Unit::Day`, the rounding increment must be equal to
4459    /// `1`. Otherwise an error will be returned from [`Zoned::round`].
4460    ///
4461    /// # Example
4462    ///
4463    /// ```
4464    /// use jiff::{civil::date, Unit, ZonedRound};
4465    ///
4466    /// let zdt = date(2024, 6, 20).at(3, 25, 30, 0).in_tz("America/New_York")?;
4467    /// assert_eq!(
4468    ///     zdt.round(ZonedRound::new().smallest(Unit::Minute))?,
4469    ///     date(2024, 6, 20).at(3, 26, 0, 0).in_tz("America/New_York")?,
4470    /// );
4471    /// // Or, utilize the `From<Unit> for ZonedRound` impl:
4472    /// assert_eq!(
4473    ///     zdt.round(Unit::Minute)?,
4474    ///     date(2024, 6, 20).at(3, 26, 0, 0).in_tz("America/New_York")?,
4475    /// );
4476    ///
4477    /// # Ok::<(), Box<dyn std::error::Error>>(())
4478    /// ```
4479    #[inline]
4480    pub fn smallest(self, unit: Unit) -> ZonedRound {
4481        ZonedRound { round: self.round.smallest(unit) }
4482    }
4483
4484    /// Set the rounding mode.
4485    ///
4486    /// This defaults to [`RoundMode::HalfExpand`], which rounds away from
4487    /// zero. It matches the kind of rounding you might have been taught in
4488    /// school.
4489    ///
4490    /// # Example
4491    ///
4492    /// This shows how to always round zoned datetimes up towards positive
4493    /// infinity.
4494    ///
4495    /// ```
4496    /// use jiff::{civil::date, RoundMode, Unit, Zoned, ZonedRound};
4497    ///
4498    /// let zdt: Zoned = "2024-06-20 03:25:01[America/New_York]".parse()?;
4499    /// assert_eq!(
4500    ///     zdt.round(
4501    ///         ZonedRound::new()
4502    ///             .smallest(Unit::Minute)
4503    ///             .mode(RoundMode::Ceil),
4504    ///     )?,
4505    ///     date(2024, 6, 20).at(3, 26, 0, 0).in_tz("America/New_York")?,
4506    /// );
4507    ///
4508    /// # Ok::<(), Box<dyn std::error::Error>>(())
4509    /// ```
4510    #[inline]
4511    pub fn mode(self, mode: RoundMode) -> ZonedRound {
4512        ZonedRound { round: self.round.mode(mode) }
4513    }
4514
4515    /// Set the rounding increment for the smallest unit.
4516    ///
4517    /// The default value is `1`. Other values permit rounding the smallest
4518    /// unit to the nearest integer increment specified. For example, if the
4519    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
4520    /// `30` would result in rounding in increments of a half hour. That is,
4521    /// the only minute value that could result would be `0` or `30`.
4522    ///
4523    /// # Errors
4524    ///
4525    /// When the smallest unit is `Unit::Day`, then the rounding increment must
4526    /// be `1` or else [`Zoned::round`] will return an error.
4527    ///
4528    /// For other units, the rounding increment must divide evenly into the
4529    /// next highest unit above the smallest unit set. The rounding increment
4530    /// must also not be equal to the next highest unit. For example, if the
4531    /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
4532    /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
4533    /// Namely, any integer that divides evenly into `1,000` nanoseconds since
4534    /// there are `1,000` nanoseconds in the next highest unit (microseconds).
4535    ///
4536    /// # Example
4537    ///
4538    /// This example shows how to round a zoned datetime to the nearest 10
4539    /// minute increment.
4540    ///
4541    /// ```
4542    /// use jiff::{civil::date, RoundMode, Unit, Zoned, ZonedRound};
4543    ///
4544    /// let zdt: Zoned = "2024-06-20 03:24:59[America/New_York]".parse()?;
4545    /// assert_eq!(
4546    ///     zdt.round((Unit::Minute, 10))?,
4547    ///     date(2024, 6, 20).at(3, 20, 0, 0).in_tz("America/New_York")?,
4548    /// );
4549    ///
4550    /// # Ok::<(), Box<dyn std::error::Error>>(())
4551    /// ```
4552    #[inline]
4553    pub fn increment(self, increment: i64) -> ZonedRound {
4554        ZonedRound { round: self.round.increment(increment) }
4555    }
4556
4557    /// Does the actual rounding.
4558    ///
4559    /// Most of the work is farmed out to civil datetime rounding.
4560    pub(crate) fn round(&self, zdt: &Zoned) -> Result<Zoned, Error> {
4561        let start = zdt.datetime();
4562        if self.round.get_smallest() == Unit::Day {
4563            return self.round_days(zdt);
4564        }
4565        let end = self.round.round(start)?;
4566        // Like in the ZonedWith API, in order to avoid small changes to clock
4567        // time hitting a 1 hour disambiguation shift, we use offset conflict
4568        // resolution to do our best to "prefer" the offset we already have.
4569        let amb = OffsetConflict::PreferOffset.resolve(
4570            end,
4571            zdt.offset(),
4572            zdt.time_zone().clone(),
4573        )?;
4574        amb.compatible()
4575    }
4576
4577    /// Does rounding when the smallest unit is equal to days. We don't reuse
4578    /// civil datetime rounding for this since the length of a day for a zoned
4579    /// datetime might not be 24 hours.
4580    ///
4581    /// Ref: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round
4582    fn round_days(&self, zdt: &Zoned) -> Result<Zoned, Error> {
4583        debug_assert_eq!(self.round.get_smallest(), Unit::Day);
4584
4585        // Rounding by days requires an increment of 1. We just re-use the
4586        // civil datetime rounding checks, which has the same constraint
4587        // although it does check for other things that aren't relevant here.
4588        increment::for_datetime(Unit::Day, self.round.get_increment())?;
4589
4590        // FIXME: We should be doing this with a &TimeZone, but will need a
4591        // refactor so that we do zone-aware arithmetic using just a Timestamp
4592        // and a &TimeZone. Fixing just this should just be some minor annoying
4593        // work. The grander refactor is something like an `Unzoned` type, but
4594        // I'm not sure that's really worth it. ---AG
4595        let start = zdt.start_of_day().context(E::FailedStartOfDay)?;
4596        let end = start
4597            .checked_add(Span::new().days_ranged(C(1).rinto()))
4598            .context(E::FailedLengthOfDay)?;
4599        let span = start
4600            .timestamp()
4601            .until((Unit::Nanosecond, end.timestamp()))
4602            .context(E::FailedSpanNanoseconds)?;
4603        let nanos = span.get_nanoseconds_ranged();
4604        let day_length =
4605            ZonedDayNanoseconds::try_rfrom("nanoseconds-per-zoned-day", nanos)
4606                .context(E::FailedSpanNanoseconds)?;
4607        let progress = zdt.timestamp().as_nanosecond_ranged()
4608            - start.timestamp().as_nanosecond_ranged();
4609        let rounded = self.round.get_mode().round(progress, day_length);
4610        let nanos = start
4611            .timestamp()
4612            .as_nanosecond_ranged()
4613            .try_checked_add("timestamp-nanos", rounded)?;
4614        Ok(Timestamp::from_nanosecond_ranged(nanos)
4615            .to_zoned(zdt.time_zone().clone()))
4616    }
4617}
4618
4619impl Default for ZonedRound {
4620    #[inline]
4621    fn default() -> ZonedRound {
4622        ZonedRound::new()
4623    }
4624}
4625
4626impl From<Unit> for ZonedRound {
4627    #[inline]
4628    fn from(unit: Unit) -> ZonedRound {
4629        ZonedRound::default().smallest(unit)
4630    }
4631}
4632
4633impl From<(Unit, i64)> for ZonedRound {
4634    #[inline]
4635    fn from((unit, increment): (Unit, i64)) -> ZonedRound {
4636        ZonedRound::from(unit).increment(increment)
4637    }
4638}
4639
4640/// A builder for setting the fields on a [`Zoned`].
4641///
4642/// This builder is constructed via [`Zoned::with`].
4643///
4644/// # Example
4645///
4646/// The builder ensures one can chain together the individual components of a
4647/// zoned datetime without it failing at an intermediate step. For example,
4648/// if you had a date of `2024-10-31T00:00:00[America/New_York]` and wanted
4649/// to change both the day and the month, and each setting was validated
4650/// independent of the other, you would need to be careful to set the day first
4651/// and then the month. In some cases, you would need to set the month first
4652/// and then the day!
4653///
4654/// But with the builder, you can set values in any order:
4655///
4656/// ```
4657/// use jiff::civil::date;
4658///
4659/// let zdt1 = date(2024, 10, 31).at(0, 0, 0, 0).in_tz("America/New_York")?;
4660/// let zdt2 = zdt1.with().month(11).day(30).build()?;
4661/// assert_eq!(
4662///     zdt2,
4663///     date(2024, 11, 30).at(0, 0, 0, 0).in_tz("America/New_York")?,
4664/// );
4665///
4666/// let zdt1 = date(2024, 4, 30).at(0, 0, 0, 0).in_tz("America/New_York")?;
4667/// let zdt2 = zdt1.with().day(31).month(7).build()?;
4668/// assert_eq!(
4669///     zdt2,
4670///     date(2024, 7, 31).at(0, 0, 0, 0).in_tz("America/New_York")?,
4671/// );
4672///
4673/// # Ok::<(), Box<dyn std::error::Error>>(())
4674/// ```
4675#[derive(Clone, Debug)]
4676pub struct ZonedWith {
4677    original: Zoned,
4678    datetime_with: DateTimeWith,
4679    offset: Option<Offset>,
4680    disambiguation: Disambiguation,
4681    offset_conflict: OffsetConflict,
4682}
4683
4684impl ZonedWith {
4685    #[inline]
4686    fn new(original: Zoned) -> ZonedWith {
4687        let datetime_with = original.datetime().with();
4688        ZonedWith {
4689            original,
4690            datetime_with,
4691            offset: None,
4692            disambiguation: Disambiguation::default(),
4693            offset_conflict: OffsetConflict::PreferOffset,
4694        }
4695    }
4696
4697    /// Create a new `Zoned` from the fields set on this configuration.
4698    ///
4699    /// An error occurs when the fields combine to an invalid zoned datetime.
4700    ///
4701    /// For any fields not set on this configuration, the values are taken from
4702    /// the [`Zoned`] that originally created this configuration. When no
4703    /// values are set, this routine is guaranteed to succeed and will always
4704    /// return the original zoned datetime without modification.
4705    ///
4706    /// # Example
4707    ///
4708    /// This creates a zoned datetime corresponding to the last day in the year
4709    /// at noon:
4710    ///
4711    /// ```
4712    /// use jiff::civil::date;
4713    ///
4714    /// let zdt = date(2023, 1, 1).at(12, 0, 0, 0).in_tz("America/New_York")?;
4715    /// assert_eq!(
4716    ///     zdt.with().day_of_year_no_leap(365).build()?,
4717    ///     date(2023, 12, 31).at(12, 0, 0, 0).in_tz("America/New_York")?,
4718    /// );
4719    ///
4720    /// // It also works with leap years for the same input:
4721    /// let zdt = date(2024, 1, 1).at(12, 0, 0, 0).in_tz("America/New_York")?;
4722    /// assert_eq!(
4723    ///     zdt.with().day_of_year_no_leap(365).build()?,
4724    ///     date(2024, 12, 31).at(12, 0, 0, 0).in_tz("America/New_York")?,
4725    /// );
4726    ///
4727    /// # Ok::<(), Box<dyn std::error::Error>>(())
4728    /// ```
4729    ///
4730    /// # Example: error for invalid zoned datetime
4731    ///
4732    /// If the fields combine to form an invalid datetime, then an error is
4733    /// returned:
4734    ///
4735    /// ```
4736    /// use jiff::civil::date;
4737    ///
4738    /// let zdt = date(2024, 11, 30).at(15, 30, 0, 0).in_tz("America/New_York")?;
4739    /// assert!(zdt.with().day(31).build().is_err());
4740    ///
4741    /// let zdt = date(2024, 2, 29).at(15, 30, 0, 0).in_tz("America/New_York")?;
4742    /// assert!(zdt.with().year(2023).build().is_err());
4743    ///
4744    /// # Ok::<(), Box<dyn std::error::Error>>(())
4745    /// ```
4746    #[inline]
4747    pub fn build(self) -> Result<Zoned, Error> {
4748        let dt = self.datetime_with.build()?;
4749        let (_, _, offset, time_zone) = self.original.into_parts();
4750        let offset = self.offset.unwrap_or(offset);
4751        let ambiguous = self.offset_conflict.resolve(dt, offset, time_zone)?;
4752        ambiguous.disambiguate(self.disambiguation)
4753    }
4754
4755    /// Set the year, month and day fields via the `Date` given.
4756    ///
4757    /// This overrides any previous year, month or day settings.
4758    ///
4759    /// # Example
4760    ///
4761    /// This shows how to create a new zoned datetime with a different date:
4762    ///
4763    /// ```
4764    /// use jiff::civil::date;
4765    ///
4766    /// let zdt1 = date(2005, 11, 5).at(15, 30, 0, 0).in_tz("America/New_York")?;
4767    /// let zdt2 = zdt1.with().date(date(2017, 10, 31)).build()?;
4768    /// // The date changes but the time remains the same.
4769    /// assert_eq!(
4770    ///     zdt2,
4771    ///     date(2017, 10, 31).at(15, 30, 0, 0).in_tz("America/New_York")?,
4772    /// );
4773    ///
4774    /// # Ok::<(), Box<dyn std::error::Error>>(())
4775    /// ```
4776    #[inline]
4777    pub fn date(self, date: Date) -> ZonedWith {
4778        ZonedWith { datetime_with: self.datetime_with.date(date), ..self }
4779    }
4780
4781    /// Set the hour, minute, second, millisecond, microsecond and nanosecond
4782    /// fields via the `Time` given.
4783    ///
4784    /// This overrides any previous hour, minute, second, millisecond,
4785    /// microsecond, nanosecond or subsecond nanosecond settings.
4786    ///
4787    /// # Example
4788    ///
4789    /// This shows how to create a new zoned datetime with a different time:
4790    ///
4791    /// ```
4792    /// use jiff::civil::{date, time};
4793    ///
4794    /// let zdt1 = date(2005, 11, 5).at(15, 30, 0, 0).in_tz("America/New_York")?;
4795    /// let zdt2 = zdt1.with().time(time(23, 59, 59, 123_456_789)).build()?;
4796    /// // The time changes but the date remains the same.
4797    /// assert_eq!(
4798    ///     zdt2,
4799    ///     date(2005, 11, 5)
4800    ///         .at(23, 59, 59, 123_456_789)
4801    ///         .in_tz("America/New_York")?,
4802    /// );
4803    ///
4804    /// # Ok::<(), Box<dyn std::error::Error>>(())
4805    /// ```
4806    #[inline]
4807    pub fn time(self, time: Time) -> ZonedWith {
4808        ZonedWith { datetime_with: self.datetime_with.time(time), ..self }
4809    }
4810
4811    /// Set the year field on a [`Zoned`].
4812    ///
4813    /// One can access this value via [`Zoned::year`].
4814    ///
4815    /// This overrides any previous year settings.
4816    ///
4817    /// # Errors
4818    ///
4819    /// This returns an error when [`ZonedWith::build`] is called if the
4820    /// given year is outside the range `-9999..=9999`. This can also return an
4821    /// error if the resulting date is otherwise invalid.
4822    ///
4823    /// # Example
4824    ///
4825    /// This shows how to create a new zoned datetime with a different year:
4826    ///
4827    /// ```
4828    /// use jiff::civil::date;
4829    ///
4830    /// let zdt1 = date(2005, 11, 5).at(15, 30, 0, 0).in_tz("America/New_York")?;
4831    /// assert_eq!(zdt1.year(), 2005);
4832    /// let zdt2 = zdt1.with().year(2007).build()?;
4833    /// assert_eq!(zdt2.year(), 2007);
4834    ///
4835    /// # Ok::<(), Box<dyn std::error::Error>>(())
4836    /// ```
4837    ///
4838    /// # Example: only changing the year can fail
4839    ///
4840    /// For example, while `2024-02-29T01:30:00[America/New_York]` is valid,
4841    /// `2023-02-29T01:30:00[America/New_York]` is not:
4842    ///
4843    /// ```
4844    /// use jiff::civil::date;
4845    ///
4846    /// let zdt = date(2024, 2, 29).at(1, 30, 0, 0).in_tz("America/New_York")?;
4847    /// assert!(zdt.with().year(2023).build().is_err());
4848    ///
4849    /// # Ok::<(), Box<dyn std::error::Error>>(())
4850    /// ```
4851    #[inline]
4852    pub fn year(self, year: i16) -> ZonedWith {
4853        ZonedWith { datetime_with: self.datetime_with.year(year), ..self }
4854    }
4855
4856    /// Set the year of a zoned datetime via its era and its non-negative
4857    /// numeric component.
4858    ///
4859    /// One can access this value via [`Zoned::era_year`].
4860    ///
4861    /// # Errors
4862    ///
4863    /// This returns an error when [`ZonedWith::build`] is called if the
4864    /// year is outside the range for the era specified. For [`Era::BCE`], the
4865    /// range is `1..=10000`. For [`Era::CE`], the range is `1..=9999`.
4866    ///
4867    /// # Example
4868    ///
4869    /// This shows that `CE` years are equivalent to the years used by this
4870    /// crate:
4871    ///
4872    /// ```
4873    /// use jiff::civil::{Era, date};
4874    ///
4875    /// let zdt1 = date(2005, 11, 5).at(8, 0, 0, 0).in_tz("America/New_York")?;
4876    /// assert_eq!(zdt1.year(), 2005);
4877    /// let zdt2 = zdt1.with().era_year(2007, Era::CE).build()?;
4878    /// assert_eq!(zdt2.year(), 2007);
4879    ///
4880    /// // CE years are always positive and can be at most 9999:
4881    /// assert!(zdt1.with().era_year(-5, Era::CE).build().is_err());
4882    /// assert!(zdt1.with().era_year(10_000, Era::CE).build().is_err());
4883    ///
4884    /// # Ok::<(), Box<dyn std::error::Error>>(())
4885    /// ```
4886    ///
4887    /// But `BCE` years always correspond to years less than or equal to `0`
4888    /// in this crate:
4889    ///
4890    /// ```
4891    /// use jiff::civil::{Era, date};
4892    ///
4893    /// let zdt1 = date(-27, 7, 1).at(8, 22, 30, 0).in_tz("America/New_York")?;
4894    /// assert_eq!(zdt1.year(), -27);
4895    /// assert_eq!(zdt1.era_year(), (28, Era::BCE));
4896    ///
4897    /// let zdt2 = zdt1.with().era_year(509, Era::BCE).build()?;
4898    /// assert_eq!(zdt2.year(), -508);
4899    /// assert_eq!(zdt2.era_year(), (509, Era::BCE));
4900    ///
4901    /// let zdt2 = zdt1.with().era_year(10_000, Era::BCE).build()?;
4902    /// assert_eq!(zdt2.year(), -9_999);
4903    /// assert_eq!(zdt2.era_year(), (10_000, Era::BCE));
4904    ///
4905    /// // BCE years are always positive and can be at most 10000:
4906    /// assert!(zdt1.with().era_year(-5, Era::BCE).build().is_err());
4907    /// assert!(zdt1.with().era_year(10_001, Era::BCE).build().is_err());
4908    ///
4909    /// # Ok::<(), Box<dyn std::error::Error>>(())
4910    /// ```
4911    ///
4912    /// # Example: overrides `ZonedWith::year`
4913    ///
4914    /// Setting this option will override any previous `ZonedWith::year`
4915    /// option:
4916    ///
4917    /// ```
4918    /// use jiff::civil::{Era, date};
4919    ///
4920    /// let zdt1 = date(2024, 7, 2).at(10, 27, 10, 123).in_tz("America/New_York")?;
4921    /// let zdt2 = zdt1.with().year(2000).era_year(1900, Era::CE).build()?;
4922    /// assert_eq!(
4923    ///     zdt2,
4924    ///     date(1900, 7, 2).at(10, 27, 10, 123).in_tz("America/New_York")?,
4925    /// );
4926    ///
4927    /// # Ok::<(), Box<dyn std::error::Error>>(())
4928    /// ```
4929    ///
4930    /// Similarly, `ZonedWith::year` will override any previous call to
4931    /// `ZonedWith::era_year`:
4932    ///
4933    /// ```
4934    /// use jiff::civil::{Era, date};
4935    ///
4936    /// let zdt1 = date(2024, 7, 2).at(19, 0, 1, 1).in_tz("America/New_York")?;
4937    /// let zdt2 = zdt1.with().era_year(1900, Era::CE).year(2000).build()?;
4938    /// assert_eq!(
4939    ///     zdt2,
4940    ///     date(2000, 7, 2).at(19, 0, 1, 1).in_tz("America/New_York")?,
4941    /// );
4942    ///
4943    /// # Ok::<(), Box<dyn std::error::Error>>(())
4944    /// ```
4945    #[inline]
4946    pub fn era_year(self, year: i16, era: Era) -> ZonedWith {
4947        ZonedWith {
4948            datetime_with: self.datetime_with.era_year(year, era),
4949            ..self
4950        }
4951    }
4952
4953    /// Set the month field on a [`Zoned`].
4954    ///
4955    /// One can access this value via [`Zoned::month`].
4956    ///
4957    /// This overrides any previous month settings.
4958    ///
4959    /// # Errors
4960    ///
4961    /// This returns an error when [`ZonedWith::build`] is called if the
4962    /// given month is outside the range `1..=12`. This can also return an
4963    /// error if the resulting date is otherwise invalid.
4964    ///
4965    /// # Example
4966    ///
4967    /// This shows how to create a new zoned datetime with a different month:
4968    ///
4969    /// ```
4970    /// use jiff::civil::date;
4971    ///
4972    /// let zdt1 = date(2005, 11, 5)
4973    ///     .at(18, 3, 59, 123_456_789)
4974    ///     .in_tz("America/New_York")?;
4975    /// assert_eq!(zdt1.month(), 11);
4976    ///
4977    /// let zdt2 = zdt1.with().month(6).build()?;
4978    /// assert_eq!(zdt2.month(), 6);
4979    ///
4980    /// # Ok::<(), Box<dyn std::error::Error>>(())
4981    /// ```
4982    ///
4983    /// # Example: only changing the month can fail
4984    ///
4985    /// For example, while `2024-10-31T00:00:00[America/New_York]` is valid,
4986    /// `2024-11-31T00:00:00[America/New_York]` is not:
4987    ///
4988    /// ```
4989    /// use jiff::civil::date;
4990    ///
4991    /// let zdt = date(2024, 10, 31).at(0, 0, 0, 0).in_tz("America/New_York")?;
4992    /// assert!(zdt.with().month(11).build().is_err());
4993    ///
4994    /// # Ok::<(), Box<dyn std::error::Error>>(())
4995    /// ```
4996    #[inline]
4997    pub fn month(self, month: i8) -> ZonedWith {
4998        ZonedWith { datetime_with: self.datetime_with.month(month), ..self }
4999    }
5000
5001    /// Set the day field on a [`Zoned`].
5002    ///
5003    /// One can access this value via [`Zoned::day`].
5004    ///
5005    /// This overrides any previous day settings.
5006    ///
5007    /// # Errors
5008    ///
5009    /// This returns an error when [`ZonedWith::build`] is called if the
5010    /// given given day is outside of allowable days for the corresponding year
5011    /// and month fields.
5012    ///
5013    /// # Example
5014    ///
5015    /// This shows some examples of setting the day, including a leap day:
5016    ///
5017    /// ```
5018    /// use jiff::civil::date;
5019    ///
5020    /// let zdt1 = date(2024, 2, 5).at(21, 59, 1, 999).in_tz("America/New_York")?;
5021    /// assert_eq!(zdt1.day(), 5);
5022    /// let zdt2 = zdt1.with().day(10).build()?;
5023    /// assert_eq!(zdt2.day(), 10);
5024    /// let zdt3 = zdt1.with().day(29).build()?;
5025    /// assert_eq!(zdt3.day(), 29);
5026    ///
5027    /// # Ok::<(), Box<dyn std::error::Error>>(())
5028    /// ```
5029    ///
5030    /// # Example: changing only the day can fail
5031    ///
5032    /// This shows some examples that will fail:
5033    ///
5034    /// ```
5035    /// use jiff::civil::date;
5036    ///
5037    /// let zdt1 = date(2023, 2, 5)
5038    ///     .at(22, 58, 58, 9_999)
5039    ///     .in_tz("America/New_York")?;
5040    /// // 2023 is not a leap year
5041    /// assert!(zdt1.with().day(29).build().is_err());
5042    ///
5043    /// // September has 30 days, not 31.
5044    /// let zdt1 = date(2023, 9, 5).in_tz("America/New_York")?;
5045    /// assert!(zdt1.with().day(31).build().is_err());
5046    ///
5047    /// # Ok::<(), Box<dyn std::error::Error>>(())
5048    /// ```
5049    #[inline]
5050    pub fn day(self, day: i8) -> ZonedWith {
5051        ZonedWith { datetime_with: self.datetime_with.day(day), ..self }
5052    }
5053
5054    /// Set the day field on a [`Zoned`] via the ordinal number of a day
5055    /// within a year.
5056    ///
5057    /// When used, any settings for month are ignored since the month is
5058    /// determined by the day of the year.
5059    ///
5060    /// The valid values for `day` are `1..=366`. Note though that `366` is
5061    /// only valid for leap years.
5062    ///
5063    /// This overrides any previous day settings.
5064    ///
5065    /// # Errors
5066    ///
5067    /// This returns an error when [`ZonedWith::build`] is called if the
5068    /// given day is outside the allowed range of `1..=366`, or when a value of
5069    /// `366` is given for a non-leap year.
5070    ///
5071    /// # Example
5072    ///
5073    /// This demonstrates that if a year is a leap year, then `60` corresponds
5074    /// to February 29:
5075    ///
5076    /// ```
5077    /// use jiff::civil::date;
5078    ///
5079    /// let zdt = date(2024, 1, 1)
5080    ///     .at(23, 59, 59, 999_999_999)
5081    ///     .in_tz("America/New_York")?;
5082    /// assert_eq!(
5083    ///     zdt.with().day_of_year(60).build()?,
5084    ///     date(2024, 2, 29)
5085    ///         .at(23, 59, 59, 999_999_999)
5086    ///         .in_tz("America/New_York")?,
5087    /// );
5088    ///
5089    /// # Ok::<(), Box<dyn std::error::Error>>(())
5090    /// ```
5091    ///
5092    /// But for non-leap years, day 60 is March 1:
5093    ///
5094    /// ```
5095    /// use jiff::civil::date;
5096    ///
5097    /// let zdt = date(2023, 1, 1)
5098    ///     .at(23, 59, 59, 999_999_999)
5099    ///     .in_tz("America/New_York")?;
5100    /// assert_eq!(
5101    ///     zdt.with().day_of_year(60).build()?,
5102    ///     date(2023, 3, 1)
5103    ///         .at(23, 59, 59, 999_999_999)
5104    ///         .in_tz("America/New_York")?,
5105    /// );
5106    ///
5107    /// # Ok::<(), Box<dyn std::error::Error>>(())
5108    /// ```
5109    ///
5110    /// And using `366` for a non-leap year will result in an error, since
5111    /// non-leap years only have 365 days:
5112    ///
5113    /// ```
5114    /// use jiff::civil::date;
5115    ///
5116    /// let zdt = date(2023, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York")?;
5117    /// assert!(zdt.with().day_of_year(366).build().is_err());
5118    /// // The maximal year is not a leap year, so it returns an error too.
5119    /// let zdt = date(9999, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York")?;
5120    /// assert!(zdt.with().day_of_year(366).build().is_err());
5121    ///
5122    /// # Ok::<(), Box<dyn std::error::Error>>(())
5123    /// ```
5124    #[inline]
5125    pub fn day_of_year(self, day: i16) -> ZonedWith {
5126        ZonedWith {
5127            datetime_with: self.datetime_with.day_of_year(day),
5128            ..self
5129        }
5130    }
5131
5132    /// Set the day field on a [`Zoned`] via the ordinal number of a day
5133    /// within a year, but ignoring leap years.
5134    ///
5135    /// When used, any settings for month are ignored since the month is
5136    /// determined by the day of the year.
5137    ///
5138    /// The valid values for `day` are `1..=365`. The value `365` always
5139    /// corresponds to the last day of the year, even for leap years. It is
5140    /// impossible for this routine to return a zoned datetime corresponding to
5141    /// February 29. (Unless there is a relevant time zone transition that
5142    /// provokes disambiguation that shifts the datetime into February 29.)
5143    ///
5144    /// This overrides any previous day settings.
5145    ///
5146    /// # Errors
5147    ///
5148    /// This returns an error when [`ZonedWith::build`] is called if the
5149    /// given day is outside the allowed range of `1..=365`.
5150    ///
5151    /// # Example
5152    ///
5153    /// This demonstrates that `60` corresponds to March 1, regardless of
5154    /// whether the year is a leap year or not:
5155    ///
5156    /// ```
5157    /// use jiff::civil::date;
5158    ///
5159    /// let zdt = date(2023, 1, 1)
5160    ///     .at(23, 59, 59, 999_999_999)
5161    ///     .in_tz("America/New_York")?;
5162    /// assert_eq!(
5163    ///     zdt.with().day_of_year_no_leap(60).build()?,
5164    ///     date(2023, 3, 1)
5165    ///         .at(23, 59, 59, 999_999_999)
5166    ///         .in_tz("America/New_York")?,
5167    /// );
5168    ///
5169    /// let zdt = date(2024, 1, 1)
5170    ///     .at(23, 59, 59, 999_999_999)
5171    ///     .in_tz("America/New_York")?;
5172    /// assert_eq!(
5173    ///     zdt.with().day_of_year_no_leap(60).build()?,
5174    ///     date(2024, 3, 1)
5175    ///         .at(23, 59, 59, 999_999_999)
5176    ///         .in_tz("America/New_York")?,
5177    /// );
5178    ///
5179    /// # Ok::<(), Box<dyn std::error::Error>>(())
5180    /// ```
5181    ///
5182    /// And using `365` for any year will always yield the last day of the
5183    /// year:
5184    ///
5185    /// ```
5186    /// use jiff::civil::date;
5187    ///
5188    /// let zdt = date(2023, 1, 1)
5189    ///     .at(23, 59, 59, 999_999_999)
5190    ///     .in_tz("America/New_York")?;
5191    /// assert_eq!(
5192    ///     zdt.with().day_of_year_no_leap(365).build()?,
5193    ///     zdt.last_of_year()?,
5194    /// );
5195    ///
5196    /// let zdt = date(2024, 1, 1)
5197    ///     .at(23, 59, 59, 999_999_999)
5198    ///     .in_tz("America/New_York")?;
5199    /// assert_eq!(
5200    ///     zdt.with().day_of_year_no_leap(365).build()?,
5201    ///     zdt.last_of_year()?,
5202    /// );
5203    ///
5204    /// // Careful at the boundaries. The last day of the year isn't
5205    /// // representable with all time zones. For example:
5206    /// let zdt = date(9999, 1, 1)
5207    ///     .at(23, 59, 59, 999_999_999)
5208    ///     .in_tz("America/New_York")?;
5209    /// assert!(zdt.with().day_of_year_no_leap(365).build().is_err());
5210    /// // But with other time zones, it works okay:
5211    /// let zdt = date(9999, 1, 1)
5212    ///     .at(23, 59, 59, 999_999_999)
5213    ///     .to_zoned(jiff::tz::TimeZone::fixed(jiff::tz::Offset::MAX))?;
5214    /// assert_eq!(
5215    ///     zdt.with().day_of_year_no_leap(365).build()?,
5216    ///     zdt.last_of_year()?,
5217    /// );
5218    ///
5219    /// # Ok::<(), Box<dyn std::error::Error>>(())
5220    /// ```
5221    ///
5222    /// A value of `366` is out of bounds, even for leap years:
5223    ///
5224    /// ```
5225    /// use jiff::civil::date;
5226    ///
5227    /// let zdt = date(2024, 1, 1).at(5, 30, 0, 0).in_tz("America/New_York")?;
5228    /// assert!(zdt.with().day_of_year_no_leap(366).build().is_err());
5229    ///
5230    /// # Ok::<(), Box<dyn std::error::Error>>(())
5231    /// ```
5232    #[inline]
5233    pub fn day_of_year_no_leap(self, day: i16) -> ZonedWith {
5234        ZonedWith {
5235            datetime_with: self.datetime_with.day_of_year_no_leap(day),
5236            ..self
5237        }
5238    }
5239
5240    /// Set the hour field on a [`Zoned`].
5241    ///
5242    /// One can access this value via [`Zoned::hour`].
5243    ///
5244    /// This overrides any previous hour settings.
5245    ///
5246    /// # Errors
5247    ///
5248    /// This returns an error when [`ZonedWith::build`] is called if the
5249    /// given hour is outside the range `0..=23`.
5250    ///
5251    /// # Example
5252    ///
5253    /// ```
5254    /// use jiff::civil::time;
5255    ///
5256    /// let zdt1 = time(15, 21, 59, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5257    /// assert_eq!(zdt1.hour(), 15);
5258    /// let zdt2 = zdt1.with().hour(3).build()?;
5259    /// assert_eq!(zdt2.hour(), 3);
5260    ///
5261    /// # Ok::<(), Box<dyn std::error::Error>>(())
5262    /// ```
5263    #[inline]
5264    pub fn hour(self, hour: i8) -> ZonedWith {
5265        ZonedWith { datetime_with: self.datetime_with.hour(hour), ..self }
5266    }
5267
5268    /// Set the minute field on a [`Zoned`].
5269    ///
5270    /// One can access this value via [`Zoned::minute`].
5271    ///
5272    /// This overrides any previous minute settings.
5273    ///
5274    /// # Errors
5275    ///
5276    /// This returns an error when [`ZonedWith::build`] is called if the
5277    /// given minute is outside the range `0..=59`.
5278    ///
5279    /// # Example
5280    ///
5281    /// ```
5282    /// use jiff::civil::time;
5283    ///
5284    /// let zdt1 = time(15, 21, 59, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5285    /// assert_eq!(zdt1.minute(), 21);
5286    /// let zdt2 = zdt1.with().minute(3).build()?;
5287    /// assert_eq!(zdt2.minute(), 3);
5288    ///
5289    /// # Ok::<(), Box<dyn std::error::Error>>(())
5290    /// ```
5291    #[inline]
5292    pub fn minute(self, minute: i8) -> ZonedWith {
5293        ZonedWith { datetime_with: self.datetime_with.minute(minute), ..self }
5294    }
5295
5296    /// Set the second field on a [`Zoned`].
5297    ///
5298    /// One can access this value via [`Zoned::second`].
5299    ///
5300    /// This overrides any previous second settings.
5301    ///
5302    /// # Errors
5303    ///
5304    /// This returns an error when [`ZonedWith::build`] is called if the
5305    /// given second is outside the range `0..=59`.
5306    ///
5307    /// # Example
5308    ///
5309    /// ```
5310    /// use jiff::civil::time;
5311    ///
5312    /// let zdt1 = time(15, 21, 59, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5313    /// assert_eq!(zdt1.second(), 59);
5314    /// let zdt2 = zdt1.with().second(3).build()?;
5315    /// assert_eq!(zdt2.second(), 3);
5316    ///
5317    /// # Ok::<(), Box<dyn std::error::Error>>(())
5318    /// ```
5319    #[inline]
5320    pub fn second(self, second: i8) -> ZonedWith {
5321        ZonedWith { datetime_with: self.datetime_with.second(second), ..self }
5322    }
5323
5324    /// Set the millisecond field on a [`Zoned`].
5325    ///
5326    /// One can access this value via [`Zoned::millisecond`].
5327    ///
5328    /// This overrides any previous millisecond settings.
5329    ///
5330    /// Note that this only sets the millisecond component. It does
5331    /// not change the microsecond or nanosecond components. To set
5332    /// the fractional second component to nanosecond precision, use
5333    /// [`ZonedWith::subsec_nanosecond`].
5334    ///
5335    /// # Errors
5336    ///
5337    /// This returns an error when [`ZonedWith::build`] is called if the
5338    /// given millisecond is outside the range `0..=999`, or if both this and
5339    /// [`ZonedWith::subsec_nanosecond`] are set.
5340    ///
5341    /// # Example
5342    ///
5343    /// This shows the relationship between [`Zoned::millisecond`] and
5344    /// [`Zoned::subsec_nanosecond`]:
5345    ///
5346    /// ```
5347    /// use jiff::civil::time;
5348    ///
5349    /// let zdt1 = time(15, 21, 35, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5350    /// let zdt2 = zdt1.with().millisecond(123).build()?;
5351    /// assert_eq!(zdt2.subsec_nanosecond(), 123_000_000);
5352    ///
5353    /// # Ok::<(), Box<dyn std::error::Error>>(())
5354    /// ```
5355    #[inline]
5356    pub fn millisecond(self, millisecond: i16) -> ZonedWith {
5357        ZonedWith {
5358            datetime_with: self.datetime_with.millisecond(millisecond),
5359            ..self
5360        }
5361    }
5362
5363    /// Set the microsecond field on a [`Zoned`].
5364    ///
5365    /// One can access this value via [`Zoned::microsecond`].
5366    ///
5367    /// This overrides any previous microsecond settings.
5368    ///
5369    /// Note that this only sets the microsecond component. It does
5370    /// not change the millisecond or nanosecond components. To set
5371    /// the fractional second component to nanosecond precision, use
5372    /// [`ZonedWith::subsec_nanosecond`].
5373    ///
5374    /// # Errors
5375    ///
5376    /// This returns an error when [`ZonedWith::build`] is called if the
5377    /// given microsecond is outside the range `0..=999`, or if both this and
5378    /// [`ZonedWith::subsec_nanosecond`] are set.
5379    ///
5380    /// # Example
5381    ///
5382    /// This shows the relationship between [`Zoned::microsecond`] and
5383    /// [`Zoned::subsec_nanosecond`]:
5384    ///
5385    /// ```
5386    /// use jiff::civil::time;
5387    ///
5388    /// let zdt1 = time(15, 21, 35, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5389    /// let zdt2 = zdt1.with().microsecond(123).build()?;
5390    /// assert_eq!(zdt2.subsec_nanosecond(), 123_000);
5391    ///
5392    /// # Ok::<(), Box<dyn std::error::Error>>(())
5393    /// ```
5394    #[inline]
5395    pub fn microsecond(self, microsecond: i16) -> ZonedWith {
5396        ZonedWith {
5397            datetime_with: self.datetime_with.microsecond(microsecond),
5398            ..self
5399        }
5400    }
5401
5402    /// Set the nanosecond field on a [`Zoned`].
5403    ///
5404    /// One can access this value via [`Zoned::nanosecond`].
5405    ///
5406    /// This overrides any previous nanosecond settings.
5407    ///
5408    /// Note that this only sets the nanosecond component. It does
5409    /// not change the millisecond or microsecond components. To set
5410    /// the fractional second component to nanosecond precision, use
5411    /// [`ZonedWith::subsec_nanosecond`].
5412    ///
5413    /// # Errors
5414    ///
5415    /// This returns an error when [`ZonedWith::build`] is called if the
5416    /// given nanosecond is outside the range `0..=999`, or if both this and
5417    /// [`ZonedWith::subsec_nanosecond`] are set.
5418    ///
5419    /// # Example
5420    ///
5421    /// This shows the relationship between [`Zoned::nanosecond`] and
5422    /// [`Zoned::subsec_nanosecond`]:
5423    ///
5424    /// ```
5425    /// use jiff::civil::time;
5426    ///
5427    /// let zdt1 = time(15, 21, 35, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5428    /// let zdt2 = zdt1.with().nanosecond(123).build()?;
5429    /// assert_eq!(zdt2.subsec_nanosecond(), 123);
5430    ///
5431    /// # Ok::<(), Box<dyn std::error::Error>>(())
5432    /// ```
5433    #[inline]
5434    pub fn nanosecond(self, nanosecond: i16) -> ZonedWith {
5435        ZonedWith {
5436            datetime_with: self.datetime_with.nanosecond(nanosecond),
5437            ..self
5438        }
5439    }
5440
5441    /// Set the subsecond nanosecond field on a [`Zoned`].
5442    ///
5443    /// If you want to access this value on `Zoned`, then use
5444    /// [`Zoned::subsec_nanosecond`].
5445    ///
5446    /// This overrides any previous subsecond nanosecond settings.
5447    ///
5448    /// Note that this sets the entire fractional second component to
5449    /// nanosecond precision, and overrides any individual millisecond,
5450    /// microsecond or nanosecond settings. To set individual components,
5451    /// use [`ZonedWith::millisecond`], [`ZonedWith::microsecond`] or
5452    /// [`ZonedWith::nanosecond`].
5453    ///
5454    /// # Errors
5455    ///
5456    /// This returns an error when [`ZonedWith::build`] is called if the
5457    /// given subsecond nanosecond is outside the range `0..=999,999,999`,
5458    /// or if both this and one of [`ZonedWith::millisecond`],
5459    /// [`ZonedWith::microsecond`] or [`ZonedWith::nanosecond`] are set.
5460    ///
5461    /// # Example
5462    ///
5463    /// This shows the relationship between constructing a `Zoned` value
5464    /// with subsecond nanoseconds and its individual subsecond fields:
5465    ///
5466    /// ```
5467    /// use jiff::civil::time;
5468    ///
5469    /// let zdt1 = time(15, 21, 35, 0).on(2010, 6, 1).in_tz("America/New_York")?;
5470    /// let zdt2 = zdt1.with().subsec_nanosecond(123_456_789).build()?;
5471    /// assert_eq!(zdt2.millisecond(), 123);
5472    /// assert_eq!(zdt2.microsecond(), 456);
5473    /// assert_eq!(zdt2.nanosecond(), 789);
5474    ///
5475    /// # Ok::<(), Box<dyn std::error::Error>>(())
5476    /// ```
5477    #[inline]
5478    pub fn subsec_nanosecond(self, subsec_nanosecond: i32) -> ZonedWith {
5479        ZonedWith {
5480            datetime_with: self
5481                .datetime_with
5482                .subsec_nanosecond(subsec_nanosecond),
5483            ..self
5484        }
5485    }
5486
5487    /// Set the offset to use in the new zoned datetime.
5488    ///
5489    /// This can be used in some cases to explicitly disambiguate a datetime
5490    /// that could correspond to multiple instants in time.
5491    ///
5492    /// How the offset is used to construct a new zoned datetime
5493    /// depends on the offset conflict resolution strategy
5494    /// set via [`ZonedWith::offset_conflict`]. The default is
5495    /// [`OffsetConflict::PreferOffset`], which will always try to use the
5496    /// offset to resolve a datetime to an instant, unless the offset is
5497    /// incorrect for this zoned datetime's time zone. In which case, only the
5498    /// time zone is used to select the correct offset (which may involve using
5499    /// the disambiguation strategy set via [`ZonedWith::disambiguation`]).
5500    ///
5501    /// # Example
5502    ///
5503    /// This example shows parsing the first time the 1 o'clock hour appeared
5504    /// on a clock in New York on 2024-11-03, and then changing only the
5505    /// offset to flip it to the second time 1 o'clock appeared on the clock:
5506    ///
5507    /// ```
5508    /// use jiff::{tz, Zoned};
5509    ///
5510    /// let zdt1: Zoned = "2024-11-03 01:30-04[America/New_York]".parse()?;
5511    /// let zdt2 = zdt1.with().offset(tz::offset(-5)).build()?;
5512    /// assert_eq!(
5513    ///     zdt2.to_string(),
5514    ///     // Everything stays the same, except for the offset.
5515    ///     "2024-11-03T01:30:00-05:00[America/New_York]",
5516    /// );
5517    ///
5518    /// // If we use an invalid offset for the America/New_York time zone,
5519    /// // then it will be ignored and the disambiguation strategy set will
5520    /// // be used.
5521    /// let zdt3 = zdt1.with().offset(tz::offset(-12)).build()?;
5522    /// assert_eq!(
5523    ///     zdt3.to_string(),
5524    ///     // The default disambiguation is Compatible.
5525    ///     "2024-11-03T01:30:00-04:00[America/New_York]",
5526    /// );
5527    /// // But we could change the disambiguation strategy to reject such
5528    /// // cases!
5529    /// let result = zdt1
5530    ///     .with()
5531    ///     .offset(tz::offset(-12))
5532    ///     .disambiguation(tz::Disambiguation::Reject)
5533    ///     .build();
5534    /// assert!(result.is_err());
5535    ///
5536    /// # Ok::<(), Box<dyn std::error::Error>>(())
5537    /// ```
5538    #[inline]
5539    pub fn offset(self, offset: Offset) -> ZonedWith {
5540        ZonedWith { offset: Some(offset), ..self }
5541    }
5542
5543    /// Set the conflict resolution strategy for when an offset is inconsistent
5544    /// with the time zone.
5545    ///
5546    /// See the documentation on [`OffsetConflict`] for more details about the
5547    /// different strategies one can choose.
5548    ///
5549    /// Unlike parsing (where the default is `OffsetConflict::Reject`), the
5550    /// default for `ZonedWith` is [`OffsetConflict::PreferOffset`], which
5551    /// avoids daylight saving time disambiguation causing unexpected 1-hour
5552    /// shifts after small changes to clock time.
5553    ///
5554    /// # Example
5555    ///
5556    /// ```
5557    /// use jiff::Zoned;
5558    ///
5559    /// // Set to the "second" time 1:30 is on the clocks in New York on
5560    /// // 2024-11-03. The offset in the datetime string makes this
5561    /// // unambiguous.
5562    /// let zdt1 = "2024-11-03T01:30-05[America/New_York]".parse::<Zoned>()?;
5563    /// // Now we change the minute field:
5564    /// let zdt2 = zdt1.with().minute(34).build()?;
5565    /// assert_eq!(
5566    ///     zdt2.to_string(),
5567    ///     // Without taking the offset of the `Zoned` value into account,
5568    ///     // this would have defaulted to using the "compatible"
5569    ///     // disambiguation strategy, which would have selected the earlier
5570    ///     // offset of -04 instead of sticking with the later offset of -05.
5571    ///     "2024-11-03T01:34:00-05:00[America/New_York]",
5572    /// );
5573    ///
5574    /// // But note that if we change the clock time such that the previous
5575    /// // offset is no longer valid (by moving back before DST ended), then
5576    /// // the default strategy will automatically adapt and change the offset.
5577    /// let zdt2 = zdt1.with().hour(0).build()?;
5578    /// assert_eq!(
5579    ///     zdt2.to_string(),
5580    ///     "2024-11-03T00:30:00-04:00[America/New_York]",
5581    /// );
5582    ///
5583    /// # Ok::<(), Box<dyn std::error::Error>>(())
5584    /// ```
5585    #[inline]
5586    pub fn offset_conflict(self, strategy: OffsetConflict) -> ZonedWith {
5587        ZonedWith { offset_conflict: strategy, ..self }
5588    }
5589
5590    /// Set the disambiguation strategy for when a zoned datetime falls into a
5591    /// time zone transition "fold" or "gap."
5592    ///
5593    /// The most common manifestation of such time zone transitions is daylight
5594    /// saving time. In most cases, the transition into daylight saving time
5595    /// moves the civil time ("the time you see on the clock") ahead one hour.
5596    /// This is called a "gap" because an hour on the clock is skipped. While
5597    /// the transition out of daylight saving time moves the civil time back
5598    /// one hour. This is called a "fold" because an hour on the clock is
5599    /// repeated.
5600    ///
5601    /// In the case of a gap, an ambiguous datetime manifests as a time that
5602    /// never appears on a clock. (For example, `02:30` on `2024-03-10` in New
5603    /// York.) In the case of a fold, an ambiguous datetime manifests as a
5604    /// time that repeats itself. (For example, `01:30` on `2024-11-03` in New
5605    /// York.) So when a fold occurs, you don't know whether it's the "first"
5606    /// occurrence of that time or the "second."
5607    ///
5608    /// Time zone transitions are not just limited to daylight saving time,
5609    /// although those are the most common. In other cases, a transition occurs
5610    /// because of a change in the offset of the time zone itself. (See the
5611    /// examples below.)
5612    ///
5613    /// # Example: time zone offset change
5614    ///
5615    /// In this example, we explore a time zone offset change in Hawaii that
5616    /// occurred on `1947-06-08`. Namely, Hawaii went from a `-10:30` offset
5617    /// to a `-10:00` offset at `02:00`. This results in a 30 minute gap in
5618    /// civil time.
5619    ///
5620    /// ```
5621    /// use jiff::{civil::date, tz, ToSpan, Zoned};
5622    ///
5623    /// // This datetime is unambiguous...
5624    /// let zdt1 = "1943-06-02T02:05[Pacific/Honolulu]".parse::<Zoned>()?;
5625    /// // but... 02:05 didn't exist on clocks on 1947-06-08.
5626    /// let zdt2 = zdt1
5627    ///     .with()
5628    ///     .disambiguation(tz::Disambiguation::Later)
5629    ///     .year(1947)
5630    ///     .day(8)
5631    ///     .build()?;
5632    /// // Our parser is configured to select the later time, so we jump to
5633    /// // 02:35. But if we used `Disambiguation::Earlier`, then we'd get
5634    /// // 01:35.
5635    /// assert_eq!(zdt2.datetime(), date(1947, 6, 8).at(2, 35, 0, 0));
5636    /// assert_eq!(zdt2.offset(), tz::offset(-10));
5637    ///
5638    /// // If we subtract 10 minutes from 02:35, notice that we (correctly)
5639    /// // jump to 01:55 *and* our offset is corrected to -10:30.
5640    /// let zdt3 = zdt2.checked_sub(10.minutes())?;
5641    /// assert_eq!(zdt3.datetime(), date(1947, 6, 8).at(1, 55, 0, 0));
5642    /// assert_eq!(zdt3.offset(), tz::offset(-10).saturating_sub(30.minutes()));
5643    ///
5644    /// # Ok::<(), Box<dyn std::error::Error>>(())
5645    /// ```
5646    ///
5647    /// # Example: offset conflict resolution and disambiguation
5648    ///
5649    /// This example shows how the disambiguation configuration can
5650    /// interact with the default offset conflict resolution strategy of
5651    /// [`OffsetConflict::PreferOffset`]:
5652    ///
5653    /// ```
5654    /// use jiff::{civil::date, tz, Zoned};
5655    ///
5656    /// // This datetime is unambiguous.
5657    /// let zdt1 = "2024-03-11T02:05[America/New_York]".parse::<Zoned>()?;
5658    /// assert_eq!(zdt1.offset(), tz::offset(-4));
5659    /// // But the same time on March 10 is ambiguous because there is a gap!
5660    /// let zdt2 = zdt1
5661    ///     .with()
5662    ///     .disambiguation(tz::Disambiguation::Earlier)
5663    ///     .day(10)
5664    ///     .build()?;
5665    /// assert_eq!(zdt2.datetime(), date(2024, 3, 10).at(1, 5, 0, 0));
5666    /// assert_eq!(zdt2.offset(), tz::offset(-5));
5667    ///
5668    /// # Ok::<(), Box<dyn std::error::Error>>(())
5669    /// ```
5670    ///
5671    /// Namely, while we started with an offset of `-04`, it (along with all
5672    /// other offsets) are considered invalid during civil time gaps due to
5673    /// time zone transitions (such as the beginning of daylight saving time in
5674    /// most locations).
5675    ///
5676    /// The default disambiguation strategy is
5677    /// [`Disambiguation::Compatible`], which in the case of gaps, chooses the
5678    /// time after the gap:
5679    ///
5680    /// ```
5681    /// use jiff::{civil::date, tz, Zoned};
5682    ///
5683    /// // This datetime is unambiguous.
5684    /// let zdt1 = "2024-03-11T02:05[America/New_York]".parse::<Zoned>()?;
5685    /// assert_eq!(zdt1.offset(), tz::offset(-4));
5686    /// // But the same time on March 10 is ambiguous because there is a gap!
5687    /// let zdt2 = zdt1
5688    ///     .with()
5689    ///     .day(10)
5690    ///     .build()?;
5691    /// assert_eq!(zdt2.datetime(), date(2024, 3, 10).at(3, 5, 0, 0));
5692    /// assert_eq!(zdt2.offset(), tz::offset(-4));
5693    ///
5694    /// # Ok::<(), Box<dyn std::error::Error>>(())
5695    /// ```
5696    ///
5697    /// Alternatively, one can choose to always respect the offset, and thus
5698    /// civil time for the provided time zone will be adjusted to match the
5699    /// instant prescribed by the offset. In this case, no disambiguation is
5700    /// performed:
5701    ///
5702    /// ```
5703    /// use jiff::{civil::date, tz, Zoned};
5704    ///
5705    /// // This datetime is unambiguous. But `2024-03-10T02:05` is!
5706    /// let zdt1 = "2024-03-11T02:05[America/New_York]".parse::<Zoned>()?;
5707    /// assert_eq!(zdt1.offset(), tz::offset(-4));
5708    /// // But the same time on March 10 is ambiguous because there is a gap!
5709    /// let zdt2 = zdt1
5710    ///     .with()
5711    ///     .offset_conflict(tz::OffsetConflict::AlwaysOffset)
5712    ///     .day(10)
5713    ///     .build()?;
5714    /// // Why do we get this result? Because `2024-03-10T02:05-04` is
5715    /// // `2024-03-10T06:05Z`. And in `America/New_York`, the civil time
5716    /// // for that timestamp is `2024-03-10T01:05-05`.
5717    /// assert_eq!(zdt2.datetime(), date(2024, 3, 10).at(1, 5, 0, 0));
5718    /// assert_eq!(zdt2.offset(), tz::offset(-5));
5719    ///
5720    /// # Ok::<(), Box<dyn std::error::Error>>(())
5721    /// ```
5722    #[inline]
5723    pub fn disambiguation(self, strategy: Disambiguation) -> ZonedWith {
5724        ZonedWith { disambiguation: strategy, ..self }
5725    }
5726}
5727
5728#[cfg(test)]
5729mod tests {
5730    use std::io::Cursor;
5731
5732    use alloc::string::ToString;
5733
5734    use crate::{
5735        civil::{date, datetime},
5736        span::span_eq,
5737        tz, ToSpan,
5738    };
5739
5740    use super::*;
5741
5742    #[test]
5743    fn until_with_largest_unit() {
5744        if crate::tz::db().is_definitively_empty() {
5745            return;
5746        }
5747
5748        let zdt1: Zoned = date(1995, 12, 7)
5749            .at(3, 24, 30, 3500)
5750            .in_tz("Asia/Kolkata")
5751            .unwrap();
5752        let zdt2: Zoned =
5753            date(2019, 1, 31).at(15, 30, 0, 0).in_tz("Asia/Kolkata").unwrap();
5754        let span = zdt1.until(&zdt2).unwrap();
5755        span_eq!(
5756            span,
5757            202956
5758                .hours()
5759                .minutes(5)
5760                .seconds(29)
5761                .milliseconds(999)
5762                .microseconds(996)
5763                .nanoseconds(500)
5764        );
5765        let span = zdt1.until((Unit::Year, &zdt2)).unwrap();
5766        span_eq!(
5767            span,
5768            23.years()
5769                .months(1)
5770                .days(24)
5771                .hours(12)
5772                .minutes(5)
5773                .seconds(29)
5774                .milliseconds(999)
5775                .microseconds(996)
5776                .nanoseconds(500)
5777        );
5778
5779        let span = zdt2.until((Unit::Year, &zdt1)).unwrap();
5780        span_eq!(
5781            span,
5782            -23.years()
5783                .months(1)
5784                .days(24)
5785                .hours(12)
5786                .minutes(5)
5787                .seconds(29)
5788                .milliseconds(999)
5789                .microseconds(996)
5790                .nanoseconds(500)
5791        );
5792        let span = zdt1.until((Unit::Nanosecond, &zdt2)).unwrap();
5793        span_eq!(span, 730641929999996500i64.nanoseconds());
5794
5795        let zdt1: Zoned =
5796            date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
5797        let zdt2: Zoned = date(2020, 4, 24)
5798            .at(21, 0, 0, 0)
5799            .in_tz("America/New_York")
5800            .unwrap();
5801        let span = zdt1.until(&zdt2).unwrap();
5802        span_eq!(span, 2756.hours());
5803        let span = zdt1.until((Unit::Year, &zdt2)).unwrap();
5804        span_eq!(span, 3.months().days(23).hours(21));
5805
5806        let zdt1: Zoned = date(2000, 10, 29)
5807            .at(0, 0, 0, 0)
5808            .in_tz("America/Vancouver")
5809            .unwrap();
5810        let zdt2: Zoned = date(2000, 10, 29)
5811            .at(23, 0, 0, 5)
5812            .in_tz("America/Vancouver")
5813            .unwrap();
5814        let span = zdt1.until((Unit::Day, &zdt2)).unwrap();
5815        span_eq!(span, 24.hours().nanoseconds(5));
5816    }
5817
5818    #[cfg(target_pointer_width = "64")]
5819    #[test]
5820    fn zoned_size() {
5821        #[cfg(debug_assertions)]
5822        {
5823            #[cfg(feature = "alloc")]
5824            {
5825                assert_eq!(96, core::mem::size_of::<Zoned>());
5826            }
5827            #[cfg(all(target_pointer_width = "64", not(feature = "alloc")))]
5828            {
5829                assert_eq!(96, core::mem::size_of::<Zoned>());
5830            }
5831        }
5832        #[cfg(not(debug_assertions))]
5833        {
5834            #[cfg(feature = "alloc")]
5835            {
5836                assert_eq!(40, core::mem::size_of::<Zoned>());
5837            }
5838            #[cfg(all(target_pointer_width = "64", not(feature = "alloc")))]
5839            {
5840                // This asserts the same value as the alloc value above, but
5841                // it wasn't always this way, which is why it's written out
5842                // separately. Moreover, in theory, I'd be open to regressing
5843                // this value if it led to an improvement in alloc-mode. But
5844                // more likely, it would be nice to decrease this size in
5845                // non-alloc modes.
5846                assert_eq!(40, core::mem::size_of::<Zoned>());
5847            }
5848        }
5849    }
5850
5851    /// A `serde` deserializer compatibility test.
5852    ///
5853    /// Serde YAML used to be unable to deserialize `jiff` types,
5854    /// as deserializing from bytes is not supported by the deserializer.
5855    ///
5856    /// - <https://github.com/BurntSushi/jiff/issues/138>
5857    /// - <https://github.com/BurntSushi/jiff/discussions/148>
5858    #[test]
5859    fn zoned_deserialize_yaml() {
5860        if crate::tz::db().is_definitively_empty() {
5861            return;
5862        }
5863
5864        let expected = datetime(2024, 10, 31, 16, 33, 53, 123456789)
5865            .in_tz("UTC")
5866            .unwrap();
5867
5868        let deserialized: Zoned =
5869            serde_yaml::from_str("2024-10-31T16:33:53.123456789+00:00[UTC]")
5870                .unwrap();
5871
5872        assert_eq!(deserialized, expected);
5873
5874        let deserialized: Zoned = serde_yaml::from_slice(
5875            "2024-10-31T16:33:53.123456789+00:00[UTC]".as_bytes(),
5876        )
5877        .unwrap();
5878
5879        assert_eq!(deserialized, expected);
5880
5881        let cursor = Cursor::new(b"2024-10-31T16:33:53.123456789+00:00[UTC]");
5882        let deserialized: Zoned = serde_yaml::from_reader(cursor).unwrap();
5883
5884        assert_eq!(deserialized, expected);
5885    }
5886
5887    /// This is a regression test for a case where changing a zoned datetime
5888    /// to have a time of midnight ends up producing a counter-intuitive
5889    /// result.
5890    ///
5891    /// See: <https://github.com/BurntSushi/jiff/issues/211>
5892    #[test]
5893    fn zoned_with_time_dst_after_gap() {
5894        if crate::tz::db().is_definitively_empty() {
5895            return;
5896        }
5897
5898        let zdt1: Zoned = "2024-03-31T12:00[Atlantic/Azores]".parse().unwrap();
5899        assert_eq!(
5900            zdt1.to_string(),
5901            "2024-03-31T12:00:00+00:00[Atlantic/Azores]"
5902        );
5903
5904        let zdt2 = zdt1.with().time(Time::midnight()).build().unwrap();
5905        assert_eq!(
5906            zdt2.to_string(),
5907            "2024-03-31T01:00:00+00:00[Atlantic/Azores]"
5908        );
5909    }
5910
5911    /// Similar to `zoned_with_time_dst_after_gap`, but tests what happens
5912    /// when moving from/to both sides of the gap.
5913    ///
5914    /// See: <https://github.com/BurntSushi/jiff/issues/211>
5915    #[test]
5916    fn zoned_with_time_dst_us_eastern() {
5917        if crate::tz::db().is_definitively_empty() {
5918            return;
5919        }
5920
5921        let zdt1: Zoned = "2024-03-10T01:30[US/Eastern]".parse().unwrap();
5922        assert_eq!(zdt1.to_string(), "2024-03-10T01:30:00-05:00[US/Eastern]");
5923        let zdt2 = zdt1.with().hour(2).build().unwrap();
5924        assert_eq!(zdt2.to_string(), "2024-03-10T03:30:00-04:00[US/Eastern]");
5925
5926        let zdt1: Zoned = "2024-03-10T03:30[US/Eastern]".parse().unwrap();
5927        assert_eq!(zdt1.to_string(), "2024-03-10T03:30:00-04:00[US/Eastern]");
5928        let zdt2 = zdt1.with().hour(2).build().unwrap();
5929        assert_eq!(zdt2.to_string(), "2024-03-10T03:30:00-04:00[US/Eastern]");
5930
5931        // I originally thought that this was difference from Temporal. Namely,
5932        // I thought that Temporal ignored the disambiguation setting (and the
5933        // bad offset). But it doesn't. I was holding it wrong.
5934        //
5935        // See: https://github.com/tc39/proposal-temporal/issues/3078
5936        let zdt1: Zoned = "2024-03-10T01:30[US/Eastern]".parse().unwrap();
5937        assert_eq!(zdt1.to_string(), "2024-03-10T01:30:00-05:00[US/Eastern]");
5938        let zdt2 = zdt1
5939            .with()
5940            .offset(tz::offset(10))
5941            .hour(2)
5942            .disambiguation(Disambiguation::Earlier)
5943            .build()
5944            .unwrap();
5945        assert_eq!(zdt2.to_string(), "2024-03-10T01:30:00-05:00[US/Eastern]");
5946
5947        // This should also respect the disambiguation setting even without
5948        // explicitly specifying an invalid offset. This is because `02:30-05`
5949        // is regarded as invalid since `02:30` isn't a valid civil time on
5950        // this date in this time zone.
5951        let zdt1: Zoned = "2024-03-10T01:30[US/Eastern]".parse().unwrap();
5952        assert_eq!(zdt1.to_string(), "2024-03-10T01:30:00-05:00[US/Eastern]");
5953        let zdt2 = zdt1
5954            .with()
5955            .hour(2)
5956            .disambiguation(Disambiguation::Earlier)
5957            .build()
5958            .unwrap();
5959        assert_eq!(zdt2.to_string(), "2024-03-10T01:30:00-05:00[US/Eastern]");
5960    }
5961
5962    #[test]
5963    fn zoned_precision_loss() {
5964        if crate::tz::db().is_definitively_empty() {
5965            return;
5966        }
5967
5968        let zdt1: Zoned = "2025-01-25T19:32:21.783444592+01:00[Europe/Paris]"
5969            .parse()
5970            .unwrap();
5971        let span = 1.second();
5972        let zdt2 = &zdt1 + span;
5973        assert_eq!(
5974            zdt2.to_string(),
5975            "2025-01-25T19:32:22.783444592+01:00[Europe/Paris]"
5976        );
5977        assert_eq!(zdt1, &zdt2 - span, "should be reversible");
5978    }
5979
5980    // See: https://github.com/BurntSushi/jiff/issues/290
5981    #[test]
5982    fn zoned_roundtrip_regression() {
5983        if crate::tz::db().is_definitively_empty() {
5984            return;
5985        }
5986
5987        let zdt: Zoned =
5988            "2063-03-31T10:00:00+11:00[Australia/Sydney]".parse().unwrap();
5989        assert_eq!(zdt.offset(), super::Offset::constant(11));
5990        let roundtrip = zdt.time_zone().to_zoned(zdt.datetime()).unwrap();
5991        assert_eq!(zdt, roundtrip);
5992    }
5993
5994    // See: https://github.com/BurntSushi/jiff/issues/305
5995    #[test]
5996    fn zoned_round_dst_day_length() {
5997        if crate::tz::db().is_definitively_empty() {
5998            return;
5999        }
6000
6001        let zdt1: Zoned =
6002            "2025-03-09T12:15[America/New_York]".parse().unwrap();
6003        let zdt2 = zdt1.round(Unit::Day).unwrap();
6004        // Since this day is only 23 hours long, it should round down instead
6005        // of up (as it would on a normal 24 hour day). Interestingly, the bug
6006        // was causing this to not only round up, but to a datetime that wasn't
6007        // the start of a day. Specifically, 2025-03-10T01:00:00-04:00.
6008        assert_eq!(
6009            zdt2.to_string(),
6010            "2025-03-09T00:00:00-05:00[America/New_York]"
6011        );
6012    }
6013
6014    #[test]
6015    fn zoned_round_errors() {
6016        if crate::tz::db().is_definitively_empty() {
6017            return;
6018        }
6019
6020        let zdt: Zoned = "2025-03-09T12:15[America/New_York]".parse().unwrap();
6021
6022        insta::assert_snapshot!(
6023            zdt.round(Unit::Year).unwrap_err(),
6024            @"failed rounding datetime: rounding to years is not supported"
6025        );
6026        insta::assert_snapshot!(
6027            zdt.round(Unit::Month).unwrap_err(),
6028            @"failed rounding datetime: rounding to months is not supported"
6029        );
6030        insta::assert_snapshot!(
6031            zdt.round(Unit::Week).unwrap_err(),
6032            @"failed rounding datetime: rounding to weeks is not supported"
6033        );
6034
6035        let options = ZonedRound::new().smallest(Unit::Day).increment(2);
6036        insta::assert_snapshot!(
6037            zdt.round(options).unwrap_err(),
6038            @"failed rounding datetime: increment for rounding to days must be 1) less than 2, 2) divide into it evenly and 3) greater than zero"
6039        );
6040    }
6041
6042    // This tests that if we get a time zone offset with an explicit second
6043    // component, then it must *exactly* match the correct offset for that
6044    // civil time.
6045    //
6046    // See: https://github.com/tc39/proposal-temporal/issues/3099
6047    // See: https://github.com/tc39/proposal-temporal/pull/3107
6048    #[test]
6049    fn time_zone_offset_seconds_exact_match() {
6050        if crate::tz::db().is_definitively_empty() {
6051            return;
6052        }
6053
6054        let zdt: Zoned =
6055            "1970-06-01T00:00:00-00:45[Africa/Monrovia]".parse().unwrap();
6056        assert_eq!(
6057            zdt.to_string(),
6058            "1970-06-01T00:00:00-00:45[Africa/Monrovia]"
6059        );
6060
6061        let zdt: Zoned =
6062            "1970-06-01T00:00:00-00:44:30[Africa/Monrovia]".parse().unwrap();
6063        assert_eq!(
6064            zdt.to_string(),
6065            "1970-06-01T00:00:00-00:45[Africa/Monrovia]"
6066        );
6067
6068        insta::assert_snapshot!(
6069            "1970-06-01T00:00:00-00:44:40[Africa/Monrovia]".parse::<Zoned>().unwrap_err(),
6070            @"datetime could not resolve to a timestamp since `reject` conflict resolution was chosen, and because datetime has offset `-00:44:40`, but the time zone `Africa/Monrovia` for the given datetime unambiguously has offset `-00:44:30`",
6071        );
6072
6073        insta::assert_snapshot!(
6074            "1970-06-01T00:00:00-00:45:00[Africa/Monrovia]".parse::<Zoned>().unwrap_err(),
6075            @"datetime could not resolve to a timestamp since `reject` conflict resolution was chosen, and because datetime has offset `-00:45`, but the time zone `Africa/Monrovia` for the given datetime unambiguously has offset `-00:44:30`",
6076        );
6077    }
6078
6079    // These are some interesting tests because the time zones have transitions
6080    // that are very close to one another (within 14 days!). I picked these up
6081    // from a bug report to Temporal. Their reference implementation uses
6082    // different logic to examine time zone transitions than Jiff. In contrast,
6083    // Jiff uses the IANA time zone database directly. So it was unaffected.
6084    //
6085    // [1]: https://github.com/tc39/proposal-temporal/issues/3110
6086    #[test]
6087    fn weird_time_zone_transitions() {
6088        if crate::tz::db().is_definitively_empty() {
6089            return;
6090        }
6091
6092        let zdt: Zoned =
6093            "2000-10-08T01:00:00-01:00[America/Noronha]".parse().unwrap();
6094        let sod = zdt.start_of_day().unwrap();
6095        assert_eq!(
6096            sod.to_string(),
6097            "2000-10-08T01:00:00-01:00[America/Noronha]"
6098        );
6099
6100        let zdt: Zoned =
6101            "2000-10-08T03:00:00-03:00[America/Boa_Vista]".parse().unwrap();
6102        let sod = zdt.start_of_day().unwrap();
6103        assert_eq!(
6104            sod.to_string(),
6105            "2000-10-08T01:00:00-03:00[America/Boa_Vista]",
6106        );
6107    }
6108}