chrono/offset/
mod.rs

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
21use core::fmt;
22
23use crate::format::{parse, ParseResult, Parsed, StrftimeItems};
24use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
25use crate::Weekday;
26#[allow(deprecated)]
27use crate::{Date, DateTime};
28
29pub(crate) mod fixed;
30pub use self::fixed::FixedOffset;
31
32#[cfg(feature = "clock")]
33pub(crate) mod local;
34#[cfg(feature = "clock")]
35pub use self::local::Local;
36
37pub(crate) mod utc;
38pub 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)]
42pub 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
53impl<T> LocalResult<T> {
54    /// Returns `Some` only when the conversion result is unique, or `None` otherwise.
55    #[must_use]
56    pub fn single(self) -> Option<T> {
57        match self {
58            LocalResult::Single(t) => Some(t),
59            _ => None,
60        }
61    }
62
63    /// Returns `Some` for the earliest possible conversion result, or `None` if none.
64    #[must_use]
65    pub fn earliest(self) -> Option<T> {
66        match self {
67            LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t),
68            _ => None,
69        }
70    }
71
72    /// Returns `Some` for the latest possible conversion result, or `None` if none.
73    #[must_use]
74    pub fn latest(self) -> Option<T> {
75        match self {
76            LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t),
77            _ => None,
78        }
79    }
80
81    /// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
82    #[must_use]
83    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
84        match self {
85            LocalResult::None => LocalResult::None,
86            LocalResult::Single(v) => LocalResult::Single(f(v)),
87            LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
88        }
89    }
90}
91
92#[allow(deprecated)]
93impl<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    pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
101        match self {
102            LocalResult::Single(d) => {
103                d.and_time(time).map_or(LocalResult::None, LocalResult::Single)
104            }
105            _ => LocalResult::None,
106        }
107    }
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    pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
116        match self {
117            LocalResult::Single(d) => {
118                d.and_hms_opt(hour, min, sec).map_or(LocalResult::None, LocalResult::Single)
119            }
120            _ => LocalResult::None,
121        }
122    }
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    pub fn and_hms_milli_opt(
132        self,
133        hour: u32,
134        min: u32,
135        sec: u32,
136        milli: u32,
137    ) -> LocalResult<DateTime<Tz>> {
138        match self {
139            LocalResult::Single(d) => d
140                .and_hms_milli_opt(hour, min, sec, milli)
141                .map_or(LocalResult::None, LocalResult::Single),
142            _ => LocalResult::None,
143        }
144    }
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    pub fn and_hms_micro_opt(
154        self,
155        hour: u32,
156        min: u32,
157        sec: u32,
158        micro: u32,
159    ) -> LocalResult<DateTime<Tz>> {
160        match self {
161            LocalResult::Single(d) => d
162                .and_hms_micro_opt(hour, min, sec, micro)
163                .map_or(LocalResult::None, LocalResult::Single),
164            _ => LocalResult::None,
165        }
166    }
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    pub fn and_hms_nano_opt(
176        self,
177        hour: u32,
178        min: u32,
179        sec: u32,
180        nano: u32,
181    ) -> LocalResult<DateTime<Tz>> {
182        match self {
183            LocalResult::Single(d) => d
184                .and_hms_nano_opt(hour, min, sec, nano)
185                .map_or(LocalResult::None, LocalResult::Single),
186            _ => LocalResult::None,
187        }
188    }
189}
190
191impl<T: fmt::Debug> LocalResult<T> {
192    /// Returns the single unique conversion result, or panics accordingly.
193    #[must_use]
194    #[track_caller]
195    pub fn unwrap(self) -> T {
196        match self {
197            LocalResult::None => panic!("No such local time"),
198            LocalResult::Single(t) => t,
199            LocalResult::Ambiguous(t1, t2) => {
200                panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
201            }
202        }
203    }
204}
205
206/// The offset from the local time to UTC.
207pub 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.
215pub 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    fn with_ymd_and_hms(
227        &self,
228        year: i32,
229        month: u32,
230        day: u32,
231        hour: u32,
232        min: u32,
233        sec: u32,
234    ) -> LocalResult<DateTime<Self>> {
235        match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec))
236        {
237            Some(dt) => self.from_local_datetime(&dt),
238            None => LocalResult::None,
239        }
240    }
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    fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
252        self.ymd_opt(year, month, day).unwrap()
253    }
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    fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
265        match NaiveDate::from_ymd_opt(year, month, day) {
266            Some(d) => self.from_local_date(&d),
267            None => LocalResult::None,
268        }
269    }
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    fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
284        self.yo_opt(year, ordinal).unwrap()
285    }
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    fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
300        match NaiveDate::from_yo_opt(year, ordinal) {
301            Some(d) => self.from_local_date(&d),
302            None => LocalResult::None,
303        }
304    }
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    fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
321        self.isoywd_opt(year, week, weekday).unwrap()
322    }
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    fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
339        match NaiveDate::from_isoywd_opt(year, week, weekday) {
340            Some(d) => self.from_local_date(&d),
341            None => LocalResult::None,
342        }
343    }
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    fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
359        self.timestamp_opt(secs, nsecs).unwrap()
360    }
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::{Utc, TimeZone};
379    ///
380    /// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC");
381    /// ```
382    fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> {
383        match NaiveDateTime::from_timestamp_opt(secs, nsecs) {
384            Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
385            None => LocalResult::None,
386        }
387    }
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    fn timestamp_millis(&self, millis: i64) -> DateTime<Self> {
396        self.timestamp_millis_opt(millis).unwrap()
397    }
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::{Utc, TimeZone, LocalResult};
411    /// match Utc.timestamp_millis_opt(1431648000) {
412    ///     LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648),
413    ///     _ => panic!("Incorrect timestamp_millis"),
414    /// };
415    /// ```
416    fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>> {
417        match NaiveDateTime::from_timestamp_millis(millis) {
418            Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
419            None => LocalResult::None,
420        }
421    }
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::{Utc, TimeZone};
432    ///
433    /// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648);
434    /// ```
435    fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self> {
436        let (mut secs, mut nanos) = (nanos / 1_000_000_000, nanos % 1_000_000_000);
437        if nanos < 0 {
438            secs -= 1;
439            nanos += 1_000_000_000;
440        }
441        self.timestamp_opt(secs, nanos as u32).unwrap()
442    }
443
444    /// Makes a new `DateTime` from the number of non-leap microseconds
445    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
446    ///
447    /// # Example
448    ///
449    /// ```
450    /// use chrono::{Utc, TimeZone};
451    ///
452    /// assert_eq!(Utc.timestamp_micros(1431648000000).unwrap().timestamp(), 1431648);
453    /// ```
454    fn timestamp_micros(&self, micros: i64) -> LocalResult<DateTime<Self>> {
455        match NaiveDateTime::from_timestamp_micros(micros) {
456            Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
457            None => LocalResult::None,
458        }
459    }
460
461    /// Parses a string with the specified format string and returns a
462    /// `DateTime` with the current offset.
463    ///
464    /// See the [`crate::format::strftime`] module on the
465    /// supported escape sequences.
466    ///
467    /// If the to-be-parsed string includes an offset, it *must* match the
468    /// offset of the TimeZone, otherwise an error will be returned.
469    ///
470    /// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with
471    /// parsed [`FixedOffset`].
472    ///
473    /// See also [`NaiveDateTime::parse_from_str`] which gives a [`NaiveDateTime`] without
474    /// an offset, but can be converted to a [`DateTime`] with [`NaiveDateTime::and_utc`] or
475    /// [`NaiveDateTime::and_local_timezone`].
476    #[deprecated(
477        since = "0.4.29",
478        note = "use `DateTime::parse_from_str` or `NaiveDateTime::parse_from_str` with `and_utc()` or `and_local_timezone()` instead"
479    )]
480    fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
481        let mut parsed = Parsed::new();
482        parse(&mut parsed, s, StrftimeItems::new(fmt))?;
483        parsed.to_datetime_with_timezone(self)
484    }
485
486    /// Reconstructs the time zone from the offset.
487    fn from_offset(offset: &Self::Offset) -> Self;
488
489    /// Creates the offset(s) for given local `NaiveDate` if possible.
490    fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
491
492    /// Creates the offset(s) for given local `NaiveDateTime` if possible.
493    fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
494
495    /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
496    #[allow(clippy::wrong_self_convention)]
497    #[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")]
498    #[allow(deprecated)]
499    fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
500        self.offset_from_local_date(local).map(|offset| {
501            // since FixedOffset is within +/- 1 day, the date is never affected
502            Date::from_utc(*local, offset)
503        })
504    }
505
506    /// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
507    #[allow(clippy::wrong_self_convention)]
508    fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
509        // Return `LocalResult::None` when the offset pushes a value out of range, instead of
510        // panicking.
511        match self.offset_from_local_datetime(local) {
512            LocalResult::None => LocalResult::None,
513            LocalResult::Single(offset) => match local.checked_sub_offset(offset.fix()) {
514                Some(dt) => LocalResult::Single(DateTime::from_naive_utc_and_offset(dt, offset)),
515                None => LocalResult::None,
516            },
517            LocalResult::Ambiguous(o1, o2) => {
518                match (local.checked_sub_offset(o1.fix()), local.checked_sub_offset(o2.fix())) {
519                    (Some(d1), Some(d2)) => LocalResult::Ambiguous(
520                        DateTime::from_naive_utc_and_offset(d1, o1),
521                        DateTime::from_naive_utc_and_offset(d2, o2),
522                    ),
523                    _ => LocalResult::None,
524                }
525            }
526        }
527    }
528
529    /// Creates the offset for given UTC `NaiveDate`. This cannot fail.
530    fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset;
531
532    /// Creates the offset for given UTC `NaiveDateTime`. This cannot fail.
533    fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset;
534
535    /// Converts the UTC `NaiveDate` to the local time.
536    /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
537    #[allow(clippy::wrong_self_convention)]
538    #[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")]
539    #[allow(deprecated)]
540    fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
541        Date::from_utc(*utc, self.offset_from_utc_date(utc))
542    }
543
544    /// Converts the UTC `NaiveDateTime` to the local time.
545    /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
546    #[allow(clippy::wrong_self_convention)]
547    fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
548        DateTime::from_naive_utc_and_offset(*utc, self.offset_from_utc_datetime(utc))
549    }
550}
551
552#[cfg(test)]
553mod tests {
554    use super::*;
555
556    #[test]
557    fn test_fixed_offset_min_max_dates() {
558        for offset_hour in -23..=23 {
559            dbg!(offset_hour);
560            let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap();
561
562            let local_max = offset.from_utc_datetime(&NaiveDateTime::MAX);
563            assert_eq!(local_max.naive_utc(), NaiveDateTime::MAX);
564            let local_min = offset.from_utc_datetime(&NaiveDateTime::MIN);
565            assert_eq!(local_min.naive_utc(), NaiveDateTime::MIN);
566
567            let local_max = offset.from_local_datetime(&NaiveDateTime::MAX);
568            if offset_hour >= 0 {
569                assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX);
570            } else {
571                assert_eq!(local_max, LocalResult::None);
572            }
573            let local_min = offset.from_local_datetime(&NaiveDateTime::MIN);
574            if offset_hour <= 0 {
575                assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN);
576            } else {
577                assert_eq!(local_min, LocalResult::None);
578            }
579        }
580    }
581
582    #[test]
583    fn test_negative_millis() {
584        let dt = Utc.timestamp_millis_opt(-1000).unwrap();
585        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
586        let dt = Utc.timestamp_millis_opt(-7000).unwrap();
587        assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC");
588        let dt = Utc.timestamp_millis_opt(-7001).unwrap();
589        assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC");
590        let dt = Utc.timestamp_millis_opt(-7003).unwrap();
591        assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC");
592        let dt = Utc.timestamp_millis_opt(-999).unwrap();
593        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC");
594        let dt = Utc.timestamp_millis_opt(-1).unwrap();
595        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC");
596        let dt = Utc.timestamp_millis_opt(-60000).unwrap();
597        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
598        let dt = Utc.timestamp_millis_opt(-3600000).unwrap();
599        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
600
601        for (millis, expected) in &[
602            (-7000, "1969-12-31 23:59:53 UTC"),
603            (-7001, "1969-12-31 23:59:52.999 UTC"),
604            (-7003, "1969-12-31 23:59:52.997 UTC"),
605        ] {
606            match Utc.timestamp_millis_opt(*millis) {
607                LocalResult::Single(dt) => {
608                    assert_eq!(dt.to_string(), *expected);
609                }
610                e => panic!("Got {:?} instead of an okay answer", e),
611            }
612        }
613    }
614
615    #[test]
616    fn test_negative_nanos() {
617        let dt = Utc.timestamp_nanos(-1_000_000_000);
618        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
619        let dt = Utc.timestamp_nanos(-999_999_999);
620        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000000001 UTC");
621        let dt = Utc.timestamp_nanos(-1);
622        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999999 UTC");
623        let dt = Utc.timestamp_nanos(-60_000_000_000);
624        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
625        let dt = Utc.timestamp_nanos(-3_600_000_000_000);
626        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
627    }
628
629    #[test]
630    fn test_nanos_never_panics() {
631        Utc.timestamp_nanos(i64::max_value());
632        Utc.timestamp_nanos(i64::default());
633        Utc.timestamp_nanos(i64::min_value());
634    }
635
636    #[test]
637    fn test_negative_micros() {
638        let dt = Utc.timestamp_micros(-1_000_000).unwrap();
639        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
640        let dt = Utc.timestamp_micros(-999_999).unwrap();
641        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000001 UTC");
642        let dt = Utc.timestamp_micros(-1).unwrap();
643        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999 UTC");
644        let dt = Utc.timestamp_micros(-60_000_000).unwrap();
645        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
646        let dt = Utc.timestamp_micros(-3_600_000_000).unwrap();
647        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
648    }
649}