1 | // This is a part of Chrono. |
2 | // See README.md and LICENSE.txt for details. |
3 | |
4 | //! ISO 8601 calendar date with time zone. |
5 | #![allow (deprecated)] |
6 | |
7 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
8 | use core::borrow::Borrow; |
9 | use core::cmp::Ordering; |
10 | use core::ops::{Add, AddAssign, Sub, SubAssign}; |
11 | use core::{fmt, hash}; |
12 | |
13 | #[cfg (feature = "rkyv" )] |
14 | use rkyv::{Archive, Deserialize, Serialize}; |
15 | |
16 | #[cfg (feature = "unstable-locales" )] |
17 | use crate::format::Locale; |
18 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
19 | use crate::format::{DelayedFormat, Item, StrftimeItems}; |
20 | use crate::naive::{IsoWeek, NaiveDate, NaiveTime}; |
21 | use crate::offset::{TimeZone, Utc}; |
22 | use crate::oldtime::Duration as OldDuration; |
23 | use crate::DateTime; |
24 | use crate::{Datelike, Weekday}; |
25 | |
26 | /// ISO 8601 calendar date with time zone. |
27 | /// |
28 | /// You almost certainly want to be using a [`NaiveDate`] instead of this type. |
29 | /// |
30 | /// This type primarily exists to aid in the construction of DateTimes that |
31 | /// have a timezone by way of the [`TimeZone`] datelike constructors (e.g. |
32 | /// [`TimeZone::ymd`]). |
33 | /// |
34 | /// This type should be considered ambiguous at best, due to the inherent lack |
35 | /// of precision required for the time zone resolution. |
36 | /// |
37 | /// There are some guarantees on the usage of `Date<Tz>`: |
38 | /// |
39 | /// - If properly constructed via [`TimeZone::ymd`] and others without an error, |
40 | /// the corresponding local date should exist for at least a moment. |
41 | /// (It may still have a gap from the offset changes.) |
42 | /// |
43 | /// - The `TimeZone` is free to assign *any* [`Offset`](crate::offset::Offset) to the |
44 | /// local date, as long as that offset did occur in given day. |
45 | /// |
46 | /// For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`, |
47 | /// it may produce either `2015-03-08-08:00` or `2015-03-08-07:00` |
48 | /// but *not* `2015-03-08+00:00` and others. |
49 | /// |
50 | /// - Once constructed as a full `DateTime`, [`DateTime::date`] and other associated |
51 | /// methods should return those for the original `Date`. For example, if `dt = |
52 | /// tz.ymd_opt(y,m,d).unwrap().hms(h,n,s)` were valid, `dt.date() == tz.ymd_opt(y,m,d).unwrap()`. |
53 | /// |
54 | /// - The date is timezone-agnostic up to one day (i.e. practically always), |
55 | /// so the local date and UTC date should be equal for most cases |
56 | /// even though the raw calculation between `NaiveDate` and `Duration` may not. |
57 | #[deprecated (since = "0.4.23" , note = "Use `NaiveDate` or `DateTime<Tz>` instead" )] |
58 | #[derive (Clone)] |
59 | #[cfg_attr (feature = "rkyv" , derive(Archive, Deserialize, Serialize))] |
60 | pub struct Date<Tz: TimeZone> { |
61 | date: NaiveDate, |
62 | offset: Tz::Offset, |
63 | } |
64 | |
65 | /// The minimum possible `Date`. |
66 | #[allow (deprecated)] |
67 | #[deprecated (since = "0.4.20" , note = "Use Date::MIN_UTC instead" )] |
68 | pub const MIN_DATE: Date<Utc> = Date::<Utc>::MIN_UTC; |
69 | /// The maximum possible `Date`. |
70 | #[allow (deprecated)] |
71 | #[deprecated (since = "0.4.20" , note = "Use Date::MAX_UTC instead" )] |
72 | pub const MAX_DATE: Date<Utc> = Date::<Utc>::MAX_UTC; |
73 | |
74 | impl<Tz: TimeZone> Date<Tz> { |
75 | /// Makes a new `Date` with given *UTC* date and offset. |
76 | /// The local date should be constructed via the `TimeZone` trait. |
77 | // |
78 | // note: this constructor is purposely not named to `new` to discourage the direct usage. |
79 | #[inline ] |
80 | #[must_use ] |
81 | pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> { |
82 | Date { date, offset } |
83 | } |
84 | |
85 | /// Makes a new `DateTime` from the current date and given `NaiveTime`. |
86 | /// The offset in the current date is preserved. |
87 | /// |
88 | /// Panics on invalid datetime. |
89 | #[inline ] |
90 | #[must_use ] |
91 | pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> { |
92 | let localdt = self.naive_local().and_time(time); |
93 | self.timezone().from_local_datetime(&localdt).single() |
94 | } |
95 | |
96 | /// Makes a new `DateTime` from the current date, hour, minute and second. |
97 | /// The offset in the current date is preserved. |
98 | /// |
99 | /// Panics on invalid hour, minute and/or second. |
100 | #[deprecated (since = "0.4.23" , note = "Use and_hms_opt() instead" )] |
101 | #[inline ] |
102 | #[must_use ] |
103 | pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> { |
104 | self.and_hms_opt(hour, min, sec).expect("invalid time" ) |
105 | } |
106 | |
107 | /// Makes a new `DateTime` from the current date, hour, minute and second. |
108 | /// The offset in the current date is preserved. |
109 | /// |
110 | /// Returns `None` on invalid hour, minute and/or second. |
111 | #[inline ] |
112 | #[must_use ] |
113 | pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> { |
114 | NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time)) |
115 | } |
116 | |
117 | /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. |
118 | /// The millisecond part can exceed 1,000 in order to represent the leap second. |
119 | /// The offset in the current date is preserved. |
120 | /// |
121 | /// Panics on invalid hour, minute, second and/or millisecond. |
122 | #[deprecated (since = "0.4.23" , note = "Use and_hms_milli_opt() instead" )] |
123 | #[inline ] |
124 | #[must_use ] |
125 | pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> { |
126 | self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time" ) |
127 | } |
128 | |
129 | /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. |
130 | /// The millisecond part can exceed 1,000 in order to represent the leap second. |
131 | /// The offset in the current date is preserved. |
132 | /// |
133 | /// Returns `None` on invalid hour, minute, second and/or millisecond. |
134 | #[inline ] |
135 | #[must_use ] |
136 | pub fn and_hms_milli_opt( |
137 | &self, |
138 | hour: u32, |
139 | min: u32, |
140 | sec: u32, |
141 | milli: u32, |
142 | ) -> Option<DateTime<Tz>> { |
143 | NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time)) |
144 | } |
145 | |
146 | /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. |
147 | /// The microsecond part can exceed 1,000,000 in order to represent the leap second. |
148 | /// The offset in the current date is preserved. |
149 | /// |
150 | /// Panics on invalid hour, minute, second and/or microsecond. |
151 | #[deprecated (since = "0.4.23" , note = "Use and_hms_micro_opt() instead" )] |
152 | #[inline ] |
153 | #[must_use ] |
154 | pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> { |
155 | self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time" ) |
156 | } |
157 | |
158 | /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. |
159 | /// The microsecond part can exceed 1,000,000 in order to represent the leap second. |
160 | /// The offset in the current date is preserved. |
161 | /// |
162 | /// Returns `None` on invalid hour, minute, second and/or microsecond. |
163 | #[inline ] |
164 | #[must_use ] |
165 | pub fn and_hms_micro_opt( |
166 | &self, |
167 | hour: u32, |
168 | min: u32, |
169 | sec: u32, |
170 | micro: u32, |
171 | ) -> Option<DateTime<Tz>> { |
172 | NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time)) |
173 | } |
174 | |
175 | /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. |
176 | /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. |
177 | /// The offset in the current date is preserved. |
178 | /// |
179 | /// Panics on invalid hour, minute, second and/or nanosecond. |
180 | #[deprecated (since = "0.4.23" , note = "Use and_hms_nano_opt() instead" )] |
181 | #[inline ] |
182 | #[must_use ] |
183 | pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> { |
184 | self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time" ) |
185 | } |
186 | |
187 | /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. |
188 | /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. |
189 | /// The offset in the current date is preserved. |
190 | /// |
191 | /// Returns `None` on invalid hour, minute, second and/or nanosecond. |
192 | #[inline ] |
193 | #[must_use ] |
194 | pub fn and_hms_nano_opt( |
195 | &self, |
196 | hour: u32, |
197 | min: u32, |
198 | sec: u32, |
199 | nano: u32, |
200 | ) -> Option<DateTime<Tz>> { |
201 | NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time)) |
202 | } |
203 | |
204 | /// Makes a new `Date` for the next date. |
205 | /// |
206 | /// Panics when `self` is the last representable date. |
207 | #[deprecated (since = "0.4.23" , note = "Use succ_opt() instead" )] |
208 | #[inline ] |
209 | #[must_use ] |
210 | pub fn succ(&self) -> Date<Tz> { |
211 | self.succ_opt().expect("out of bound" ) |
212 | } |
213 | |
214 | /// Makes a new `Date` for the next date. |
215 | /// |
216 | /// Returns `None` when `self` is the last representable date. |
217 | #[inline ] |
218 | #[must_use ] |
219 | pub fn succ_opt(&self) -> Option<Date<Tz>> { |
220 | self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone())) |
221 | } |
222 | |
223 | /// Makes a new `Date` for the prior date. |
224 | /// |
225 | /// Panics when `self` is the first representable date. |
226 | #[deprecated (since = "0.4.23" , note = "Use pred_opt() instead" )] |
227 | #[inline ] |
228 | #[must_use ] |
229 | pub fn pred(&self) -> Date<Tz> { |
230 | self.pred_opt().expect("out of bound" ) |
231 | } |
232 | |
233 | /// Makes a new `Date` for the prior date. |
234 | /// |
235 | /// Returns `None` when `self` is the first representable date. |
236 | #[inline ] |
237 | #[must_use ] |
238 | pub fn pred_opt(&self) -> Option<Date<Tz>> { |
239 | self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone())) |
240 | } |
241 | |
242 | /// Retrieves an associated offset from UTC. |
243 | #[inline ] |
244 | #[must_use ] |
245 | pub fn offset(&self) -> &Tz::Offset { |
246 | &self.offset |
247 | } |
248 | |
249 | /// Retrieves an associated time zone. |
250 | #[inline ] |
251 | #[must_use ] |
252 | pub fn timezone(&self) -> Tz { |
253 | TimeZone::from_offset(&self.offset) |
254 | } |
255 | |
256 | /// Changes the associated time zone. |
257 | /// This does not change the actual `Date` (but will change the string representation). |
258 | #[inline ] |
259 | #[must_use ] |
260 | pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> { |
261 | tz.from_utc_date(&self.date) |
262 | } |
263 | |
264 | /// Adds given `Duration` to the current date. |
265 | /// |
266 | /// Returns `None` when it will result in overflow. |
267 | #[inline ] |
268 | #[must_use ] |
269 | pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> { |
270 | let date = self.date.checked_add_signed(rhs)?; |
271 | Some(Date { date, offset: self.offset }) |
272 | } |
273 | |
274 | /// Subtracts given `Duration` from the current date. |
275 | /// |
276 | /// Returns `None` when it will result in overflow. |
277 | #[inline ] |
278 | #[must_use ] |
279 | pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> { |
280 | let date = self.date.checked_sub_signed(rhs)?; |
281 | Some(Date { date, offset: self.offset }) |
282 | } |
283 | |
284 | /// Subtracts another `Date` from the current date. |
285 | /// Returns a `Duration` of integral numbers. |
286 | /// |
287 | /// This does not overflow or underflow at all, |
288 | /// as all possible output fits in the range of `Duration`. |
289 | #[inline ] |
290 | #[must_use ] |
291 | pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration { |
292 | self.date.signed_duration_since(rhs.date) |
293 | } |
294 | |
295 | /// Returns a view to the naive UTC date. |
296 | #[inline ] |
297 | #[must_use ] |
298 | pub fn naive_utc(&self) -> NaiveDate { |
299 | self.date |
300 | } |
301 | |
302 | /// Returns a view to the naive local date. |
303 | /// |
304 | /// This is technically the same as [`naive_utc`](#method.naive_utc) |
305 | /// because the offset is restricted to never exceed one day, |
306 | /// but provided for the consistency. |
307 | #[inline ] |
308 | #[must_use ] |
309 | pub fn naive_local(&self) -> NaiveDate { |
310 | self.date |
311 | } |
312 | |
313 | /// Returns the number of whole years from the given `base` until `self`. |
314 | #[must_use ] |
315 | pub fn years_since(&self, base: Self) -> Option<u32> { |
316 | self.date.years_since(base.date) |
317 | } |
318 | |
319 | /// The minimum possible `Date`. |
320 | pub const MIN_UTC: Date<Utc> = Date { date: NaiveDate::MIN, offset: Utc }; |
321 | /// The maximum possible `Date`. |
322 | pub const MAX_UTC: Date<Utc> = Date { date: NaiveDate::MAX, offset: Utc }; |
323 | } |
324 | |
325 | /// Maps the local date to other date with given conversion function. |
326 | fn map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>> |
327 | where |
328 | F: FnMut(NaiveDate) -> Option<NaiveDate>, |
329 | { |
330 | f(d.naive_local()).and_then(|date: NaiveDate| d.timezone().from_local_date(&date).single()) |
331 | } |
332 | |
333 | impl<Tz: TimeZone> Date<Tz> |
334 | where |
335 | Tz::Offset: fmt::Display, |
336 | { |
337 | /// Formats the date with the specified formatting items. |
338 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
339 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
340 | #[inline ] |
341 | #[must_use ] |
342 | pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> |
343 | where |
344 | I: Iterator<Item = B> + Clone, |
345 | B: Borrow<Item<'a>>, |
346 | { |
347 | DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items) |
348 | } |
349 | |
350 | /// Formats the date with the specified format string. |
351 | /// See the [`crate::format::strftime`] module |
352 | /// on the supported escape sequences. |
353 | #[cfg (any(feature = "alloc" , feature = "std" , test))] |
354 | #[cfg_attr (docsrs, doc(cfg(any(feature = "alloc" , feature = "std" ))))] |
355 | #[inline ] |
356 | #[must_use ] |
357 | pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { |
358 | self.format_with_items(StrftimeItems::new(fmt)) |
359 | } |
360 | |
361 | /// Formats the date with the specified formatting items and locale. |
362 | #[cfg (feature = "unstable-locales" )] |
363 | #[cfg_attr (docsrs, doc(cfg(feature = "unstable-locales" )))] |
364 | #[inline ] |
365 | #[must_use ] |
366 | pub fn format_localized_with_items<'a, I, B>( |
367 | &self, |
368 | items: I, |
369 | locale: Locale, |
370 | ) -> DelayedFormat<I> |
371 | where |
372 | I: Iterator<Item = B> + Clone, |
373 | B: Borrow<Item<'a>>, |
374 | { |
375 | DelayedFormat::new_with_offset_and_locale( |
376 | Some(self.naive_local()), |
377 | None, |
378 | &self.offset, |
379 | items, |
380 | locale, |
381 | ) |
382 | } |
383 | |
384 | /// Formats the date with the specified format string and locale. |
385 | /// See the [`crate::format::strftime`] module |
386 | /// on the supported escape sequences. |
387 | #[cfg (feature = "unstable-locales" )] |
388 | #[cfg_attr (docsrs, doc(cfg(feature = "unstable-locales" )))] |
389 | #[inline ] |
390 | #[must_use ] |
391 | pub fn format_localized<'a>( |
392 | &self, |
393 | fmt: &'a str, |
394 | locale: Locale, |
395 | ) -> DelayedFormat<StrftimeItems<'a>> { |
396 | self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) |
397 | } |
398 | } |
399 | |
400 | impl<Tz: TimeZone> Datelike for Date<Tz> { |
401 | #[inline ] |
402 | fn year(&self) -> i32 { |
403 | self.naive_local().year() |
404 | } |
405 | #[inline ] |
406 | fn month(&self) -> u32 { |
407 | self.naive_local().month() |
408 | } |
409 | #[inline ] |
410 | fn month0(&self) -> u32 { |
411 | self.naive_local().month0() |
412 | } |
413 | #[inline ] |
414 | fn day(&self) -> u32 { |
415 | self.naive_local().day() |
416 | } |
417 | #[inline ] |
418 | fn day0(&self) -> u32 { |
419 | self.naive_local().day0() |
420 | } |
421 | #[inline ] |
422 | fn ordinal(&self) -> u32 { |
423 | self.naive_local().ordinal() |
424 | } |
425 | #[inline ] |
426 | fn ordinal0(&self) -> u32 { |
427 | self.naive_local().ordinal0() |
428 | } |
429 | #[inline ] |
430 | fn weekday(&self) -> Weekday { |
431 | self.naive_local().weekday() |
432 | } |
433 | #[inline ] |
434 | fn iso_week(&self) -> IsoWeek { |
435 | self.naive_local().iso_week() |
436 | } |
437 | |
438 | #[inline ] |
439 | fn with_year(&self, year: i32) -> Option<Date<Tz>> { |
440 | map_local(self, |date| date.with_year(year)) |
441 | } |
442 | |
443 | #[inline ] |
444 | fn with_month(&self, month: u32) -> Option<Date<Tz>> { |
445 | map_local(self, |date| date.with_month(month)) |
446 | } |
447 | |
448 | #[inline ] |
449 | fn with_month0(&self, month0: u32) -> Option<Date<Tz>> { |
450 | map_local(self, |date| date.with_month0(month0)) |
451 | } |
452 | |
453 | #[inline ] |
454 | fn with_day(&self, day: u32) -> Option<Date<Tz>> { |
455 | map_local(self, |date| date.with_day(day)) |
456 | } |
457 | |
458 | #[inline ] |
459 | fn with_day0(&self, day0: u32) -> Option<Date<Tz>> { |
460 | map_local(self, |date| date.with_day0(day0)) |
461 | } |
462 | |
463 | #[inline ] |
464 | fn with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>> { |
465 | map_local(self, |date| date.with_ordinal(ordinal)) |
466 | } |
467 | |
468 | #[inline ] |
469 | fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>> { |
470 | map_local(self, |date| date.with_ordinal0(ordinal0)) |
471 | } |
472 | } |
473 | |
474 | // we need them as automatic impls cannot handle associated types |
475 | impl<Tz: TimeZone> Copy for Date<Tz> where <Tz as TimeZone>::Offset: Copy {} |
476 | unsafe impl<Tz: TimeZone> Send for Date<Tz> where <Tz as TimeZone>::Offset: Send {} |
477 | |
478 | impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Date<Tz2>> for Date<Tz> { |
479 | fn eq(&self, other: &Date<Tz2>) -> bool { |
480 | self.date == other.date |
481 | } |
482 | } |
483 | |
484 | impl<Tz: TimeZone> Eq for Date<Tz> {} |
485 | |
486 | impl<Tz: TimeZone> PartialOrd for Date<Tz> { |
487 | fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> { |
488 | self.date.partial_cmp(&other.date) |
489 | } |
490 | } |
491 | |
492 | impl<Tz: TimeZone> Ord for Date<Tz> { |
493 | fn cmp(&self, other: &Date<Tz>) -> Ordering { |
494 | self.date.cmp(&other.date) |
495 | } |
496 | } |
497 | |
498 | impl<Tz: TimeZone> hash::Hash for Date<Tz> { |
499 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
500 | self.date.hash(state) |
501 | } |
502 | } |
503 | |
504 | impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> { |
505 | type Output = Date<Tz>; |
506 | |
507 | #[inline ] |
508 | fn add(self, rhs: OldDuration) -> Date<Tz> { |
509 | self.checked_add_signed(rhs).expect(msg:"`Date + Duration` overflowed" ) |
510 | } |
511 | } |
512 | |
513 | impl<Tz: TimeZone> AddAssign<OldDuration> for Date<Tz> { |
514 | #[inline ] |
515 | fn add_assign(&mut self, rhs: OldDuration) { |
516 | self.date = self.date.checked_add_signed(rhs).expect(msg:"`Date + Duration` overflowed" ); |
517 | } |
518 | } |
519 | |
520 | impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> { |
521 | type Output = Date<Tz>; |
522 | |
523 | #[inline ] |
524 | fn sub(self, rhs: OldDuration) -> Date<Tz> { |
525 | self.checked_sub_signed(rhs).expect(msg:"`Date - Duration` overflowed" ) |
526 | } |
527 | } |
528 | |
529 | impl<Tz: TimeZone> SubAssign<OldDuration> for Date<Tz> { |
530 | #[inline ] |
531 | fn sub_assign(&mut self, rhs: OldDuration) { |
532 | self.date = self.date.checked_sub_signed(rhs).expect(msg:"`Date - Duration` overflowed" ); |
533 | } |
534 | } |
535 | |
536 | impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> { |
537 | type Output = OldDuration; |
538 | |
539 | #[inline ] |
540 | fn sub(self, rhs: Date<Tz>) -> OldDuration { |
541 | self.signed_duration_since(rhs) |
542 | } |
543 | } |
544 | |
545 | impl<Tz: TimeZone> fmt::Debug for Date<Tz> { |
546 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
547 | self.naive_local().fmt(f)?; |
548 | self.offset.fmt(f) |
549 | } |
550 | } |
551 | |
552 | impl<Tz: TimeZone> fmt::Display for Date<Tz> |
553 | where |
554 | Tz::Offset: fmt::Display, |
555 | { |
556 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
557 | self.naive_local().fmt(f)?; |
558 | self.offset.fmt(f) |
559 | } |
560 | } |
561 | |
562 | // Note that implementation of Arbitrary cannot be automatically derived for Date<Tz>, due to |
563 | // the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary. |
564 | #[cfg (feature = "arbitrary" )] |
565 | impl<'a, Tz> arbitrary::Arbitrary<'a> for Date<Tz> |
566 | where |
567 | Tz: TimeZone, |
568 | <Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>, |
569 | { |
570 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Date<Tz>> { |
571 | let date = NaiveDate::arbitrary(u)?; |
572 | let offset = <Tz as TimeZone>::Offset::arbitrary(u)?; |
573 | Ok(Date::from_utc(date, offset)) |
574 | } |
575 | } |
576 | |
577 | #[cfg (test)] |
578 | mod tests { |
579 | use super::Date; |
580 | |
581 | use crate::oldtime::Duration; |
582 | use crate::{FixedOffset, NaiveDate, Utc}; |
583 | |
584 | #[cfg (feature = "clock" )] |
585 | use crate::offset::{Local, TimeZone}; |
586 | |
587 | #[test ] |
588 | #[cfg (feature = "clock" )] |
589 | fn test_years_elapsed() { |
590 | const WEEKS_PER_YEAR: f32 = 52.1775; |
591 | |
592 | // This is always at least one year because 1 year = 52.1775 weeks. |
593 | let one_year_ago = Utc::today() - Duration::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64); |
594 | // A bit more than 2 years. |
595 | let two_year_ago = Utc::today() - Duration::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64); |
596 | |
597 | assert_eq!(Utc::today().years_since(one_year_ago), Some(1)); |
598 | assert_eq!(Utc::today().years_since(two_year_ago), Some(2)); |
599 | |
600 | // If the given DateTime is later than now, the function will always return 0. |
601 | let future = Utc::today() + Duration::weeks(12); |
602 | assert_eq!(Utc::today().years_since(future), None); |
603 | } |
604 | |
605 | #[test ] |
606 | fn test_date_add_assign() { |
607 | let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); |
608 | let date = Date::<Utc>::from_utc(naivedate, Utc); |
609 | let mut date_add = date; |
610 | |
611 | date_add += Duration::days(5); |
612 | assert_eq!(date_add, date + Duration::days(5)); |
613 | |
614 | let timezone = FixedOffset::east_opt(60 * 60).unwrap(); |
615 | let date = date.with_timezone(&timezone); |
616 | let date_add = date_add.with_timezone(&timezone); |
617 | |
618 | assert_eq!(date_add, date + Duration::days(5)); |
619 | |
620 | let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); |
621 | let date = date.with_timezone(&timezone); |
622 | let date_add = date_add.with_timezone(&timezone); |
623 | |
624 | assert_eq!(date_add, date + Duration::days(5)); |
625 | } |
626 | |
627 | #[test ] |
628 | #[cfg (feature = "clock" )] |
629 | fn test_date_add_assign_local() { |
630 | let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); |
631 | |
632 | let date = Local.from_utc_date(&naivedate); |
633 | let mut date_add = date; |
634 | |
635 | date_add += Duration::days(5); |
636 | assert_eq!(date_add, date + Duration::days(5)); |
637 | } |
638 | |
639 | #[test ] |
640 | fn test_date_sub_assign() { |
641 | let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); |
642 | let date = Date::<Utc>::from_utc(naivedate, Utc); |
643 | let mut date_sub = date; |
644 | |
645 | date_sub -= Duration::days(5); |
646 | assert_eq!(date_sub, date - Duration::days(5)); |
647 | |
648 | let timezone = FixedOffset::east_opt(60 * 60).unwrap(); |
649 | let date = date.with_timezone(&timezone); |
650 | let date_sub = date_sub.with_timezone(&timezone); |
651 | |
652 | assert_eq!(date_sub, date - Duration::days(5)); |
653 | |
654 | let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); |
655 | let date = date.with_timezone(&timezone); |
656 | let date_sub = date_sub.with_timezone(&timezone); |
657 | |
658 | assert_eq!(date_sub, date - Duration::days(5)); |
659 | } |
660 | |
661 | #[test ] |
662 | #[cfg (feature = "clock" )] |
663 | fn test_date_sub_assign_local() { |
664 | let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); |
665 | |
666 | let date = Local.from_utc_date(&naivedate); |
667 | let mut date_sub = date; |
668 | |
669 | date_sub -= Duration::days(5); |
670 | assert_eq!(date_sub, date - Duration::days(5)); |
671 | } |
672 | } |
673 | |