Coverage Report

Created: 2026-06-28 08:04

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