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