Coverage Report

Created: 2026-07-14 07:20

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