Coverage Report

Created: 2026-03-31 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chrono-0.4.35/src/offset/mod.rs
Line
Count
Source
1
// This is a part of Chrono.
2
// See README.md and LICENSE.txt for details.
3
4
//! The time zone, which calculates offsets from the local time to UTC.
5
//!
6
//! There are four operations provided by the `TimeZone` trait:
7
//!
8
//! 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
9
//! 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
10
//! 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
11
//! 4. Constructing `DateTime<Tz>` objects from various offsets
12
//!
13
//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
14
//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
15
//! which implements `Offset` (which then passed to `TimeZone` for actual implementations).
16
//! Technically speaking `TimeZone` has a total knowledge about given timescale,
17
//! but `Offset` is used as a cache to avoid the repeated conversion
18
//! and provides implementations for 1 and 3.
19
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
20
21
use core::fmt;
22
23
use crate::format::{parse, ParseResult, Parsed, StrftimeItems};
24
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
25
use crate::Weekday;
26
#[allow(deprecated)]
27
use crate::{Date, DateTime};
28
29
pub(crate) mod fixed;
30
pub use self::fixed::FixedOffset;
31
32
#[cfg(feature = "clock")]
33
pub(crate) mod local;
34
#[cfg(feature = "clock")]
35
pub use self::local::Local;
36
37
pub(crate) mod utc;
38
pub use self::utc::Utc;
39
40
/// The conversion result from the local time to the timezone-aware datetime types.
41
#[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)]
42
pub enum LocalResult<T> {
43
    /// Given local time representation is invalid.
44
    /// This can occur when, for example, the positive timezone transition.
45
    None,
46
    /// Given local time representation has a single unique result.
47
    Single(T),
48
    /// Given local time representation has multiple results and thus ambiguous.
49
    /// This can occur when, for example, the negative timezone transition.
50
    Ambiguous(T /* min */, T /* max */),
51
}
52
53
impl<T> LocalResult<T> {
54
    /// Returns `Some` only when the conversion result is unique, or `None` otherwise.
55
    #[must_use]
56
0
    pub fn single(self) -> Option<T> {
57
0
        match self {
58
0
            LocalResult::Single(t) => Some(t),
59
0
            _ => None,
60
        }
61
0
    }
Unexecuted instantiation: <chrono::offset::LocalResult<_>>::single
Unexecuted instantiation: <chrono::offset::LocalResult<chrono::datetime::DateTime<chrono::offset::utc::Utc>>>::single
62
63
    /// Returns `Some` for the earliest possible conversion result, or `None` if none.
64
    #[must_use]
65
0
    pub fn earliest(self) -> Option<T> {
66
0
        match self {
67
0
            LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t),
68
0
            _ => None,
69
        }
70
0
    }
71
72
    /// Returns `Some` for the latest possible conversion result, or `None` if none.
73
    #[must_use]
74
0
    pub fn latest(self) -> Option<T> {
75
0
        match self {
76
0
            LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t),
77
0
            _ => None,
78
        }
79
0
    }
80
81
    /// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
82
    #[must_use]
83
0
    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
84
0
        match self {
85
0
            LocalResult::None => LocalResult::None,
86
0
            LocalResult::Single(v) => LocalResult::Single(f(v)),
87
0
            LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
88
        }
89
0
    }
90
}
91
92
#[allow(deprecated)]
93
impl<Tz: TimeZone> LocalResult<Date<Tz>> {
94
    /// Makes a new `DateTime` from the current date and given `NaiveTime`.
95
    /// The offset in the current date is preserved.
96
    ///
97
    /// Propagates any error. Ambiguous result would be discarded.
98
    #[inline]
99
    #[must_use]
100
0
    pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
101
0
        match self {
102
0
            LocalResult::Single(d) => {
103
0
                d.and_time(time).map_or(LocalResult::None, LocalResult::Single)
104
            }
105
0
            _ => LocalResult::None,
106
        }
107
0
    }
108
109
    /// Makes a new `DateTime` from the current date, hour, minute and second.
110
    /// The offset in the current date is preserved.
111
    ///
112
    /// Propagates any error. Ambiguous result would be discarded.
113
    #[inline]
114
    #[must_use]
115
0
    pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
116
0
        match self {
117
0
            LocalResult::Single(d) => {
118
0
                d.and_hms_opt(hour, min, sec).map_or(LocalResult::None, LocalResult::Single)
119
            }
120
0
            _ => LocalResult::None,
121
        }
122
0
    }
123
124
    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
125
    /// The millisecond part can exceed 1,000 in order to represent the leap second.
126
    /// The offset in the current date is preserved.
127
    ///
128
    /// Propagates any error. Ambiguous result would be discarded.
129
    #[inline]
130
    #[must_use]
131
0
    pub fn and_hms_milli_opt(
132
0
        self,
133
0
        hour: u32,
134
0
        min: u32,
135
0
        sec: u32,
136
0
        milli: u32,
137
0
    ) -> LocalResult<DateTime<Tz>> {
138
0
        match self {
139
0
            LocalResult::Single(d) => d
140
0
                .and_hms_milli_opt(hour, min, sec, milli)
141
0
                .map_or(LocalResult::None, LocalResult::Single),
142
0
            _ => LocalResult::None,
143
        }
144
0
    }
145
146
    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
147
    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
148
    /// The offset in the current date is preserved.
149
    ///
150
    /// Propagates any error. Ambiguous result would be discarded.
151
    #[inline]
152
    #[must_use]
153
0
    pub fn and_hms_micro_opt(
154
0
        self,
155
0
        hour: u32,
156
0
        min: u32,
157
0
        sec: u32,
158
0
        micro: u32,
159
0
    ) -> LocalResult<DateTime<Tz>> {
160
0
        match self {
161
0
            LocalResult::Single(d) => d
162
0
                .and_hms_micro_opt(hour, min, sec, micro)
163
0
                .map_or(LocalResult::None, LocalResult::Single),
164
0
            _ => LocalResult::None,
165
        }
166
0
    }
167
168
    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
169
    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
170
    /// The offset in the current date is preserved.
171
    ///
172
    /// Propagates any error. Ambiguous result would be discarded.
173
    #[inline]
174
    #[must_use]
175
0
    pub fn and_hms_nano_opt(
176
0
        self,
177
0
        hour: u32,
178
0
        min: u32,
179
0
        sec: u32,
180
0
        nano: u32,
181
0
    ) -> LocalResult<DateTime<Tz>> {
182
0
        match self {
183
0
            LocalResult::Single(d) => d
184
0
                .and_hms_nano_opt(hour, min, sec, nano)
185
0
                .map_or(LocalResult::None, LocalResult::Single),
186
0
            _ => LocalResult::None,
187
        }
188
0
    }
189
}
190
191
impl<T: fmt::Debug> LocalResult<T> {
192
    /// Returns the single unique conversion result, or panics accordingly.
193
    #[must_use]
194
    #[track_caller]
195
0
    pub fn unwrap(self) -> T {
196
0
        match self {
197
0
            LocalResult::None => panic!("No such local time"),
198
0
            LocalResult::Single(t) => t,
199
0
            LocalResult::Ambiguous(t1, t2) => {
200
0
                panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
201
            }
202
        }
203
0
    }
204
}
205
206
/// The offset from the local time to UTC.
207
pub trait Offset: Sized + Clone + fmt::Debug {
208
    /// Returns the fixed offset from UTC to the local time stored.
209
    fn fix(&self) -> FixedOffset;
210
}
211
212
/// The time zone.
213
///
214
/// The methods here are the primary constructors for the [`DateTime`] type.
215
pub trait TimeZone: Sized + Clone {
216
    /// An associated offset type.
217
    /// This type is used to store the actual offset in date and time types.
218
    /// The original `TimeZone` value can be recovered via `TimeZone::from_offset`.
219
    type Offset: Offset;
220
221
    /// Make a new `DateTime` from year, month, day, time components and current time zone.
222
    ///
223
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
224
    ///
225
    /// Returns `LocalResult::None` on invalid input data.
226
0
    fn with_ymd_and_hms(
227
0
        &self,
228
0
        year: i32,
229
0
        month: u32,
230
0
        day: u32,
231
0
        hour: u32,
232
0
        min: u32,
233
0
        sec: u32,
234
0
    ) -> LocalResult<DateTime<Self>> {
235
0
        match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec))
Unexecuted instantiation: <_ as chrono::offset::TimeZone>::with_ymd_and_hms::{closure#0}
Unexecuted instantiation: <chrono::offset::utc::Utc as chrono::offset::TimeZone>::with_ymd_and_hms::{closure#0}
236
        {
237
0
            Some(dt) => self.from_local_datetime(&dt),
238
0
            None => LocalResult::None,
239
        }
240
0
    }
Unexecuted instantiation: <_ as chrono::offset::TimeZone>::with_ymd_and_hms
Unexecuted instantiation: <chrono::offset::utc::Utc as chrono::offset::TimeZone>::with_ymd_and_hms
241
242
    /// Makes a new `Date` from year, month, day and the current time zone.
243
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
244
    ///
245
    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
246
    /// but it will propagate to the `DateTime` values constructed via this date.
247
    ///
248
    /// Panics on the out-of-range date, invalid month and/or day.
249
    #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
250
    #[allow(deprecated)]
251
0
    fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
252
0
        self.ymd_opt(year, month, day).unwrap()
253
0
    }
254
255
    /// Makes a new `Date` from year, month, day and the current time zone.
256
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
257
    ///
258
    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
259
    /// but it will propagate to the `DateTime` values constructed via this date.
260
    ///
261
    /// Returns `None` on the out-of-range date, invalid month and/or day.
262
    #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
263
    #[allow(deprecated)]
264
0
    fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
265
0
        match NaiveDate::from_ymd_opt(year, month, day) {
266
0
            Some(d) => self.from_local_date(&d),
267
0
            None => LocalResult::None,
268
        }
269
0
    }
270
271
    /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
272
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
273
    ///
274
    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
275
    /// but it will propagate to the `DateTime` values constructed via this date.
276
    ///
277
    /// Panics on the out-of-range date and/or invalid DOY.
278
    #[deprecated(
279
        since = "0.4.23",
280
        note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
281
    )]
282
    #[allow(deprecated)]
283
0
    fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
284
0
        self.yo_opt(year, ordinal).unwrap()
285
0
    }
286
287
    /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
288
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
289
    ///
290
    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
291
    /// but it will propagate to the `DateTime` values constructed via this date.
292
    ///
293
    /// Returns `None` on the out-of-range date and/or invalid DOY.
294
    #[deprecated(
295
        since = "0.4.23",
296
        note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
297
    )]
298
    #[allow(deprecated)]
299
0
    fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
300
0
        match NaiveDate::from_yo_opt(year, ordinal) {
301
0
            Some(d) => self.from_local_date(&d),
302
0
            None => LocalResult::None,
303
        }
304
0
    }
305
306
    /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
307
    /// the current time zone.
308
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
309
    /// The resulting `Date` may have a different year from the input year.
310
    ///
311
    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
312
    /// but it will propagate to the `DateTime` values constructed via this date.
313
    ///
314
    /// Panics on the out-of-range date and/or invalid week number.
315
    #[deprecated(
316
        since = "0.4.23",
317
        note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
318
    )]
319
    #[allow(deprecated)]
320
0
    fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
321
0
        self.isoywd_opt(year, week, weekday).unwrap()
322
0
    }
323
324
    /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
325
    /// the current time zone.
326
    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
327
    /// The resulting `Date` may have a different year from the input year.
328
    ///
329
    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
330
    /// but it will propagate to the `DateTime` values constructed via this date.
331
    ///
332
    /// Returns `None` on the out-of-range date and/or invalid week number.
333
    #[deprecated(
334
        since = "0.4.23",
335
        note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
336
    )]
337
    #[allow(deprecated)]
338
0
    fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
339
0
        match NaiveDate::from_isoywd_opt(year, week, weekday) {
340
0
            Some(d) => self.from_local_date(&d),
341
0
            None => LocalResult::None,
342
        }
343
0
    }
344
345
    /// Makes a new `DateTime` from the number of non-leap seconds
346
    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
347
    /// and the number of nanoseconds since the last whole non-leap second.
348
    ///
349
    /// The nanosecond part can exceed 1,000,000,000 in order to represent a
350
    /// [leap second](crate::NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
351
    /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
352
    ///
353
    /// # Panics
354
    ///
355
    /// Panics on the out-of-range number of seconds and/or invalid nanosecond,
356
    /// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt).
357
    #[deprecated(since = "0.4.23", note = "use `timestamp_opt()` instead")]
358
0
    fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
359
0
        self.timestamp_opt(secs, nsecs).unwrap()
360
0
    }
361
362
    /// Makes a new `DateTime` from the number of non-leap seconds
363
    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
364
    /// and the number of nanoseconds since the last whole non-leap second.
365
    ///
366
    /// The nanosecond part can exceed 1,000,000,000 in order to represent a
367
    /// [leap second](crate::NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
368
    /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
369
    ///
370
    /// # Errors
371
    ///
372
    /// Returns `LocalResult::None` on out-of-range number of seconds and/or
373
    /// invalid nanosecond, otherwise always returns `LocalResult::Single`.
374
    ///
375
    /// # Example
376
    ///
377
    /// ```
378
    /// use chrono::{TimeZone, Utc};
379
    ///
380
    /// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC");
381
    /// ```
382
0
    fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> {
383
0
        match DateTime::from_timestamp(secs, nsecs) {
384
0
            Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt.naive_utc())),
385
0
            None => LocalResult::None,
386
        }
387
0
    }
388
389
    /// Makes a new `DateTime` from the number of non-leap milliseconds
390
    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
391
    ///
392
    /// Panics on out-of-range number of milliseconds for a non-panicking
393
    /// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt).
394
    #[deprecated(since = "0.4.23", note = "use `timestamp_millis_opt()` instead")]
395
0
    fn timestamp_millis(&self, millis: i64) -> DateTime<Self> {
396
0
        self.timestamp_millis_opt(millis).unwrap()
397
0
    }
398
399
    /// Makes a new `DateTime` from the number of non-leap milliseconds
400
    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
401
    ///
402
    ///
403
    /// Returns `LocalResult::None` on out-of-range number of milliseconds
404
    /// and/or invalid nanosecond, otherwise always returns
405
    /// `LocalResult::Single`.
406
    ///
407
    /// # Example
408
    ///
409
    /// ```
410
    /// use chrono::{LocalResult, TimeZone, Utc};
411
    /// match Utc.timestamp_millis_opt(1431648000) {
412
    ///     LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648),
413
    ///     _ => panic!("Incorrect timestamp_millis"),
414
    /// };
415
    /// ```
416
0
    fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>> {
417
0
        match DateTime::from_timestamp_millis(millis) {
418
0
            Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt.naive_utc())),
419
0
            None => LocalResult::None,
420
        }
421
0
    }
422
423
    /// Makes a new `DateTime` from the number of non-leap nanoseconds
424
    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
425
    ///
426
    /// Unlike [`timestamp_millis_opt`](#method.timestamp_millis_opt), this never fails.
427
    ///
428
    /// # Example
429
    ///
430
    /// ```
431
    /// use chrono::{TimeZone, Utc};
432
    ///
433
    /// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648);
434
    /// ```
435
0
    fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self> {
436
0
        self.from_utc_datetime(&DateTime::from_timestamp_nanos(nanos).naive_utc())
437
0
    }
438
439
    /// Makes a new `DateTime` from the number of non-leap microseconds
440
    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
441
    ///
442
    /// # Example
443
    ///
444
    /// ```
445
    /// use chrono::{TimeZone, Utc};
446
    ///
447
    /// assert_eq!(Utc.timestamp_micros(1431648000000).unwrap().timestamp(), 1431648);
448
    /// ```
449
0
    fn timestamp_micros(&self, micros: i64) -> LocalResult<DateTime<Self>> {
450
0
        match DateTime::from_timestamp_micros(micros) {
451
0
            Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt.naive_utc())),
452
0
            None => LocalResult::None,
453
        }
454
0
    }
455
456
    /// Parses a string with the specified format string and returns a
457
    /// `DateTime` with the current offset.
458
    ///
459
    /// See the [`crate::format::strftime`] module on the
460
    /// supported escape sequences.
461
    ///
462
    /// If the to-be-parsed string includes an offset, it *must* match the
463
    /// offset of the TimeZone, otherwise an error will be returned.
464
    ///
465
    /// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with
466
    /// parsed [`FixedOffset`].
467
    ///
468
    /// See also [`NaiveDateTime::parse_from_str`] which gives a [`NaiveDateTime`] without
469
    /// an offset, but can be converted to a [`DateTime`] with [`NaiveDateTime::and_utc`] or
470
    /// [`NaiveDateTime::and_local_timezone`].
471
    #[deprecated(
472
        since = "0.4.29",
473
        note = "use `DateTime::parse_from_str` or `NaiveDateTime::parse_from_str` with `and_utc()` or `and_local_timezone()` instead"
474
    )]
475
0
    fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
476
0
        let mut parsed = Parsed::new();
477
0
        parse(&mut parsed, s, StrftimeItems::new(fmt))?;
478
0
        parsed.to_datetime_with_timezone(self)
479
0
    }
480
481
    /// Reconstructs the time zone from the offset.
482
    fn from_offset(offset: &Self::Offset) -> Self;
483
484
    /// Creates the offset(s) for given local `NaiveDate` if possible.
485
    fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
486
487
    /// Creates the offset(s) for given local `NaiveDateTime` if possible.
488
    fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
489
490
    /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
491
    #[allow(clippy::wrong_self_convention)]
492
    #[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")]
493
    #[allow(deprecated)]
494
0
    fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
495
0
        self.offset_from_local_date(local).map(|offset| {
496
            // since FixedOffset is within +/- 1 day, the date is never affected
497
0
            Date::from_utc(*local, offset)
498
0
        })
499
0
    }
500
501
    /// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
502
    #[allow(clippy::wrong_self_convention)]
503
0
    fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
504
        // Return `LocalResult::None` when the offset pushes a value out of range, instead of
505
        // panicking.
506
0
        match self.offset_from_local_datetime(local) {
507
0
            LocalResult::None => LocalResult::None,
508
0
            LocalResult::Single(offset) => match local.checked_sub_offset(offset.fix()) {
509
0
                Some(dt) => LocalResult::Single(DateTime::from_naive_utc_and_offset(dt, offset)),
510
0
                None => LocalResult::None,
511
            },
512
0
            LocalResult::Ambiguous(o1, o2) => {
513
0
                match (local.checked_sub_offset(o1.fix()), local.checked_sub_offset(o2.fix())) {
514
0
                    (Some(d1), Some(d2)) => LocalResult::Ambiguous(
515
0
                        DateTime::from_naive_utc_and_offset(d1, o1),
516
0
                        DateTime::from_naive_utc_and_offset(d2, o2),
517
0
                    ),
518
0
                    _ => LocalResult::None,
519
                }
520
            }
521
        }
522
0
    }
Unexecuted instantiation: <chrono::offset::fixed::FixedOffset as chrono::offset::TimeZone>::from_local_datetime
Unexecuted instantiation: <chrono::offset::utc::Utc as chrono::offset::TimeZone>::from_local_datetime
523
524
    /// Creates the offset for given UTC `NaiveDate`. This cannot fail.
525
    fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset;
526
527
    /// Creates the offset for given UTC `NaiveDateTime`. This cannot fail.
528
    fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset;
529
530
    /// Converts the UTC `NaiveDate` to the local time.
531
    /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
532
    #[allow(clippy::wrong_self_convention)]
533
    #[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")]
534
    #[allow(deprecated)]
535
0
    fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
536
0
        Date::from_utc(*utc, self.offset_from_utc_date(utc))
537
0
    }
538
539
    /// Converts the UTC `NaiveDateTime` to the local time.
540
    /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
541
    #[allow(clippy::wrong_self_convention)]
542
0
    fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
543
0
        DateTime::from_naive_utc_and_offset(*utc, self.offset_from_utc_datetime(utc))
544
0
    }
Unexecuted instantiation: <chrono::offset::fixed::FixedOffset as chrono::offset::TimeZone>::from_utc_datetime
Unexecuted instantiation: <chrono::offset::utc::Utc as chrono::offset::TimeZone>::from_utc_datetime
545
}
546
547
#[cfg(test)]
548
mod tests {
549
    use super::*;
550
551
    #[test]
552
    fn test_fixed_offset_min_max_dates() {
553
        for offset_hour in -23..=23 {
554
            dbg!(offset_hour);
555
            let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap();
556
557
            let local_max = offset.from_utc_datetime(&NaiveDateTime::MAX);
558
            assert_eq!(local_max.naive_utc(), NaiveDateTime::MAX);
559
            let local_min = offset.from_utc_datetime(&NaiveDateTime::MIN);
560
            assert_eq!(local_min.naive_utc(), NaiveDateTime::MIN);
561
562
            let local_max = offset.from_local_datetime(&NaiveDateTime::MAX);
563
            if offset_hour >= 0 {
564
                assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX);
565
            } else {
566
                assert_eq!(local_max, LocalResult::None);
567
            }
568
            let local_min = offset.from_local_datetime(&NaiveDateTime::MIN);
569
            if offset_hour <= 0 {
570
                assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN);
571
            } else {
572
                assert_eq!(local_min, LocalResult::None);
573
            }
574
        }
575
    }
576
577
    #[test]
578
    fn test_negative_millis() {
579
        let dt = Utc.timestamp_millis_opt(-1000).unwrap();
580
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
581
        let dt = Utc.timestamp_millis_opt(-7000).unwrap();
582
        assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC");
583
        let dt = Utc.timestamp_millis_opt(-7001).unwrap();
584
        assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC");
585
        let dt = Utc.timestamp_millis_opt(-7003).unwrap();
586
        assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC");
587
        let dt = Utc.timestamp_millis_opt(-999).unwrap();
588
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC");
589
        let dt = Utc.timestamp_millis_opt(-1).unwrap();
590
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC");
591
        let dt = Utc.timestamp_millis_opt(-60000).unwrap();
592
        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
593
        let dt = Utc.timestamp_millis_opt(-3600000).unwrap();
594
        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
595
596
        for (millis, expected) in &[
597
            (-7000, "1969-12-31 23:59:53 UTC"),
598
            (-7001, "1969-12-31 23:59:52.999 UTC"),
599
            (-7003, "1969-12-31 23:59:52.997 UTC"),
600
        ] {
601
            match Utc.timestamp_millis_opt(*millis) {
602
                LocalResult::Single(dt) => {
603
                    assert_eq!(dt.to_string(), *expected);
604
                }
605
                e => panic!("Got {:?} instead of an okay answer", e),
606
            }
607
        }
608
    }
609
610
    #[test]
611
    fn test_negative_nanos() {
612
        let dt = Utc.timestamp_nanos(-1_000_000_000);
613
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
614
        let dt = Utc.timestamp_nanos(-999_999_999);
615
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000000001 UTC");
616
        let dt = Utc.timestamp_nanos(-1);
617
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999999 UTC");
618
        let dt = Utc.timestamp_nanos(-60_000_000_000);
619
        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
620
        let dt = Utc.timestamp_nanos(-3_600_000_000_000);
621
        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
622
    }
623
624
    #[test]
625
    fn test_nanos_never_panics() {
626
        Utc.timestamp_nanos(i64::max_value());
627
        Utc.timestamp_nanos(i64::default());
628
        Utc.timestamp_nanos(i64::min_value());
629
    }
630
631
    #[test]
632
    fn test_negative_micros() {
633
        let dt = Utc.timestamp_micros(-1_000_000).unwrap();
634
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
635
        let dt = Utc.timestamp_micros(-999_999).unwrap();
636
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000001 UTC");
637
        let dt = Utc.timestamp_micros(-1).unwrap();
638
        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999 UTC");
639
        let dt = Utc.timestamp_micros(-60_000_000).unwrap();
640
        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
641
        let dt = Utc.timestamp_micros(-3_600_000_000).unwrap();
642
        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
643
    }
644
}