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