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