• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
6 #[cfg(any(feature = "alloc", feature = "std", test))]
7 use core::borrow::Borrow;
8 use core::cmp::Ordering;
9 use core::ops::{Add, Sub};
10 use core::{fmt, hash};
11 use oldtime::Duration as OldDuration;
12 
13 #[cfg(feature = "unstable-locales")]
14 use format::Locale;
15 #[cfg(any(feature = "alloc", feature = "std", test))]
16 use format::{DelayedFormat, Item, StrftimeItems};
17 use naive::{self, IsoWeek, NaiveDate, NaiveTime};
18 use offset::{TimeZone, Utc};
19 use DateTime;
20 use {Datelike, Weekday};
21 
22 /// ISO 8601 calendar date with time zone.
23 ///
24 /// This type should be considered ambiguous at best,
25 /// due to the inherent lack of precision required for the time zone resolution.
26 /// For serialization and deserialization uses, it is best to use `NaiveDate` instead.
27 /// There are some guarantees on the usage of `Date<Tz>`:
28 ///
29 /// - If properly constructed via `TimeZone::ymd` and others without an error,
30 ///   the corresponding local date should exist for at least a moment.
31 ///   (It may still have a gap from the offset changes.)
32 ///
33 /// - The `TimeZone` is free to assign *any* `Offset` to the local date,
34 ///   as long as that offset did occur in given day.
35 ///   For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`,
36 ///   it may produce either `2015-03-08-08:00` or `2015-03-08-07:00`
37 ///   but *not* `2015-03-08+00:00` and others.
38 ///
39 /// - Once constructed as a full `DateTime`,
40 ///   `DateTime::date` and other associated methods should return those for the original `Date`.
41 ///   For example, if `dt = tz.ymd(y,m,d).hms(h,n,s)` were valid, `dt.date() == tz.ymd(y,m,d)`.
42 ///
43 /// - The date is timezone-agnostic up to one day (i.e. practically always),
44 ///   so the local date and UTC date should be equal for most cases
45 ///   even though the raw calculation between `NaiveDate` and `Duration` may not.
46 #[derive(Clone)]
47 pub struct Date<Tz: TimeZone> {
48     date: NaiveDate,
49     offset: Tz::Offset,
50 }
51 
52 /// The minimum possible `Date`.
53 pub const MIN_DATE: Date<Utc> = Date { date: naive::MIN_DATE, offset: Utc };
54 /// The maximum possible `Date`.
55 pub const MAX_DATE: Date<Utc> = Date { date: naive::MAX_DATE, offset: Utc };
56 
57 impl<Tz: TimeZone> Date<Tz> {
58     /// Makes a new `Date` with given *UTC* date and offset.
59     /// The local date should be constructed via the `TimeZone` trait.
60     //
61     // note: this constructor is purposely not named to `new` to discourage the direct usage.
62     #[inline]
from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz>63     pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
64         Date { date: date, offset: offset }
65     }
66 
67     /// Makes a new `DateTime` from the current date and given `NaiveTime`.
68     /// The offset in the current date is preserved.
69     ///
70     /// Panics on invalid datetime.
71     #[inline]
and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>>72     pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
73         let localdt = self.naive_local().and_time(time);
74         self.timezone().from_local_datetime(&localdt).single()
75     }
76 
77     /// Makes a new `DateTime` from the current date, hour, minute and second.
78     /// The offset in the current date is preserved.
79     ///
80     /// Panics on invalid hour, minute and/or second.
81     #[inline]
and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz>82     pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> {
83         self.and_hms_opt(hour, min, sec).expect("invalid time")
84     }
85 
86     /// Makes a new `DateTime` from the current date, hour, minute and second.
87     /// The offset in the current date is preserved.
88     ///
89     /// Returns `None` on invalid hour, minute and/or second.
90     #[inline]
and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>>91     pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> {
92         NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
93     }
94 
95     /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
96     /// The millisecond part can exceed 1,000 in order to represent the leap second.
97     /// The offset in the current date is preserved.
98     ///
99     /// Panics on invalid hour, minute, second and/or millisecond.
100     #[inline]
and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz>101     pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> {
102         self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
103     }
104 
105     /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
106     /// The millisecond part can exceed 1,000 in order to represent the leap second.
107     /// The offset in the current date is preserved.
108     ///
109     /// Returns `None` on invalid hour, minute, second and/or millisecond.
110     #[inline]
and_hms_milli_opt( &self, hour: u32, min: u32, sec: u32, milli: u32, ) -> Option<DateTime<Tz>>111     pub fn and_hms_milli_opt(
112         &self,
113         hour: u32,
114         min: u32,
115         sec: u32,
116         milli: u32,
117     ) -> Option<DateTime<Tz>> {
118         NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
119     }
120 
121     /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
122     /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
123     /// The offset in the current date is preserved.
124     ///
125     /// Panics on invalid hour, minute, second and/or microsecond.
126     #[inline]
and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz>127     pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> {
128         self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
129     }
130 
131     /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
132     /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
133     /// The offset in the current date is preserved.
134     ///
135     /// Returns `None` on invalid hour, minute, second and/or microsecond.
136     #[inline]
and_hms_micro_opt( &self, hour: u32, min: u32, sec: u32, micro: u32, ) -> Option<DateTime<Tz>>137     pub fn and_hms_micro_opt(
138         &self,
139         hour: u32,
140         min: u32,
141         sec: u32,
142         micro: u32,
143     ) -> Option<DateTime<Tz>> {
144         NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
145     }
146 
147     /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
148     /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
149     /// The offset in the current date is preserved.
150     ///
151     /// Panics on invalid hour, minute, second and/or nanosecond.
152     #[inline]
and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz>153     pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> {
154         self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
155     }
156 
157     /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
158     /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
159     /// The offset in the current date is preserved.
160     ///
161     /// Returns `None` on invalid hour, minute, second and/or nanosecond.
162     #[inline]
and_hms_nano_opt( &self, hour: u32, min: u32, sec: u32, nano: u32, ) -> Option<DateTime<Tz>>163     pub fn and_hms_nano_opt(
164         &self,
165         hour: u32,
166         min: u32,
167         sec: u32,
168         nano: u32,
169     ) -> Option<DateTime<Tz>> {
170         NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
171     }
172 
173     /// Makes a new `Date` for the next date.
174     ///
175     /// Panics when `self` is the last representable date.
176     #[inline]
succ(&self) -> Date<Tz>177     pub fn succ(&self) -> Date<Tz> {
178         self.succ_opt().expect("out of bound")
179     }
180 
181     /// Makes a new `Date` for the next date.
182     ///
183     /// Returns `None` when `self` is the last representable date.
184     #[inline]
succ_opt(&self) -> Option<Date<Tz>>185     pub fn succ_opt(&self) -> Option<Date<Tz>> {
186         self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
187     }
188 
189     /// Makes a new `Date` for the prior date.
190     ///
191     /// Panics when `self` is the first representable date.
192     #[inline]
pred(&self) -> Date<Tz>193     pub fn pred(&self) -> Date<Tz> {
194         self.pred_opt().expect("out of bound")
195     }
196 
197     /// Makes a new `Date` for the prior date.
198     ///
199     /// Returns `None` when `self` is the first representable date.
200     #[inline]
pred_opt(&self) -> Option<Date<Tz>>201     pub fn pred_opt(&self) -> Option<Date<Tz>> {
202         self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
203     }
204 
205     /// Retrieves an associated offset from UTC.
206     #[inline]
offset(&self) -> &Tz::Offset207     pub fn offset(&self) -> &Tz::Offset {
208         &self.offset
209     }
210 
211     /// Retrieves an associated time zone.
212     #[inline]
timezone(&self) -> Tz213     pub fn timezone(&self) -> Tz {
214         TimeZone::from_offset(&self.offset)
215     }
216 
217     /// Changes the associated time zone.
218     /// This does not change the actual `Date` (but will change the string representation).
219     #[inline]
with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2>220     pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
221         tz.from_utc_date(&self.date)
222     }
223 
224     /// Adds given `Duration` to the current date.
225     ///
226     /// Returns `None` when it will result in overflow.
227     #[inline]
checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>>228     pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
229         let date = try_opt!(self.date.checked_add_signed(rhs));
230         Some(Date { date: date, offset: self.offset })
231     }
232 
233     /// Subtracts given `Duration` from the current date.
234     ///
235     /// Returns `None` when it will result in overflow.
236     #[inline]
checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>>237     pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
238         let date = try_opt!(self.date.checked_sub_signed(rhs));
239         Some(Date { date: date, offset: self.offset })
240     }
241 
242     /// Subtracts another `Date` from the current date.
243     /// Returns a `Duration` of integral numbers.
244     ///
245     /// This does not overflow or underflow at all,
246     /// as all possible output fits in the range of `Duration`.
247     #[inline]
signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration248     pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration {
249         self.date.signed_duration_since(rhs.date)
250     }
251 
252     /// Returns a view to the naive UTC date.
253     #[inline]
naive_utc(&self) -> NaiveDate254     pub fn naive_utc(&self) -> NaiveDate {
255         self.date
256     }
257 
258     /// Returns a view to the naive local date.
259     ///
260     /// This is technically the same as [`naive_utc`](#method.naive_utc)
261     /// because the offset is restricted to never exceed one day,
262     /// but provided for the consistency.
263     #[inline]
naive_local(&self) -> NaiveDate264     pub fn naive_local(&self) -> NaiveDate {
265         self.date
266     }
267 }
268 
269 /// Maps the local date to other date with given conversion function.
map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>> where F: FnMut(NaiveDate) -> Option<NaiveDate>,270 fn map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>>
271 where
272     F: FnMut(NaiveDate) -> Option<NaiveDate>,
273 {
274     f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single())
275 }
276 
277 impl<Tz: TimeZone> Date<Tz>
278 where
279     Tz::Offset: fmt::Display,
280 {
281     /// Formats the date with the specified formatting items.
282     #[cfg(any(feature = "alloc", feature = "std", test))]
283     #[inline]
format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> where I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>,284     pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
285     where
286         I: Iterator<Item = B> + Clone,
287         B: Borrow<Item<'a>>,
288     {
289         DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
290     }
291 
292     /// Formats the date with the specified format string.
293     /// See the [`format::strftime` module](./format/strftime/index.html)
294     /// on the supported escape sequences.
295     #[cfg(any(feature = "alloc", feature = "std", test))]
296     #[inline]
format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>>297     pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
298         self.format_with_items(StrftimeItems::new(fmt))
299     }
300 
301     /// Formats the date with the specified formatting items and locale.
302     #[cfg(feature = "unstable-locales")]
303     #[inline]
format_localized_with_items<'a, I, B>( &self, items: I, locale: Locale, ) -> DelayedFormat<I> where I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>,304     pub fn format_localized_with_items<'a, I, B>(
305         &self,
306         items: I,
307         locale: Locale,
308     ) -> DelayedFormat<I>
309     where
310         I: Iterator<Item = B> + Clone,
311         B: Borrow<Item<'a>>,
312     {
313         DelayedFormat::new_with_offset_and_locale(
314             Some(self.naive_local()),
315             None,
316             &self.offset,
317             items,
318             locale,
319         )
320     }
321 
322     /// Formats the date with the specified format string and locale.
323     /// See the [`format::strftime` module](./format/strftime/index.html)
324     /// on the supported escape sequences.
325     #[cfg(feature = "unstable-locales")]
326     #[inline]
format_localized<'a>( &self, fmt: &'a str, locale: Locale, ) -> DelayedFormat<StrftimeItems<'a>>327     pub fn format_localized<'a>(
328         &self,
329         fmt: &'a str,
330         locale: Locale,
331     ) -> DelayedFormat<StrftimeItems<'a>> {
332         self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale)
333     }
334 }
335 
336 impl<Tz: TimeZone> Datelike for Date<Tz> {
337     #[inline]
year(&self) -> i32338     fn year(&self) -> i32 {
339         self.naive_local().year()
340     }
341     #[inline]
month(&self) -> u32342     fn month(&self) -> u32 {
343         self.naive_local().month()
344     }
345     #[inline]
month0(&self) -> u32346     fn month0(&self) -> u32 {
347         self.naive_local().month0()
348     }
349     #[inline]
day(&self) -> u32350     fn day(&self) -> u32 {
351         self.naive_local().day()
352     }
353     #[inline]
day0(&self) -> u32354     fn day0(&self) -> u32 {
355         self.naive_local().day0()
356     }
357     #[inline]
ordinal(&self) -> u32358     fn ordinal(&self) -> u32 {
359         self.naive_local().ordinal()
360     }
361     #[inline]
ordinal0(&self) -> u32362     fn ordinal0(&self) -> u32 {
363         self.naive_local().ordinal0()
364     }
365     #[inline]
weekday(&self) -> Weekday366     fn weekday(&self) -> Weekday {
367         self.naive_local().weekday()
368     }
369     #[inline]
iso_week(&self) -> IsoWeek370     fn iso_week(&self) -> IsoWeek {
371         self.naive_local().iso_week()
372     }
373 
374     #[inline]
with_year(&self, year: i32) -> Option<Date<Tz>>375     fn with_year(&self, year: i32) -> Option<Date<Tz>> {
376         map_local(self, |date| date.with_year(year))
377     }
378 
379     #[inline]
with_month(&self, month: u32) -> Option<Date<Tz>>380     fn with_month(&self, month: u32) -> Option<Date<Tz>> {
381         map_local(self, |date| date.with_month(month))
382     }
383 
384     #[inline]
with_month0(&self, month0: u32) -> Option<Date<Tz>>385     fn with_month0(&self, month0: u32) -> Option<Date<Tz>> {
386         map_local(self, |date| date.with_month0(month0))
387     }
388 
389     #[inline]
with_day(&self, day: u32) -> Option<Date<Tz>>390     fn with_day(&self, day: u32) -> Option<Date<Tz>> {
391         map_local(self, |date| date.with_day(day))
392     }
393 
394     #[inline]
with_day0(&self, day0: u32) -> Option<Date<Tz>>395     fn with_day0(&self, day0: u32) -> Option<Date<Tz>> {
396         map_local(self, |date| date.with_day0(day0))
397     }
398 
399     #[inline]
with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>>400     fn with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>> {
401         map_local(self, |date| date.with_ordinal(ordinal))
402     }
403 
404     #[inline]
with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>>405     fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>> {
406         map_local(self, |date| date.with_ordinal0(ordinal0))
407     }
408 }
409 
410 // we need them as automatic impls cannot handle associated types
411 impl<Tz: TimeZone> Copy for Date<Tz> where <Tz as TimeZone>::Offset: Copy {}
412 unsafe impl<Tz: TimeZone> Send for Date<Tz> where <Tz as TimeZone>::Offset: Send {}
413 
414 impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Date<Tz2>> for Date<Tz> {
eq(&self, other: &Date<Tz2>) -> bool415     fn eq(&self, other: &Date<Tz2>) -> bool {
416         self.date == other.date
417     }
418 }
419 
420 impl<Tz: TimeZone> Eq for Date<Tz> {}
421 
422 impl<Tz: TimeZone> PartialOrd for Date<Tz> {
partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering>423     fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> {
424         self.date.partial_cmp(&other.date)
425     }
426 }
427 
428 impl<Tz: TimeZone> Ord for Date<Tz> {
cmp(&self, other: &Date<Tz>) -> Ordering429     fn cmp(&self, other: &Date<Tz>) -> Ordering {
430         self.date.cmp(&other.date)
431     }
432 }
433 
434 impl<Tz: TimeZone> hash::Hash for Date<Tz> {
hash<H: hash::Hasher>(&self, state: &mut H)435     fn hash<H: hash::Hasher>(&self, state: &mut H) {
436         self.date.hash(state)
437     }
438 }
439 
440 impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
441     type Output = Date<Tz>;
442 
443     #[inline]
add(self, rhs: OldDuration) -> Date<Tz>444     fn add(self, rhs: OldDuration) -> Date<Tz> {
445         self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
446     }
447 }
448 
449 impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> {
450     type Output = Date<Tz>;
451 
452     #[inline]
sub(self, rhs: OldDuration) -> Date<Tz>453     fn sub(self, rhs: OldDuration) -> Date<Tz> {
454         self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed")
455     }
456 }
457 
458 impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> {
459     type Output = OldDuration;
460 
461     #[inline]
sub(self, rhs: Date<Tz>) -> OldDuration462     fn sub(self, rhs: Date<Tz>) -> OldDuration {
463         self.signed_duration_since(rhs)
464     }
465 }
466 
467 impl<Tz: TimeZone> fmt::Debug for Date<Tz> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result468     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
469         write!(f, "{:?}{:?}", self.naive_local(), self.offset)
470     }
471 }
472 
473 impl<Tz: TimeZone> fmt::Display for Date<Tz>
474 where
475     Tz::Offset: fmt::Display,
476 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result477     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478         write!(f, "{}{}", self.naive_local(), self.offset)
479     }
480 }
481