1 use super::DateTime;
2 use crate::naive::{NaiveDate, NaiveTime};
3 use crate::offset::{FixedOffset, TimeZone, Utc};
4 #[cfg(feature = "clock")]
5 use crate::offset::{Local, Offset};
6 use crate::{Datelike, Days, LocalResult, Months, NaiveDateTime, TimeDelta, Timelike, Weekday};
7 
8 #[derive(Clone)]
9 struct DstTester;
10 
11 impl DstTester {
winter_offset() -> FixedOffset12     fn winter_offset() -> FixedOffset {
13         FixedOffset::east_opt(8 * 60 * 60).unwrap()
14     }
summer_offset() -> FixedOffset15     fn summer_offset() -> FixedOffset {
16         FixedOffset::east_opt(9 * 60 * 60).unwrap()
17     }
18 
19     const TO_WINTER_MONTH_DAY: (u32, u32) = (4, 15);
20     const TO_SUMMER_MONTH_DAY: (u32, u32) = (9, 15);
21 
transition_start_local() -> NaiveTime22     fn transition_start_local() -> NaiveTime {
23         NaiveTime::from_hms_opt(2, 0, 0).unwrap()
24     }
25 }
26 
27 impl TimeZone for DstTester {
28     type Offset = FixedOffset;
29 
from_offset(_: &Self::Offset) -> Self30     fn from_offset(_: &Self::Offset) -> Self {
31         DstTester
32     }
33 
offset_from_local_date(&self, _: &NaiveDate) -> crate::LocalResult<Self::Offset>34     fn offset_from_local_date(&self, _: &NaiveDate) -> crate::LocalResult<Self::Offset> {
35         unimplemented!()
36     }
37 
offset_from_local_datetime( &self, local: &NaiveDateTime, ) -> crate::LocalResult<Self::Offset>38     fn offset_from_local_datetime(
39         &self,
40         local: &NaiveDateTime,
41     ) -> crate::LocalResult<Self::Offset> {
42         let local_to_winter_transition_start = NaiveDate::from_ymd_opt(
43             local.year(),
44             DstTester::TO_WINTER_MONTH_DAY.0,
45             DstTester::TO_WINTER_MONTH_DAY.1,
46         )
47         .unwrap()
48         .and_time(DstTester::transition_start_local());
49 
50         let local_to_winter_transition_end = NaiveDate::from_ymd_opt(
51             local.year(),
52             DstTester::TO_WINTER_MONTH_DAY.0,
53             DstTester::TO_WINTER_MONTH_DAY.1,
54         )
55         .unwrap()
56         .and_time(DstTester::transition_start_local() - TimeDelta::hours(1));
57 
58         let local_to_summer_transition_start = NaiveDate::from_ymd_opt(
59             local.year(),
60             DstTester::TO_SUMMER_MONTH_DAY.0,
61             DstTester::TO_SUMMER_MONTH_DAY.1,
62         )
63         .unwrap()
64         .and_time(DstTester::transition_start_local());
65 
66         let local_to_summer_transition_end = NaiveDate::from_ymd_opt(
67             local.year(),
68             DstTester::TO_SUMMER_MONTH_DAY.0,
69             DstTester::TO_SUMMER_MONTH_DAY.1,
70         )
71         .unwrap()
72         .and_time(DstTester::transition_start_local() + TimeDelta::hours(1));
73 
74         if *local < local_to_winter_transition_end || *local >= local_to_summer_transition_end {
75             LocalResult::Single(DstTester::summer_offset())
76         } else if *local >= local_to_winter_transition_start
77             && *local < local_to_summer_transition_start
78         {
79             LocalResult::Single(DstTester::winter_offset())
80         } else if *local >= local_to_winter_transition_end
81             && *local < local_to_winter_transition_start
82         {
83             LocalResult::Ambiguous(DstTester::winter_offset(), DstTester::summer_offset())
84         } else if *local >= local_to_summer_transition_start
85             && *local < local_to_summer_transition_end
86         {
87             LocalResult::None
88         } else {
89             panic!("Unexpected local time {}", local)
90         }
91     }
92 
offset_from_utc_date(&self, _: &NaiveDate) -> Self::Offset93     fn offset_from_utc_date(&self, _: &NaiveDate) -> Self::Offset {
94         unimplemented!()
95     }
96 
offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset97     fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset {
98         let utc_to_winter_transition = NaiveDate::from_ymd_opt(
99             utc.year(),
100             DstTester::TO_WINTER_MONTH_DAY.0,
101             DstTester::TO_WINTER_MONTH_DAY.1,
102         )
103         .unwrap()
104         .and_time(DstTester::transition_start_local())
105             - DstTester::summer_offset();
106 
107         let utc_to_summer_transition = NaiveDate::from_ymd_opt(
108             utc.year(),
109             DstTester::TO_SUMMER_MONTH_DAY.0,
110             DstTester::TO_SUMMER_MONTH_DAY.1,
111         )
112         .unwrap()
113         .and_time(DstTester::transition_start_local())
114             - DstTester::winter_offset();
115 
116         if *utc < utc_to_winter_transition || *utc >= utc_to_summer_transition {
117             DstTester::summer_offset()
118         } else if *utc >= utc_to_winter_transition && *utc < utc_to_summer_transition {
119             DstTester::winter_offset()
120         } else {
121             panic!("Unexpected utc time {}", utc)
122         }
123     }
124 }
125 
126 #[test]
test_datetime_add_days()127 fn test_datetime_add_days() {
128     let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();
129     let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();
130 
131     assert_eq!(
132         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)),
133         "2014-05-11 07:08:09 -05:00"
134     );
135     assert_eq!(
136         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)),
137         "2014-05-11 07:08:09 +09:00"
138     );
139 
140     assert_eq!(
141         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)),
142         "2014-06-10 07:08:09 -05:00"
143     );
144     assert_eq!(
145         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)),
146         "2014-06-10 07:08:09 +09:00"
147     );
148 
149     assert_eq!(
150         format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(5)),
151         "2014-04-11 07:08:09 +09:00"
152     );
153     assert_eq!(
154         format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(10)),
155         "2014-04-16 07:08:09 +08:00"
156     );
157 
158     assert_eq!(
159         format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(5)),
160         "2014-09-11 07:08:09 +08:00"
161     );
162     assert_eq!(
163         format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(10)),
164         "2014-09-16 07:08:09 +09:00"
165     );
166 }
167 
168 #[test]
test_datetime_sub_days()169 fn test_datetime_sub_days() {
170     let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();
171     let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();
172 
173     assert_eq!(
174         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)),
175         "2014-05-01 07:08:09 -05:00"
176     );
177     assert_eq!(
178         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)),
179         "2014-05-01 07:08:09 +09:00"
180     );
181 
182     assert_eq!(
183         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)),
184         "2014-04-01 07:08:09 -05:00"
185     );
186     assert_eq!(
187         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)),
188         "2014-04-01 07:08:09 +09:00"
189     );
190 }
191 
192 #[test]
test_datetime_add_months()193 fn test_datetime_add_months() {
194     let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();
195     let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();
196 
197     assert_eq!(
198         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)),
199         "2014-06-06 07:08:09 -05:00"
200     );
201     assert_eq!(
202         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)),
203         "2014-06-06 07:08:09 +09:00"
204     );
205 
206     assert_eq!(
207         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)),
208         "2014-10-06 07:08:09 -05:00"
209     );
210     assert_eq!(
211         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)),
212         "2014-10-06 07:08:09 +09:00"
213     );
214 }
215 
216 #[test]
test_datetime_sub_months()217 fn test_datetime_sub_months() {
218     let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();
219     let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();
220 
221     assert_eq!(
222         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)),
223         "2014-04-06 07:08:09 -05:00"
224     );
225     assert_eq!(
226         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)),
227         "2014-04-06 07:08:09 +09:00"
228     );
229 
230     assert_eq!(
231         format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)),
232         "2013-12-06 07:08:09 -05:00"
233     );
234     assert_eq!(
235         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)),
236         "2013-12-06 07:08:09 +09:00"
237     );
238 }
239 
240 // local helper function to easily create a DateTime<FixedOffset>
241 #[allow(clippy::too_many_arguments)]
ymdhms( fixedoffset: &FixedOffset, year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, ) -> DateTime<FixedOffset>242 fn ymdhms(
243     fixedoffset: &FixedOffset,
244     year: i32,
245     month: u32,
246     day: u32,
247     hour: u32,
248     min: u32,
249     sec: u32,
250 ) -> DateTime<FixedOffset> {
251     fixedoffset.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap()
252 }
253 
254 // local helper function to easily create a DateTime<FixedOffset>
255 #[allow(clippy::too_many_arguments)]
ymdhms_milli( fixedoffset: &FixedOffset, year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, milli: u32, ) -> DateTime<FixedOffset>256 fn ymdhms_milli(
257     fixedoffset: &FixedOffset,
258     year: i32,
259     month: u32,
260     day: u32,
261     hour: u32,
262     min: u32,
263     sec: u32,
264     milli: u32,
265 ) -> DateTime<FixedOffset> {
266     fixedoffset
267         .with_ymd_and_hms(year, month, day, hour, min, sec)
268         .unwrap()
269         .with_nanosecond(milli * 1_000_000)
270         .unwrap()
271 }
272 
273 // local helper function to easily create a DateTime<FixedOffset>
274 #[allow(clippy::too_many_arguments)]
275 #[cfg(feature = "alloc")]
ymdhms_micro( fixedoffset: &FixedOffset, year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, micro: u32, ) -> DateTime<FixedOffset>276 fn ymdhms_micro(
277     fixedoffset: &FixedOffset,
278     year: i32,
279     month: u32,
280     day: u32,
281     hour: u32,
282     min: u32,
283     sec: u32,
284     micro: u32,
285 ) -> DateTime<FixedOffset> {
286     fixedoffset
287         .with_ymd_and_hms(year, month, day, hour, min, sec)
288         .unwrap()
289         .with_nanosecond(micro * 1000)
290         .unwrap()
291 }
292 
293 // local helper function to easily create a DateTime<FixedOffset>
294 #[allow(clippy::too_many_arguments)]
295 #[cfg(feature = "alloc")]
ymdhms_nano( fixedoffset: &FixedOffset, year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, nano: u32, ) -> DateTime<FixedOffset>296 fn ymdhms_nano(
297     fixedoffset: &FixedOffset,
298     year: i32,
299     month: u32,
300     day: u32,
301     hour: u32,
302     min: u32,
303     sec: u32,
304     nano: u32,
305 ) -> DateTime<FixedOffset> {
306     fixedoffset
307         .with_ymd_and_hms(year, month, day, hour, min, sec)
308         .unwrap()
309         .with_nanosecond(nano)
310         .unwrap()
311 }
312 
313 // local helper function to easily create a DateTime<Utc>
314 #[cfg(feature = "alloc")]
ymdhms_utc(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Utc>315 fn ymdhms_utc(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Utc> {
316     Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap()
317 }
318 
319 // local helper function to easily create a DateTime<Utc>
ymdhms_milli_utc( year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32, milli: u32, ) -> DateTime<Utc>320 fn ymdhms_milli_utc(
321     year: i32,
322     month: u32,
323     day: u32,
324     hour: u32,
325     min: u32,
326     sec: u32,
327     milli: u32,
328 ) -> DateTime<Utc> {
329     Utc.with_ymd_and_hms(year, month, day, hour, min, sec)
330         .unwrap()
331         .with_nanosecond(milli * 1_000_000)
332         .unwrap()
333 }
334 
335 #[test]
test_datetime_offset()336 fn test_datetime_offset() {
337     let est = FixedOffset::west_opt(5 * 60 * 60).unwrap();
338     let edt = FixedOffset::west_opt(4 * 60 * 60).unwrap();
339     let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();
340 
341     assert_eq!(
342         format!("{}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
343         "2014-05-06 07:08:09 UTC"
344     );
345     assert_eq!(
346         format!("{}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
347         "2014-05-06 07:08:09 -04:00"
348     );
349     assert_eq!(
350         format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
351         "2014-05-06 07:08:09 +09:00"
352     );
353     assert_eq!(
354         format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
355         "2014-05-06T07:08:09Z"
356     );
357     assert_eq!(
358         format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
359         "2014-05-06T07:08:09-04:00"
360     );
361     assert_eq!(
362         format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
363         "2014-05-06T07:08:09+09:00"
364     );
365 
366     // edge cases
367     assert_eq!(
368         format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()),
369         "2014-05-06T00:00:00Z"
370     );
371     assert_eq!(
372         format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()),
373         "2014-05-06T00:00:00-04:00"
374     );
375     assert_eq!(
376         format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()),
377         "2014-05-06T00:00:00+09:00"
378     );
379     assert_eq!(
380         format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()),
381         "2014-05-06T23:59:59Z"
382     );
383     assert_eq!(
384         format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()),
385         "2014-05-06T23:59:59-04:00"
386     );
387     assert_eq!(
388         format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()),
389         "2014-05-06T23:59:59+09:00"
390     );
391 
392     let dt = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
393     assert_eq!(dt, edt.with_ymd_and_hms(2014, 5, 6, 3, 8, 9).unwrap());
394     assert_eq!(
395         dt + TimeDelta::seconds(3600 + 60 + 1),
396         Utc.with_ymd_and_hms(2014, 5, 6, 8, 9, 10).unwrap()
397     );
398     assert_eq!(
399         dt.signed_duration_since(edt.with_ymd_and_hms(2014, 5, 6, 10, 11, 12).unwrap()),
400         TimeDelta::seconds(-7 * 3600 - 3 * 60 - 3)
401     );
402 
403     assert_eq!(*Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), Utc);
404     assert_eq!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), edt);
405     assert!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset() != est);
406 }
407 
408 #[test]
409 #[allow(clippy::needless_borrow, clippy::op_ref)]
signed_duration_since_autoref()410 fn signed_duration_since_autoref() {
411     let dt1 = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
412     let dt2 = Utc.with_ymd_and_hms(2014, 3, 4, 5, 6, 7).unwrap();
413     let diff1 = dt1.signed_duration_since(dt2); // Copy/consume
414     #[allow(clippy::needless_borrows_for_generic_args)]
415     let diff2 = dt2.signed_duration_since(&dt1); // Take by reference
416     assert_eq!(diff1, -diff2);
417 
418     let diff1 = dt1 - &dt2; // We can choose to substract rhs by reference
419     let diff2 = dt2 - dt1; // Or consume rhs
420     assert_eq!(diff1, -diff2);
421 }
422 
423 #[test]
test_datetime_date_and_time()424 fn test_datetime_date_and_time() {
425     let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap();
426     let d = tz.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
427     assert_eq!(d.time(), NaiveTime::from_hms_opt(7, 8, 9).unwrap());
428     assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2014, 5, 6).unwrap());
429 
430     let tz = FixedOffset::east_opt(4 * 60 * 60).unwrap();
431     let d = tz.with_ymd_and_hms(2016, 5, 4, 3, 2, 1).unwrap();
432     assert_eq!(d.time(), NaiveTime::from_hms_opt(3, 2, 1).unwrap());
433     assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2016, 5, 4).unwrap());
434 
435     let tz = FixedOffset::west_opt(13 * 60 * 60).unwrap();
436     let d = tz.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap();
437     assert_eq!(d.time(), NaiveTime::from_hms_opt(12, 34, 56).unwrap());
438     assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2017, 8, 9).unwrap());
439 
440     let utc_d = Utc.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap();
441     assert!(utc_d < d);
442 }
443 
444 #[test]
445 #[cfg(feature = "clock")]
test_datetime_with_timezone()446 fn test_datetime_with_timezone() {
447     let local_now = Local::now();
448     let utc_now = local_now.with_timezone(&Utc);
449     let local_now2 = utc_now.with_timezone(&Local);
450     assert_eq!(local_now, local_now2);
451 }
452 
453 #[test]
454 #[cfg(feature = "alloc")]
test_datetime_rfc2822()455 fn test_datetime_rfc2822() {
456     let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap();
457 
458     // timezone 0
459     assert_eq!(
460         Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(),
461         "Wed, 18 Feb 2015 23:16:09 +0000"
462     );
463     assert_eq!(
464         Utc.with_ymd_and_hms(2015, 2, 1, 23, 16, 9).unwrap().to_rfc2822(),
465         "Sun, 1 Feb 2015 23:16:09 +0000"
466     );
467     // timezone +05
468     assert_eq!(
469         edt.from_local_datetime(
470             &NaiveDate::from_ymd_opt(2015, 2, 18)
471                 .unwrap()
472                 .and_hms_milli_opt(23, 16, 9, 150)
473                 .unwrap()
474         )
475         .unwrap()
476         .to_rfc2822(),
477         "Wed, 18 Feb 2015 23:16:09 +0500"
478     );
479     assert_eq!(
480         DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"),
481         Ok(edt
482             .from_local_datetime(
483                 &NaiveDate::from_ymd_opt(2015, 2, 18)
484                     .unwrap()
485                     .and_hms_milli_opt(23, 59, 59, 1_000)
486                     .unwrap()
487             )
488             .unwrap())
489     );
490     assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err());
491     assert_eq!(
492         DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"),
493         Ok(edt
494             .from_local_datetime(
495                 &NaiveDate::from_ymd_opt(2015, 2, 18)
496                     .unwrap()
497                     .and_hms_micro_opt(23, 59, 59, 1_234_567)
498                     .unwrap()
499             )
500             .unwrap())
501     );
502     // seconds 60
503     assert_eq!(
504         edt.from_local_datetime(
505             &NaiveDate::from_ymd_opt(2015, 2, 18)
506                 .unwrap()
507                 .and_hms_micro_opt(23, 59, 59, 1_234_567)
508                 .unwrap()
509         )
510         .unwrap()
511         .to_rfc2822(),
512         "Wed, 18 Feb 2015 23:59:60 +0500"
513     );
514 
515     assert_eq!(
516         DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"),
517         Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap())
518     );
519     assert_eq!(
520         DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"),
521         Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap())
522     );
523     assert_eq!(
524         ymdhms_micro(&edt, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc2822(),
525         "Wed, 18 Feb 2015 23:59:60 +0500"
526     );
527     assert_eq!(
528         DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:58 +0500"),
529         Ok(ymdhms(&edt, 2015, 2, 18, 23, 59, 58))
530     );
531     assert_ne!(
532         DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:58 +0500"),
533         Ok(ymdhms_milli(&edt, 2015, 2, 18, 23, 59, 58, 500))
534     );
535 
536     // many varying whitespace intermixed
537     assert_eq!(
538         DateTime::parse_from_rfc2822(
539             "\t\t\tWed,\n\t\t18 \r\n\t\tFeb \u{3000} 2015\r\n\t\t\t23:59:58    \t+0500"
540         ),
541         Ok(ymdhms(&edt, 2015, 2, 18, 23, 59, 58))
542     );
543     // example from RFC 2822 Appendix A.5.
544     assert_eq!(
545         DateTime::parse_from_rfc2822(
546             "Thu,\n\t13\n      Feb\n        1969\n    23:32\n             -0330 (Newfoundland Time)"
547         ),
548         Ok(
549             ymdhms(
550                 &FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60).unwrap(),
551                 1969, 2, 13, 23, 32, 0,
552             )
553         )
554     );
555     // example from RFC 2822 Appendix A.5. without trailing " (Newfoundland Time)"
556     assert_eq!(
557         DateTime::parse_from_rfc2822(
558             "Thu,\n\t13\n      Feb\n        1969\n    23:32\n             -0330"
559         ),
560         Ok(
561             ymdhms(&FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60).unwrap(), 1969, 2, 13, 23, 32, 0,)
562         )
563     );
564 
565     // bad year
566     assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err());
567     // wrong format
568     assert!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +00:00").is_err());
569     // full name day of week
570     assert!(DateTime::parse_from_rfc2822("Wednesday, 18 Feb 2015 23:16:09 +0000").is_err());
571     // full name day of week
572     assert!(DateTime::parse_from_rfc2822("Wednesday 18 Feb 2015 23:16:09 +0000").is_err());
573     // wrong day of week separator '.'
574     assert!(DateTime::parse_from_rfc2822("Wed. 18 Feb 2015 23:16:09 +0000").is_err());
575     // *trailing* space causes failure
576     assert!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000   ").is_err());
577 }
578 
579 #[test]
580 #[cfg(feature = "alloc")]
test_datetime_rfc3339()581 fn test_datetime_rfc3339() {
582     let edt5 = FixedOffset::east_opt(5 * 60 * 60).unwrap();
583     let edt0 = FixedOffset::east_opt(0).unwrap();
584 
585     // timezone 0
586     assert_eq!(
587         Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(),
588         "2015-02-18T23:16:09+00:00"
589     );
590     // timezone +05
591     assert_eq!(
592         edt5.from_local_datetime(
593             &NaiveDate::from_ymd_opt(2015, 2, 18)
594                 .unwrap()
595                 .and_hms_milli_opt(23, 16, 9, 150)
596                 .unwrap()
597         )
598         .unwrap()
599         .to_rfc3339(),
600         "2015-02-18T23:16:09.150+05:00"
601     );
602 
603     assert_eq!(ymdhms_utc(2015, 2, 18, 23, 16, 9).to_rfc3339(), "2015-02-18T23:16:09+00:00");
604     assert_eq!(
605         ymdhms_milli(&edt5, 2015, 2, 18, 23, 16, 9, 150).to_rfc3339(),
606         "2015-02-18T23:16:09.150+05:00"
607     );
608     assert_eq!(
609         ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(),
610         "2015-02-18T23:59:60.234567+05:00"
611     );
612     assert_eq!(
613         DateTime::parse_from_rfc3339("2015-02-18T23:59:59.123+05:00"),
614         Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 123_000))
615     );
616     assert_eq!(
617         DateTime::parse_from_rfc3339("2015-02-18T23:59:59.123456+05:00"),
618         Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 123_456))
619     );
620     assert_eq!(
621         DateTime::parse_from_rfc3339("2015-02-18T23:59:59.123456789+05:00"),
622         Ok(ymdhms_nano(&edt5, 2015, 2, 18, 23, 59, 59, 123_456_789))
623     );
624     assert_eq!(
625         DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"),
626         Ok(ymdhms(&edt0, 2015, 2, 18, 23, 16, 9))
627     );
628 
629     assert_eq!(
630         ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(),
631         "2015-02-18T23:59:60.234567+05:00"
632     );
633     assert_eq!(
634         ymdhms_milli(&edt5, 2015, 2, 18, 23, 16, 9, 150).to_rfc3339(),
635         "2015-02-18T23:16:09.150+05:00"
636     );
637     assert_eq!(
638         DateTime::parse_from_rfc3339("2015-02-18T00:00:00.234567+05:00"),
639         Ok(ymdhms_micro(&edt5, 2015, 2, 18, 0, 0, 0, 234_567))
640     );
641     assert_eq!(
642         DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"),
643         Ok(ymdhms(&edt0, 2015, 2, 18, 23, 16, 9))
644     );
645     assert_eq!(
646         DateTime::parse_from_rfc3339("2015-02-18 23:59:60.234567+05:00"),
647         Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567))
648     );
649     assert_eq!(ymdhms_utc(2015, 2, 18, 23, 16, 9).to_rfc3339(), "2015-02-18T23:16:09+00:00");
650 
651     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567 +05:00").is_err());
652     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:059:60.234567+05:00").is_err());
653     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00PST").is_err());
654     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+PST").is_err());
655     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567PST").is_err());
656     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+0500").is_err());
657     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00:00").is_err());
658     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567:+05:00").is_err());
659     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00 ").is_err());
660     assert!(DateTime::parse_from_rfc3339(" 2015-02-18T23:59:60.234567+05:00").is_err());
661     assert!(DateTime::parse_from_rfc3339("2015- 02-18T23:59:60.234567+05:00").is_err());
662     assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567A+05:00").is_err());
663 }
664 
665 #[test]
666 #[cfg(feature = "alloc")]
test_rfc3339_opts()667 fn test_rfc3339_opts() {
668     use crate::SecondsFormat::*;
669     let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
670     let dt = pst
671         .from_local_datetime(
672             &NaiveDate::from_ymd_opt(2018, 1, 11)
673                 .unwrap()
674                 .and_hms_nano_opt(10, 5, 13, 84_660_000)
675                 .unwrap(),
676         )
677         .unwrap();
678     assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00");
679     assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00");
680     assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00");
681     assert_eq!(dt.to_rfc3339_opts(Micros, false), "2018-01-11T10:05:13.084660+08:00");
682     assert_eq!(dt.to_rfc3339_opts(Nanos, false), "2018-01-11T10:05:13.084660000+08:00");
683     assert_eq!(dt.to_rfc3339_opts(AutoSi, false), "2018-01-11T10:05:13.084660+08:00");
684 
685     let ut = dt.naive_utc().and_utc();
686     assert_eq!(ut.to_rfc3339_opts(Secs, false), "2018-01-11T02:05:13+00:00");
687     assert_eq!(ut.to_rfc3339_opts(Secs, true), "2018-01-11T02:05:13Z");
688     assert_eq!(ut.to_rfc3339_opts(Millis, false), "2018-01-11T02:05:13.084+00:00");
689     assert_eq!(ut.to_rfc3339_opts(Millis, true), "2018-01-11T02:05:13.084Z");
690     assert_eq!(ut.to_rfc3339_opts(Micros, true), "2018-01-11T02:05:13.084660Z");
691     assert_eq!(ut.to_rfc3339_opts(Nanos, true), "2018-01-11T02:05:13.084660000Z");
692     assert_eq!(ut.to_rfc3339_opts(AutoSi, true), "2018-01-11T02:05:13.084660Z");
693 }
694 
695 #[test]
696 #[should_panic]
697 #[cfg(feature = "alloc")]
test_rfc3339_opts_nonexhaustive()698 fn test_rfc3339_opts_nonexhaustive() {
699     use crate::SecondsFormat;
700     let dt = Utc.with_ymd_and_hms(1999, 10, 9, 1, 2, 3).unwrap();
701     let _ = dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true);
702 }
703 
704 #[test]
test_datetime_from_str()705 fn test_datetime_from_str() {
706     assert_eq!(
707         "2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
708         Ok(FixedOffset::east_opt(0)
709             .unwrap()
710             .from_local_datetime(
711                 &NaiveDate::from_ymd_opt(2015, 2, 18)
712                     .unwrap()
713                     .and_hms_milli_opt(23, 16, 9, 150)
714                     .unwrap()
715             )
716             .unwrap())
717     );
718     assert_eq!(
719         "2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
720         Ok(Utc
721             .from_local_datetime(
722                 &NaiveDate::from_ymd_opt(2015, 2, 18)
723                     .unwrap()
724                     .and_hms_milli_opt(23, 16, 9, 150)
725                     .unwrap()
726             )
727             .unwrap())
728     );
729     assert_eq!(
730         "2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(),
731         Ok(Utc
732             .from_local_datetime(
733                 &NaiveDate::from_ymd_opt(2015, 2, 18)
734                     .unwrap()
735                     .and_hms_milli_opt(23, 16, 9, 150)
736                     .unwrap()
737             )
738             .unwrap())
739     );
740     assert_eq!(
741         "2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(),
742         Ok(Utc
743             .from_local_datetime(
744                 &NaiveDate::from_ymd_opt(2015, 2, 18)
745                     .unwrap()
746                     .and_hms_milli_opt(23, 16, 9, 150)
747                     .unwrap()
748             )
749             .unwrap())
750     );
751     assert_eq!(
752         "2015-02-18T23:16:9.15Utc".parse::<DateTime<Utc>>(),
753         Ok(Utc
754             .from_local_datetime(
755                 &NaiveDate::from_ymd_opt(2015, 2, 18)
756                     .unwrap()
757                     .and_hms_milli_opt(23, 16, 9, 150)
758                     .unwrap()
759             )
760             .unwrap())
761     );
762 
763     assert_eq!(
764         "2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
765         Ok(FixedOffset::east_opt(0)
766             .unwrap()
767             .from_local_datetime(
768                 &NaiveDate::from_ymd_opt(2015, 2, 18)
769                     .unwrap()
770                     .and_hms_milli_opt(23, 16, 9, 150)
771                     .unwrap()
772             )
773             .unwrap())
774     );
775     assert_eq!(
776         "2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
777         Ok(FixedOffset::west_opt(10 * 3600)
778             .unwrap()
779             .from_local_datetime(
780                 &NaiveDate::from_ymd_opt(2015, 2, 18)
781                     .unwrap()
782                     .and_hms_milli_opt(13, 16, 9, 150)
783                     .unwrap()
784             )
785             .unwrap())
786     );
787     assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
788 
789     assert_eq!(
790         "2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
791         Ok(Utc
792             .from_local_datetime(
793                 &NaiveDate::from_ymd_opt(2015, 2, 18)
794                     .unwrap()
795                     .and_hms_milli_opt(23, 16, 9, 150)
796                     .unwrap()
797             )
798             .unwrap())
799     );
800     assert_eq!(
801         "2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(),
802         Ok(Utc
803             .from_local_datetime(
804                 &NaiveDate::from_ymd_opt(2015, 2, 18)
805                     .unwrap()
806                     .and_hms_milli_opt(23, 16, 9, 150)
807                     .unwrap()
808             )
809             .unwrap())
810     );
811     assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
812     assert!("2015-02-18T23:16:9.15øøø".parse::<DateTime<Utc>>().is_err());
813 
814     // no test for `DateTime<Local>`, we cannot verify that much.
815 }
816 
817 #[test]
test_parse_datetime_utc()818 fn test_parse_datetime_utc() {
819     // valid cases
820     let valid = [
821         "2001-02-03T04:05:06Z",
822         "2001-02-03T04:05:06+0000",
823         "2001-02-03T04:05:06-00:00",
824         "2001-02-03T04:05:06-01:00",
825         "2012-12-12 12:12:12Z",
826         "2012-12-12t12:12:12Z",
827         "2012-12-12T12:12:12Z",
828         "2012 -12-12T12:12:12Z",
829         "2012  -12-12T12:12:12Z",
830         "2012- 12-12T12:12:12Z",
831         "2012-  12-12T12:12:12Z",
832         "2012-12-12T 12:12:12Z",
833         "2012-12-12T12 :12:12Z",
834         "2012-12-12T12  :12:12Z",
835         "2012-12-12T12: 12:12Z",
836         "2012-12-12T12:  12:12Z",
837         "2012-12-12T12 : 12:12Z",
838         "2012-12-12T12:12:12Z ",
839         " 2012-12-12T12:12:12Z",
840         "2015-02-18T23:16:09.153Z",
841         "2015-2-18T23:16:09.153Z",
842         "+2015-2-18T23:16:09.153Z",
843         "-77-02-18T23:16:09Z",
844         "+82701-05-6T15:9:60.898989898989Z",
845     ];
846     for &s in &valid {
847         eprintln!("test_parse_datetime_utc valid {:?}", s);
848         let d = match s.parse::<DateTime<Utc>>() {
849             Ok(d) => d,
850             Err(e) => panic!("parsing `{}` has failed: {}", s, e),
851         };
852         let s_ = format!("{:?}", d);
853         // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
854         let d_ = match s_.parse::<DateTime<Utc>>() {
855             Ok(d) => d,
856             Err(e) => {
857                 panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
858             }
859         };
860         assert!(
861             d == d_,
862             "`{}` is parsed into `{:?}`, but reparsed result `{:?}` does not match",
863             s,
864             d,
865             d_
866         );
867     }
868 
869     // some invalid cases
870     // since `ParseErrorKind` is private, all we can do is to check if there was an error
871     let invalid = [
872         "",                                                          // empty
873         "Z",                                                         // missing data
874         "15Z",                                                       // missing data
875         "15:8:9Z",                                                   // missing date
876         "15-8-9Z",                                                   // missing time or date
877         "Fri, 09 Aug 2013 23:54:35 GMT",                             // valid datetime, wrong format
878         "Sat Jun 30 23:59:60 2012",                                  // valid datetime, wrong format
879         "1441497364.649",                                            // valid datetime, wrong format
880         "+1441497364.649",                                           // valid datetime, wrong format
881         "+1441497364",                                               // valid datetime, wrong format
882         "+1441497364Z",                                              // valid datetime, wrong format
883         "2014/02/03 04:05:06Z",                                      // valid datetime, wrong format
884         "2001-02-03T04:05:0600:00", // valid datetime, timezone too close
885         "2015-15-15T15:15:15Z",     // invalid datetime
886         "2012-12-12T12:12:12x",     // invalid timezone
887         "2012-123-12T12:12:12Z",    // invalid month
888         "2012-12-77T12:12:12Z",     // invalid day
889         "2012-12-12T26:12:12Z",     // invalid hour
890         "2012-12-12T12:61:12Z",     // invalid minute
891         "2012-12-12T12:12:62Z",     // invalid second
892         "2012-12-12 T12:12:12Z",    // space after date
893         "2012-12-12T12:12:12ZZ",    // trailing literal 'Z'
894         "+802701-12-12T12:12:12Z",  // invalid year (out of bounds)
895         "+ 2012-12-12T12:12:12Z",   // invalid space before year
896         "  +82701  -  05  -  6  T  15  :  9  : 60.898989898989   Z", // valid datetime, wrong format
897     ];
898     for &s in &invalid {
899         eprintln!("test_parse_datetime_utc invalid {:?}", s);
900         assert!(s.parse::<DateTime<Utc>>().is_err());
901     }
902 }
903 
904 #[test]
test_parse_from_str()905 fn test_parse_from_str() {
906     let edt = FixedOffset::east_opt(570 * 60).unwrap();
907     let edt0 = FixedOffset::east_opt(0).unwrap();
908     let wdt = FixedOffset::west_opt(10 * 3600).unwrap();
909     assert_eq!(
910         DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
911         Ok(ymdhms(&edt, 2014, 5, 7, 12, 34, 56))
912     ); // ignore offset
913     assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset
914     assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT")
915         .is_err());
916     assert_eq!(
917         DateTime::parse_from_str("0", "%s").unwrap(),
918         NaiveDateTime::from_timestamp_opt(0, 0).unwrap().and_utc().fixed_offset()
919     );
920 
921     assert_eq!(
922         "2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
923         Ok(ymdhms_milli(&edt0, 2015, 2, 18, 23, 16, 9, 150))
924     );
925     assert_eq!(
926         "2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
927         Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)),
928     );
929     assert_eq!(
930         "2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(),
931         Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150))
932     );
933     assert_eq!(
934         "2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(),
935         Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150))
936     );
937 
938     assert_eq!(
939         "2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
940         Ok(ymdhms_milli(&edt0, 2015, 2, 18, 23, 16, 9, 150))
941     );
942     assert_eq!(
943         "2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
944         Ok(ymdhms_milli(&wdt, 2015, 2, 18, 13, 16, 9, 150))
945     );
946     assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
947 
948     assert_eq!(
949         "2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
950         Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150))
951     );
952     assert_eq!(
953         "2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(),
954         Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150))
955     );
956     assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
957 
958     // no test for `DateTime<Local>`, we cannot verify that much.
959 }
960 
961 #[test]
test_datetime_parse_from_str()962 fn test_datetime_parse_from_str() {
963     let dt = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35);
964     let parse = DateTime::parse_from_str;
965 
966     // timezone variations
967 
968     //
969     // %Z
970     //
971     // wrong timezone format
972     assert!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %Z").is_err());
973     // bad timezone data?
974     assert!(parse("Aug 09 2013 23:54:35 PST", "%b %d %Y %H:%M:%S %Z").is_err());
975     // bad timezone data
976     assert!(parse("Aug 09 2013 23:54:35 XXXXX", "%b %d %Y %H:%M:%S %Z").is_err());
977 
978     //
979     // %z
980     //
981     assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %z"), Ok(dt));
982     assert_eq!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %z"), Ok(dt));
983     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %z"), Ok(dt));
984     assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %z"), Ok(dt));
985     assert_eq!(parse("Aug 09 2013 23:54:35 --0900", "%b %d %Y %H:%M:%S -%z"), Ok(dt));
986     assert_eq!(parse("Aug 09 2013 23:54:35 +-0900", "%b %d %Y %H:%M:%S +%z"), Ok(dt));
987     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00 ", "%b %d %Y %H:%M:%S %z "), Ok(dt));
988     // trailing newline after timezone
989     assert!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z").is_err());
990     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z "), Ok(dt));
991     // trailing colon
992     assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %z").is_err());
993     // trailing colon with space
994     assert!(parse("Aug 09 2013 23:54:35 -09:00: ", "%b %d %Y %H:%M:%S %z ").is_err());
995     // trailing colon, mismatch space
996     assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %z ").is_err());
997     // wrong timezone data
998     assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %z").is_err());
999     assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %z"), Ok(dt));
1000     assert_eq!(parse("Aug 09 2013 23:54:35 -0900::", "%b %d %Y %H:%M:%S %z::"), Ok(dt));
1001     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %z:00"), Ok(dt));
1002     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00:00 ", "%b %d %Y %H:%M:%S %z:00 "), Ok(dt));
1003 
1004     //
1005     // %:z
1006     //
1007     assert_eq!(parse("Aug 09 2013 23:54:35  -09:00", "%b %d %Y %H:%M:%S  %:z"), Ok(dt));
1008     assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %:z"), Ok(dt));
1009     assert_eq!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %:z"), Ok(dt));
1010     assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %:z"), Ok(dt));
1011     assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00:", "%b %d %Y %H:%M:%S %:z:"), Ok(dt));
1012     // wrong timezone data
1013     assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %:z").is_err());
1014     assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %:z"), Ok(dt));
1015     // timezone data hs too many colons
1016     assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %:z").is_err());
1017     // timezone data hs too many colons
1018     assert!(parse("Aug 09 2013 23:54:35 -09:00::", "%b %d %Y %H:%M:%S %:z").is_err());
1019     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00::", "%b %d %Y %H:%M:%S %:z::"), Ok(dt));
1020 
1021     //
1022     // %::z
1023     //
1024     assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %::z"), Ok(dt));
1025     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %::z"), Ok(dt));
1026     assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %::z"), Ok(dt));
1027     // mismatching colon expectations
1028     assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::z").is_err());
1029     assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %::z"), Ok(dt));
1030     assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %:z"), Ok(dt));
1031     // wrong timezone data
1032     assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %::z").is_err());
1033     assert_eq!(parse("Aug 09 2013 23:54:35 -09001234", "%b %d %Y %H:%M:%S %::z1234"), Ok(dt));
1034     assert_eq!(parse("Aug 09 2013 23:54:35 -09:001234", "%b %d %Y %H:%M:%S %::z1234"), Ok(dt));
1035     assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%b %d %Y %H:%M:%S %::z "), Ok(dt));
1036     assert_eq!(parse("Aug 09 2013 23:54:35 -0900\t\n", "%b %d %Y %H:%M:%S %::z\t\n"), Ok(dt));
1037     assert_eq!(parse("Aug 09 2013 23:54:35 -0900:", "%b %d %Y %H:%M:%S %::z:"), Ok(dt));
1038     assert_eq!(parse("Aug 09 2013 23:54:35 :-0900:0", "%b %d %Y %H:%M:%S :%::z:0"), Ok(dt));
1039     // mismatching colons and spaces
1040     assert!(parse("Aug 09 2013 23:54:35 :-0900: ", "%b %d %Y %H:%M:%S :%::z::").is_err());
1041     // mismatching colons expectations
1042     assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::z").is_err());
1043     assert_eq!(parse("Aug 09 2013 -0900: 23:54:35", "%b %d %Y %::z: %H:%M:%S"), Ok(dt));
1044     assert_eq!(parse("Aug 09 2013 :-0900:0 23:54:35", "%b %d %Y :%::z:0 %H:%M:%S"), Ok(dt));
1045     // mismatching colons expectations mid-string
1046     assert!(parse("Aug 09 2013 :-0900: 23:54:35", "%b %d %Y :%::z  %H:%M:%S").is_err());
1047     // mismatching colons expectations, before end
1048     assert!(parse("Aug 09 2013 23:54:35 -09:00:00 ", "%b %d %Y %H:%M:%S %::z ").is_err());
1049 
1050     //
1051     // %:::z
1052     //
1053     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %:::z"), Ok(dt));
1054     assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %:::z"), Ok(dt));
1055     assert_eq!(parse("Aug 09 2013 23:54:35 -0900  ", "%b %d %Y %H:%M:%S %:::z  "), Ok(dt));
1056     // wrong timezone data
1057     assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %:::z").is_err());
1058 
1059     //
1060     // %::::z
1061     //
1062     // too many colons
1063     assert!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %::::z").is_err());
1064     // too many colons
1065     assert!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %::::z").is_err());
1066     // too many colons
1067     assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %::::z").is_err());
1068     // too many colons
1069     assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::::z").is_err());
1070 
1071     //
1072     // %#z
1073     //
1074     assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %#z"), Ok(dt));
1075     assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %#z"), Ok(dt));
1076     assert_eq!(parse("Aug 09 2013 23:54:35  -09:00  ", "%b %d %Y %H:%M:%S  %#z  "), Ok(dt));
1077     assert_eq!(parse("Aug 09 2013 23:54:35  -0900  ", "%b %d %Y %H:%M:%S  %#z  "), Ok(dt));
1078     assert_eq!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %#z"), Ok(dt));
1079     assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %#z"), Ok(dt));
1080     assert_eq!(parse("Aug 09 2013 23:54:35 -09:", "%b %d %Y %H:%M:%S %#z"), Ok(dt));
1081     assert_eq!(parse("Aug 09 2013 23:54:35 -09: ", "%b %d %Y %H:%M:%S %#z "), Ok(dt));
1082     assert_eq!(parse("Aug 09 2013 23:54:35+-09", "%b %d %Y %H:%M:%S+%#z"), Ok(dt));
1083     assert_eq!(parse("Aug 09 2013 23:54:35--09", "%b %d %Y %H:%M:%S-%#z"), Ok(dt));
1084     assert_eq!(parse("Aug 09 2013 -09:00 23:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt));
1085     assert_eq!(parse("Aug 09 2013 -0900 23:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt));
1086     assert_eq!(parse("Aug 09 2013 -090023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt));
1087     assert_eq!(parse("Aug 09 2013 -09:0023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt));
1088     // timezone with partial minutes adjacent hours
1089     assert_ne!(parse("Aug 09 2013 -09023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt));
1090     // bad timezone data
1091     assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %#z").is_err());
1092     // bad timezone data (partial minutes)
1093     assert!(parse("Aug 09 2013 23:54:35 -090", "%b %d %Y %H:%M:%S %#z").is_err());
1094     // bad timezone data (partial minutes) with trailing space
1095     assert!(parse("Aug 09 2013 23:54:35 -090 ", "%b %d %Y %H:%M:%S %#z ").is_err());
1096     // bad timezone data (partial minutes) mid-string
1097     assert!(parse("Aug 09 2013 -090 23:54:35", "%b %d %Y %#z %H:%M:%S").is_err());
1098     // bad timezone data
1099     assert!(parse("Aug 09 2013 -09:00:00 23:54:35", "%b %d %Y %#z %H:%M:%S").is_err());
1100     // timezone data ambiguous with hours
1101     assert!(parse("Aug 09 2013 -09:00:23:54:35", "%b %d %Y %#z%H:%M:%S").is_err());
1102 }
1103 
1104 #[test]
test_to_string_round_trip()1105 fn test_to_string_round_trip() {
1106     let dt = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
1107     let _dt: DateTime<Utc> = dt.to_string().parse().unwrap();
1108 
1109     let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(3600).unwrap());
1110     let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap();
1111 
1112     let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(0).unwrap());
1113     let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap();
1114 }
1115 
1116 #[test]
1117 #[cfg(feature = "clock")]
test_to_string_round_trip_with_local()1118 fn test_to_string_round_trip_with_local() {
1119     let ndt = Local::now();
1120     let _dt: DateTime<FixedOffset> = ndt.to_string().parse().unwrap();
1121 }
1122 
1123 #[test]
1124 #[cfg(feature = "clock")]
test_datetime_format_with_local()1125 fn test_datetime_format_with_local() {
1126     // if we are not around the year boundary, local and UTC date should have the same year
1127     let dt = Local::now().with_month(5).unwrap();
1128     assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string());
1129 }
1130 
1131 #[test]
test_datetime_is_send_and_copy()1132 fn test_datetime_is_send_and_copy() {
1133     fn _assert_send_copy<T: Send + Copy>() {}
1134     // UTC is known to be `Send + Copy`.
1135     _assert_send_copy::<DateTime<Utc>>();
1136 }
1137 
1138 #[test]
test_subsecond_part()1139 fn test_subsecond_part() {
1140     let datetime = Utc
1141         .from_local_datetime(
1142             &NaiveDate::from_ymd_opt(2014, 7, 8)
1143                 .unwrap()
1144                 .and_hms_nano_opt(9, 10, 11, 1234567)
1145                 .unwrap(),
1146         )
1147         .unwrap();
1148 
1149     assert_eq!(1, datetime.timestamp_subsec_millis());
1150     assert_eq!(1234, datetime.timestamp_subsec_micros());
1151     assert_eq!(1234567, datetime.timestamp_subsec_nanos());
1152 }
1153 
1154 // Some targets, such as `wasm32-wasi`, have a problematic definition of `SystemTime`, such as an
1155 // `i32` (year 2035 problem), or an `u64` (no values before `UNIX-EPOCH`).
1156 // See https://github.com/rust-lang/rust/issues/44394.
1157 #[test]
1158 #[cfg(all(feature = "std", not(all(target_arch = "wasm32", target_os = "wasi"))))]
test_from_system_time()1159 fn test_from_system_time() {
1160     use std::time::{Duration, SystemTime, UNIX_EPOCH};
1161 
1162     let nanos = 999_999_000;
1163 
1164     let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap();
1165 
1166     // SystemTime -> DateTime<Utc>
1167     assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch);
1168     assert_eq!(
1169         DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)),
1170         Utc.from_local_datetime(
1171             &NaiveDate::from_ymd_opt(2001, 9, 9)
1172                 .unwrap()
1173                 .and_hms_nano_opt(1, 46, 39, nanos)
1174                 .unwrap()
1175         )
1176         .unwrap()
1177     );
1178     assert_eq!(
1179         DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)),
1180         Utc.from_local_datetime(
1181             &NaiveDate::from_ymd_opt(1938, 4, 24)
1182                 .unwrap()
1183                 .and_hms_nano_opt(22, 13, 20, 1_000)
1184                 .unwrap()
1185         )
1186         .unwrap()
1187     );
1188 
1189     // DateTime<Utc> -> SystemTime
1190     assert_eq!(SystemTime::from(epoch), UNIX_EPOCH);
1191     assert_eq!(
1192         SystemTime::from(
1193             Utc.from_local_datetime(
1194                 &NaiveDate::from_ymd_opt(2001, 9, 9)
1195                     .unwrap()
1196                     .and_hms_nano_opt(1, 46, 39, nanos)
1197                     .unwrap()
1198             )
1199             .unwrap()
1200         ),
1201         UNIX_EPOCH + Duration::new(999_999_999, nanos)
1202     );
1203     assert_eq!(
1204         SystemTime::from(
1205             Utc.from_local_datetime(
1206                 &NaiveDate::from_ymd_opt(1938, 4, 24)
1207                     .unwrap()
1208                     .and_hms_nano_opt(22, 13, 20, 1_000)
1209                     .unwrap()
1210             )
1211             .unwrap()
1212         ),
1213         UNIX_EPOCH - Duration::new(999_999_999, nanos)
1214     );
1215 
1216     // DateTime<any tz> -> SystemTime (via `with_timezone`)
1217     #[cfg(feature = "clock")]
1218     {
1219         assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH);
1220     }
1221     assert_eq!(
1222         SystemTime::from(epoch.with_timezone(&FixedOffset::east_opt(32400).unwrap())),
1223         UNIX_EPOCH
1224     );
1225     assert_eq!(
1226         SystemTime::from(epoch.with_timezone(&FixedOffset::west_opt(28800).unwrap())),
1227         UNIX_EPOCH
1228     );
1229 }
1230 
1231 #[test]
1232 #[allow(deprecated)]
test_datetime_from_local()1233 fn test_datetime_from_local() {
1234     // 2000-01-12T02:00:00Z
1235     let naivedatetime_utc =
1236         NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(2, 0, 0).unwrap();
1237     let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc);
1238 
1239     // 2000-01-12T10:00:00+8:00:00
1240     let timezone_east = FixedOffset::east_opt(8 * 60 * 60).unwrap();
1241     let naivedatetime_east =
1242         NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(10, 0, 0).unwrap();
1243     let datetime_east = DateTime::<FixedOffset>::from_local(naivedatetime_east, timezone_east);
1244 
1245     // 2000-01-11T19:00:00-7:00:00
1246     let timezone_west = FixedOffset::west_opt(7 * 60 * 60).unwrap();
1247     let naivedatetime_west =
1248         NaiveDate::from_ymd_opt(2000, 1, 11).unwrap().and_hms_opt(19, 0, 0).unwrap();
1249     let datetime_west = DateTime::<FixedOffset>::from_local(naivedatetime_west, timezone_west);
1250 
1251     assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east));
1252     assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west));
1253 }
1254 
1255 #[test]
test_datetime_from_timestamp_millis()1256 fn test_datetime_from_timestamp_millis() {
1257     // 2000-01-12T01:02:03:004Z
1258     let naive_dt =
1259         NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_milli_opt(1, 2, 3, 4).unwrap();
1260     let datetime_utc = DateTime::<Utc>::from_naive_utc_and_offset(naive_dt, Utc);
1261     assert_eq!(
1262         datetime_utc,
1263         DateTime::<Utc>::from_timestamp_millis(datetime_utc.timestamp_millis()).unwrap()
1264     );
1265 }
1266 
1267 #[test]
1268 #[cfg(feature = "clock")]
test_datetime_before_windows_api_limits()1269 fn test_datetime_before_windows_api_limits() {
1270     // dt corresponds to `FILETIME = 147221225472` from issue 651.
1271     // (https://github.com/chronotope/chrono/issues/651)
1272     // This used to fail on Windows for timezones with an offset of -5:00 or greater.
1273     // The API limits years to 1601..=30827.
1274     let dt = NaiveDate::from_ymd_opt(1601, 1, 1).unwrap().and_hms_milli_opt(4, 5, 22, 122).unwrap();
1275     let local_dt = Local.from_utc_datetime(&dt);
1276     dbg!(local_dt);
1277 }
1278 
1279 #[test]
1280 #[cfg(feature = "clock")]
test_years_elapsed()1281 fn test_years_elapsed() {
1282     const WEEKS_PER_YEAR: f32 = 52.1775;
1283 
1284     // This is always at least one year because 1 year = 52.1775 weeks.
1285     let one_year_ago =
1286         Utc::now().date_naive() - TimeDelta::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64);
1287     // A bit more than 2 years.
1288     let two_year_ago =
1289         Utc::now().date_naive() - TimeDelta::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64);
1290 
1291     assert_eq!(Utc::now().date_naive().years_since(one_year_ago), Some(1));
1292     assert_eq!(Utc::now().date_naive().years_since(two_year_ago), Some(2));
1293 
1294     // If the given DateTime is later than now, the function will always return 0.
1295     let future = Utc::now().date_naive() + TimeDelta::weeks(12);
1296     assert_eq!(Utc::now().date_naive().years_since(future), None);
1297 }
1298 
1299 #[test]
test_datetime_add_assign()1300 fn test_datetime_add_assign() {
1301     let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
1302     let datetime = naivedatetime.and_utc();
1303     let mut datetime_add = datetime;
1304 
1305     datetime_add += TimeDelta::seconds(60);
1306     assert_eq!(datetime_add, datetime + TimeDelta::seconds(60));
1307 
1308     let timezone = FixedOffset::east_opt(60 * 60).unwrap();
1309     let datetime = datetime.with_timezone(&timezone);
1310     let datetime_add = datetime_add.with_timezone(&timezone);
1311 
1312     assert_eq!(datetime_add, datetime + TimeDelta::seconds(60));
1313 
1314     let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap();
1315     let datetime = datetime.with_timezone(&timezone);
1316     let datetime_add = datetime_add.with_timezone(&timezone);
1317 
1318     assert_eq!(datetime_add, datetime + TimeDelta::seconds(60));
1319 }
1320 
1321 #[test]
1322 #[cfg(feature = "clock")]
test_datetime_add_assign_local()1323 fn test_datetime_add_assign_local() {
1324     let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
1325 
1326     let datetime = Local.from_utc_datetime(&naivedatetime);
1327     let mut datetime_add = Local.from_utc_datetime(&naivedatetime);
1328 
1329     // ensure we cross a DST transition
1330     for i in 1..=365 {
1331         datetime_add += TimeDelta::days(1);
1332         assert_eq!(datetime_add, datetime + TimeDelta::days(i))
1333     }
1334 }
1335 
1336 #[test]
test_datetime_sub_assign()1337 fn test_datetime_sub_assign() {
1338     let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(12, 0, 0).unwrap();
1339     let datetime = naivedatetime.and_utc();
1340     let mut datetime_sub = datetime;
1341 
1342     datetime_sub -= TimeDelta::minutes(90);
1343     assert_eq!(datetime_sub, datetime - TimeDelta::minutes(90));
1344 
1345     let timezone = FixedOffset::east_opt(60 * 60).unwrap();
1346     let datetime = datetime.with_timezone(&timezone);
1347     let datetime_sub = datetime_sub.with_timezone(&timezone);
1348 
1349     assert_eq!(datetime_sub, datetime - TimeDelta::minutes(90));
1350 
1351     let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap();
1352     let datetime = datetime.with_timezone(&timezone);
1353     let datetime_sub = datetime_sub.with_timezone(&timezone);
1354 
1355     assert_eq!(datetime_sub, datetime - TimeDelta::minutes(90));
1356 }
1357 
1358 #[test]
test_min_max_getters()1359 fn test_min_max_getters() {
1360     let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap();
1361     let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN);
1362     let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap();
1363     let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX);
1364 
1365     assert_eq!(format!("{:?}", beyond_min), "-262144-12-31T22:00:00-02:00");
1366     // RFC 2822 doesn't support years with more than 4 digits.
1367     // assert_eq!(beyond_min.to_rfc2822(), "");
1368     #[cfg(feature = "alloc")]
1369     assert_eq!(beyond_min.to_rfc3339(), "-262144-12-31T22:00:00-02:00");
1370     #[cfg(feature = "alloc")]
1371     assert_eq!(
1372         beyond_min.format("%Y-%m-%dT%H:%M:%S%:z").to_string(),
1373         "-262144-12-31T22:00:00-02:00"
1374     );
1375     assert_eq!(beyond_min.year(), -262144);
1376     assert_eq!(beyond_min.month(), 12);
1377     assert_eq!(beyond_min.month0(), 11);
1378     assert_eq!(beyond_min.day(), 31);
1379     assert_eq!(beyond_min.day0(), 30);
1380     assert_eq!(beyond_min.ordinal(), 366);
1381     assert_eq!(beyond_min.ordinal0(), 365);
1382     assert_eq!(beyond_min.weekday(), Weekday::Wed);
1383     assert_eq!(beyond_min.iso_week().year(), -262143);
1384     assert_eq!(beyond_min.iso_week().week(), 1);
1385     assert_eq!(beyond_min.hour(), 22);
1386     assert_eq!(beyond_min.minute(), 0);
1387     assert_eq!(beyond_min.second(), 0);
1388     assert_eq!(beyond_min.nanosecond(), 0);
1389 
1390     assert_eq!(format!("{:?}", beyond_max), "+262143-01-01T01:59:59.999999999+02:00");
1391     // RFC 2822 doesn't support years with more than 4 digits.
1392     // assert_eq!(beyond_max.to_rfc2822(), "");
1393     #[cfg(feature = "alloc")]
1394     assert_eq!(beyond_max.to_rfc3339(), "+262143-01-01T01:59:59.999999999+02:00");
1395     #[cfg(feature = "alloc")]
1396     assert_eq!(
1397         beyond_max.format("%Y-%m-%dT%H:%M:%S%.9f%:z").to_string(),
1398         "+262143-01-01T01:59:59.999999999+02:00"
1399     );
1400     assert_eq!(beyond_max.year(), 262143);
1401     assert_eq!(beyond_max.month(), 1);
1402     assert_eq!(beyond_max.month0(), 0);
1403     assert_eq!(beyond_max.day(), 1);
1404     assert_eq!(beyond_max.day0(), 0);
1405     assert_eq!(beyond_max.ordinal(), 1);
1406     assert_eq!(beyond_max.ordinal0(), 0);
1407     assert_eq!(beyond_max.weekday(), Weekday::Tue);
1408     assert_eq!(beyond_max.iso_week().year(), 262143);
1409     assert_eq!(beyond_max.iso_week().week(), 1);
1410     assert_eq!(beyond_max.hour(), 1);
1411     assert_eq!(beyond_max.minute(), 59);
1412     assert_eq!(beyond_max.second(), 59);
1413     assert_eq!(beyond_max.nanosecond(), 999_999_999);
1414 }
1415 
1416 #[test]
test_min_max_setters()1417 fn test_min_max_setters() {
1418     let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap();
1419     let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN);
1420     let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap();
1421     let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX);
1422 
1423     assert_eq!(beyond_min.with_year(2020).unwrap().year(), 2020);
1424     assert_eq!(beyond_min.with_month(beyond_min.month()), Some(beyond_min));
1425     assert_eq!(beyond_min.with_month(3), None);
1426     assert_eq!(beyond_min.with_month0(beyond_min.month0()), Some(beyond_min));
1427     assert_eq!(beyond_min.with_month0(3), None);
1428     assert_eq!(beyond_min.with_day(beyond_min.day()), Some(beyond_min));
1429     assert_eq!(beyond_min.with_day(15), None);
1430     assert_eq!(beyond_min.with_day0(beyond_min.day0()), Some(beyond_min));
1431     assert_eq!(beyond_min.with_day0(15), None);
1432     assert_eq!(beyond_min.with_ordinal(beyond_min.ordinal()), Some(beyond_min));
1433     assert_eq!(beyond_min.with_ordinal(200), None);
1434     assert_eq!(beyond_min.with_ordinal0(beyond_min.ordinal0()), Some(beyond_min));
1435     assert_eq!(beyond_min.with_ordinal0(200), None);
1436     assert_eq!(beyond_min.with_hour(beyond_min.hour()), Some(beyond_min));
1437     assert_eq!(beyond_min.with_hour(23), beyond_min.checked_add_signed(TimeDelta::hours(1)));
1438     assert_eq!(beyond_min.with_hour(5), None);
1439     assert_eq!(beyond_min.with_minute(0), Some(beyond_min));
1440     assert_eq!(beyond_min.with_second(0), Some(beyond_min));
1441     assert_eq!(beyond_min.with_nanosecond(0), Some(beyond_min));
1442 
1443     assert_eq!(beyond_max.with_year(2020).unwrap().year(), 2020);
1444     assert_eq!(beyond_max.with_month(beyond_max.month()), Some(beyond_max));
1445     assert_eq!(beyond_max.with_month(3), None);
1446     assert_eq!(beyond_max.with_month0(beyond_max.month0()), Some(beyond_max));
1447     assert_eq!(beyond_max.with_month0(3), None);
1448     assert_eq!(beyond_max.with_day(beyond_max.day()), Some(beyond_max));
1449     assert_eq!(beyond_max.with_day(15), None);
1450     assert_eq!(beyond_max.with_day0(beyond_max.day0()), Some(beyond_max));
1451     assert_eq!(beyond_max.with_day0(15), None);
1452     assert_eq!(beyond_max.with_ordinal(beyond_max.ordinal()), Some(beyond_max));
1453     assert_eq!(beyond_max.with_ordinal(200), None);
1454     assert_eq!(beyond_max.with_ordinal0(beyond_max.ordinal0()), Some(beyond_max));
1455     assert_eq!(beyond_max.with_ordinal0(200), None);
1456     assert_eq!(beyond_max.with_hour(beyond_max.hour()), Some(beyond_max));
1457     assert_eq!(beyond_max.with_hour(0), beyond_max.checked_sub_signed(TimeDelta::hours(1)));
1458     assert_eq!(beyond_max.with_hour(5), None);
1459     assert_eq!(beyond_max.with_minute(beyond_max.minute()), Some(beyond_max));
1460     assert_eq!(beyond_max.with_second(beyond_max.second()), Some(beyond_max));
1461     assert_eq!(beyond_max.with_nanosecond(beyond_max.nanosecond()), Some(beyond_max));
1462 }
1463 
1464 #[test]
1465 #[should_panic]
test_local_beyond_min_datetime()1466 fn test_local_beyond_min_datetime() {
1467     let min = FixedOffset::west_opt(2 * 60 * 60).unwrap().from_utc_datetime(&NaiveDateTime::MIN);
1468     let _ = min.naive_local();
1469 }
1470 
1471 #[test]
1472 #[should_panic]
test_local_beyond_max_datetime()1473 fn test_local_beyond_max_datetime() {
1474     let max = FixedOffset::east_opt(2 * 60 * 60).unwrap().from_utc_datetime(&NaiveDateTime::MAX);
1475     let _ = max.naive_local();
1476 }
1477 
1478 #[test]
1479 #[cfg(feature = "clock")]
test_datetime_sub_assign_local()1480 fn test_datetime_sub_assign_local() {
1481     let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
1482 
1483     let datetime = Local.from_utc_datetime(&naivedatetime);
1484     let mut datetime_sub = Local.from_utc_datetime(&naivedatetime);
1485 
1486     // ensure we cross a DST transition
1487     for i in 1..=365 {
1488         datetime_sub -= TimeDelta::days(1);
1489         assert_eq!(datetime_sub, datetime - TimeDelta::days(i))
1490     }
1491 }
1492 
1493 #[test]
test_core_duration_ops()1494 fn test_core_duration_ops() {
1495     use core::time::Duration;
1496 
1497     let mut utc_dt = Utc.with_ymd_and_hms(2023, 8, 29, 11, 34, 12).unwrap();
1498     let same = utc_dt + Duration::ZERO;
1499     assert_eq!(utc_dt, same);
1500 
1501     utc_dt += Duration::new(3600, 0);
1502     assert_eq!(utc_dt, Utc.with_ymd_and_hms(2023, 8, 29, 12, 34, 12).unwrap());
1503 }
1504 
1505 #[test]
1506 #[should_panic]
test_core_duration_max()1507 fn test_core_duration_max() {
1508     use core::time::Duration;
1509 
1510     let mut utc_dt = Utc.with_ymd_and_hms(2023, 8, 29, 11, 34, 12).unwrap();
1511     utc_dt += Duration::MAX;
1512 }
1513 
1514 #[test]
1515 #[cfg(feature = "clock")]
test_datetime_local_from_preserves_offset()1516 fn test_datetime_local_from_preserves_offset() {
1517     let naivedatetime = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
1518 
1519     let datetime = Local.from_utc_datetime(&naivedatetime);
1520     let offset = datetime.offset().fix();
1521 
1522     let datetime_fixed: DateTime<FixedOffset> = datetime.into();
1523     assert_eq!(&offset, datetime_fixed.offset());
1524     assert_eq!(datetime.fixed_offset(), datetime_fixed);
1525 }
1526 
1527 #[test]
test_datetime_fixed_offset()1528 fn test_datetime_fixed_offset() {
1529     let naivedatetime = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
1530 
1531     let datetime = Utc.from_utc_datetime(&naivedatetime);
1532     let fixed_utc = FixedOffset::east_opt(0).unwrap();
1533     assert_eq!(datetime.fixed_offset(), fixed_utc.from_local_datetime(&naivedatetime).unwrap());
1534 
1535     let fixed_offset = FixedOffset::east_opt(3600).unwrap();
1536     let datetime_fixed = fixed_offset.from_local_datetime(&naivedatetime).unwrap();
1537     assert_eq!(datetime_fixed.fixed_offset(), datetime_fixed);
1538 }
1539 
1540 #[test]
test_datetime_to_utc()1541 fn test_datetime_to_utc() {
1542     let dt =
1543         FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 2, 22, 23, 24, 25).unwrap();
1544     let dt_utc: DateTime<Utc> = dt.to_utc();
1545     assert_eq!(dt, dt_utc);
1546 }
1547 
1548 #[test]
test_add_sub_months()1549 fn test_add_sub_months() {
1550     let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
1551     assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap());
1552 
1553     let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap();
1554     assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
1555     assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap());
1556 
1557     let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
1558     assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap());
1559 
1560     let utc_dt = Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap();
1561     assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
1562     assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap());
1563 }
1564 
1565 #[test]
test_auto_conversion()1566 fn test_auto_conversion() {
1567     let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
1568     let cdt_dt = FixedOffset::west_opt(5 * 60 * 60)
1569         .unwrap()
1570         .with_ymd_and_hms(2018, 9, 5, 18, 58, 0)
1571         .unwrap();
1572     let utc_dt2: DateTime<Utc> = cdt_dt.into();
1573     assert_eq!(utc_dt, utc_dt2);
1574 }
1575 
1576 #[test]
1577 #[cfg(feature = "clock")]
1578 #[allow(deprecated)]
test_test_deprecated_from_offset()1579 fn test_test_deprecated_from_offset() {
1580     let now = Local::now();
1581     let naive = now.naive_local();
1582     let utc = now.naive_utc();
1583     let offset: FixedOffset = *now.offset();
1584 
1585     assert_eq!(DateTime::<Local>::from_local(naive, offset), now);
1586     assert_eq!(DateTime::<Local>::from_utc(utc, offset), now);
1587 }
1588 
1589 #[test]
1590 #[cfg(all(feature = "unstable-locales", feature = "alloc"))]
locale_decimal_point()1591 fn locale_decimal_point() {
1592     use crate::Locale::{ar_SY, nl_NL};
1593     let dt =
1594         Utc.with_ymd_and_hms(2018, 9, 5, 18, 58, 0).unwrap().with_nanosecond(123456780).unwrap();
1595 
1596     assert_eq!(dt.format_localized("%T%.f", nl_NL).to_string(), "18:58:00,123456780");
1597     assert_eq!(dt.format_localized("%T%.3f", nl_NL).to_string(), "18:58:00,123");
1598     assert_eq!(dt.format_localized("%T%.6f", nl_NL).to_string(), "18:58:00,123456");
1599     assert_eq!(dt.format_localized("%T%.9f", nl_NL).to_string(), "18:58:00,123456780");
1600 
1601     assert_eq!(dt.format_localized("%T%.f", ar_SY).to_string(), "18:58:00.123456780");
1602     assert_eq!(dt.format_localized("%T%.3f", ar_SY).to_string(), "18:58:00.123");
1603     assert_eq!(dt.format_localized("%T%.6f", ar_SY).to_string(), "18:58:00.123456");
1604     assert_eq!(dt.format_localized("%T%.9f", ar_SY).to_string(), "18:58:00.123456780");
1605 }
1606 
1607 /// This is an extended test for <https://github.com/chronotope/chrono/issues/1289>.
1608 #[test]
nano_roundrip()1609 fn nano_roundrip() {
1610     const BILLION: i64 = 1_000_000_000;
1611 
1612     for nanos in [
1613         i64::MIN,
1614         i64::MIN + 1,
1615         i64::MIN + 2,
1616         i64::MIN + BILLION - 1,
1617         i64::MIN + BILLION,
1618         i64::MIN + BILLION + 1,
1619         -BILLION - 1,
1620         -BILLION,
1621         -BILLION + 1,
1622         0,
1623         BILLION - 1,
1624         BILLION,
1625         BILLION + 1,
1626         i64::MAX - BILLION - 1,
1627         i64::MAX - BILLION,
1628         i64::MAX - BILLION + 1,
1629         i64::MAX - 2,
1630         i64::MAX - 1,
1631         i64::MAX,
1632     ] {
1633         println!("nanos: {}", nanos);
1634         let dt = Utc.timestamp_nanos(nanos);
1635         let nanos2 = dt.timestamp_nanos_opt().expect("value roundtrips");
1636         assert_eq!(nanos, nanos2);
1637     }
1638 }
1639