Skip to main content

jiff/
zoned.rs

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