Coverage Report

Created: 2025-11-06 06:06

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