Skip to main content

jiff/
zoned.rs

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