datetime/cal/datetime.rs
1//! Dates, times, datetimes, months, and weekdays.
2
3use std::cmp::{Ordering, PartialOrd};
4use std::error::Error as ErrorTrait;
5use std::fmt;
6use std::ops::{Add, Sub};
7use std::ops::Deref;
8use std::ops::{Range, RangeFrom, RangeTo, RangeFull};
9use std::slice::Iter as SliceIter;
10
11use cal::{DatePiece, TimePiece};
12use cal::fmt::ISO;
13use duration::Duration;
14use instant::Instant;
15use system::sys_time;
16use util::RangeExt;
17
18use self::Month::*;
19use self::Weekday::*;
20
21
22/// A single year.
23///
24/// This is just a wrapper around `i64` that performs year-related tests.
25#[derive(PartialEq, Debug, Copy, Clone)]
26pub struct Year(pub i64);
27
28impl Year {
29
30 /// Returns whether this year is a leap year.
31 ///
32 /// ### Examples
33 ///
34 /// ```
35 /// use datetime::Year;
36 ///
37 /// assert_eq!(Year(2000).is_leap_year(), true);
38 /// assert_eq!(Year(1900).is_leap_year(), false);
39 /// ```
40 pub fn is_leap_year(self) -> bool {
41 self.leap_year_calculations().1
42 }
43
44 /// Returns an iterator over a continuous span of months in this year,
45 /// returning year-month pairs.
46 ///
47 /// This method takes one argument that can be of four different types,
48 /// depending on the months you wish to iterate over:
49 ///
50 /// - The `RangeFull` type (such as `..`), which iterates over every
51 /// month;
52 /// - The `RangeFrom` type (such as `April ..`), which iterates over
53 /// the months starting from the month given;
54 /// - The `RangeTo` type (such as `.. June`), which iterates over the
55 /// months stopping at *but not including* the month given;
56 /// - The `Range` type (such as `April .. June`), which iterates over
57 /// the months starting from the left one and stopping at *but not
58 /// including* the right one.
59 ///
60 /// ### Examples
61 ///
62 /// ```
63 /// use datetime::Year;
64 /// use datetime::Month::{April, June};
65 ///
66 /// let year = Year(1999);
67 /// assert_eq!(year.months(..).count(), 12);
68 /// assert_eq!(year.months(April ..).count(), 9);
69 /// assert_eq!(year.months(April .. June).count(), 2);
70 /// assert_eq!(year.months(.. June).count(), 5);
71 /// ```
72 pub fn months<S: MonthSpan>(self, span: S) -> YearMonths {
73 YearMonths {
74 year: self,
75 iter: span.get_slice().iter(),
76 }
77 }
78
79 /// Returns a year-month, pairing this year with the given month.
80 ///
81 /// ### Examples
82 ///
83 /// ```
84 /// use datetime::{Year, Month};
85 ///
86 /// let expiry_date = Year(2017).month(Month::February);
87 /// assert_eq!(*expiry_date.year, 2017);
88 /// assert_eq!(expiry_date.month, Month::February);
89 /// ```
90 pub fn month(self, month: Month) -> YearMonth {
91 YearMonth {
92 year: self,
93 month,
94 }
95 }
96
97 /// Performs two related calculations for leap years, returning the
98 /// results as a two-part tuple:
99 ///
100 /// 1. The number of leap years that have elapsed prior to this year;
101 /// 2. Whether this year is a leap year or not.
102 fn leap_year_calculations(self) -> (i64, bool) {
103 let year = self.0 - 2000;
104
105 // This calculation is the reverse of LocalDate::from_days_since_epoch.
106 let (num_400y_cycles, mut remainder) = split_cycles(year, 400);
107
108 // Standard leap-year calculations, performed on the remainder
109 let currently_leap_year = remainder == 0 || (remainder % 100 != 0 && remainder % 4 == 0);
110
111 let num_100y_cycles = remainder / 100;
112 remainder -= num_100y_cycles * 100;
113
114 let leap_years_elapsed = remainder / 4
115 + 97 * num_400y_cycles // There are 97 leap years in 400 years
116 + 24 * num_100y_cycles // There are 24 leap years in 100 years
117 - if currently_leap_year { 1 } else { 0 };
118
119 (leap_years_elapsed, currently_leap_year)
120 }
121}
122
123impl Deref for Year {
124 type Target = i64;
125
126 fn deref(&self) -> &Self::Target {
127 &self.0
128 }
129}
130
131/// A span of months, which gets used to construct a `YearMonths` iterator.
132///
133/// See the `months` method of `Year` for more information.
134pub trait MonthSpan {
135
136 /// Returns a static slice of `Month` values contained by this span.
137 fn get_slice(&self) -> &'static [Month];
138}
139
140static MONTHS: &[Month] = &[
141 January, February, March,
142 April, May, June,
143 July, August, September,
144 October, November, December,
145];
146
147impl MonthSpan for RangeFull {
148 fn get_slice(&self) -> &'static [Month] {
149 MONTHS
150 }
151}
152
153impl MonthSpan for RangeFrom<Month> {
154 fn get_slice(&self) -> &'static [Month] {
155 &MONTHS[self.start.months_from_january() ..]
156 }
157}
158
159impl MonthSpan for RangeTo<Month> {
160 fn get_slice(&self) -> &'static [Month] {
161 &MONTHS[.. self.end.months_from_january()]
162 }
163}
164
165impl MonthSpan for Range<Month> {
166 fn get_slice(&self) -> &'static [Month] {
167 &MONTHS[self.start.months_from_january() .. self.end.months_from_january()]
168 }
169}
170
171
172/// An iterator over a continuous span of months in a year.
173///
174/// Use the `months` method on `Year` to create instances of this iterator.
175pub struct YearMonths {
176 year: Year,
177 iter: SliceIter<'static, Month>,
178}
179
180impl Iterator for YearMonths {
181 type Item = YearMonth;
182
183 fn next(&mut self) -> Option<YearMonth> {
184 self.iter.next().map(|m| YearMonth {
185 year: self.year,
186 month: *m,
187 })
188 }
189}
190
191impl DoubleEndedIterator for YearMonths {
192 fn next_back(&mut self) -> Option<Self::Item> {
193 self.iter.next_back().map(|m| YearMonth {
194 year: self.year,
195 month: *m,
196 })
197 }
198}
199
200impl fmt::Debug for YearMonths {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 write!(f, "YearMonths({}, {:?})", self.year.0, self.iter.as_slice())
203 }
204}
205
206/// A month-year pair.
207#[derive(PartialEq, Debug, Copy, Clone)]
208pub struct YearMonth {
209 pub year: Year,
210 pub month: Month,
211}
212
213impl YearMonth {
214
215 /// Returns the number of days in this month. This can be definitely
216 /// known, as the paired year determines whether it’s a leap year, so
217 /// there’s no chance of being caught out by February.
218 ///
219 /// ### Examples
220 ///
221 /// ```
222 /// use datetime::Year;
223 /// use datetime::Month::February;
224 ///
225 /// assert_eq!(Year(2000).month(February).day_count(), 29);
226 /// assert_eq!(Year(1900).month(February).day_count(), 28);
227 /// ```
228 pub fn day_count(&self) -> i8 {
229 self.month.days_in_month(self.year.is_leap_year())
230 }
231
232 /// Returns an iterator over a continuous span of days in this month,
233 /// returning `LocalDate` values.
234 ///
235 /// ### Examples
236 ///
237 /// ```
238 /// use datetime::Year;
239 /// use datetime::Month::September;
240 ///
241 /// let ym = Year(1999).month(September);
242 /// assert_eq!(ym.days(..).count(), 30);
243 /// assert_eq!(ym.days(10 ..).count(), 21);
244 /// assert_eq!(ym.days(10 .. 20).count(), 10);
245 /// assert_eq!(ym.days(.. 20).count(), 19);
246 /// ```
247 pub fn days<S: DaySpan>(&self, span: S) -> MonthDays {
248 MonthDays {
249 ym: *self,
250 range: span.get_range(self)
251 }
252 }
253
254 /// Returns a `LocalDate` based on the day of this month.
255 ///
256 /// This is just a short-cut for the `LocalDate::ymd` constructor.
257 pub fn day(&self, day: i8) -> Result<LocalDate, Error> {
258 LocalDate::ymd(self.year.0, self.month, day)
259 }
260}
261
262
263/// A span of days, which gets used to construct a `MonthDays` iterator.
264pub trait DaySpan {
265
266 /// Returns a `Range` of the day numbers specified for the given year-month pair.
267 fn get_range(&self, ym: &YearMonth) -> Range<i8>;
268}
269
270impl DaySpan for RangeFull {
271 fn get_range(&self, ym: &YearMonth) -> Range<i8> {
272 1 .. ym.day_count() + 1
273 }
274}
275
276impl DaySpan for RangeFrom<i8> {
277 fn get_range(&self, ym: &YearMonth) -> Range<i8> {
278 self.start .. ym.day_count() + 1
279 }
280}
281
282impl DaySpan for RangeTo<i8> {
283 fn get_range(&self, _ym: &YearMonth) -> Range<i8> {
284 1 .. self.end
285 }
286}
287
288impl DaySpan for Range<i8> {
289 fn get_range(&self, _ym: &YearMonth) -> Range<i8> {
290 self.clone()
291 }
292}
293
294
295/// An iterator over a continuous span of days in a month.
296///
297/// Use the `days` method on `YearMonth` to create instances of this iterator.
298#[derive(PartialEq, Debug)]
299pub struct MonthDays {
300 ym: YearMonth,
301 range: Range<i8>,
302}
303
304impl Iterator for MonthDays {
305 type Item = LocalDate;
306
307 fn next(&mut self) -> Option<Self::Item> {
308 self.range.next().and_then(|d| LocalDate::ymd(self.ym.year.0, self.ym.month, d).ok())
309 }
310}
311
312impl DoubleEndedIterator for MonthDays {
313 fn next_back(&mut self) -> Option<Self::Item> {
314 self.range.next_back().and_then(|d| LocalDate::ymd(self.ym.year.0, self.ym.month, d).ok())
315 }
316}
317
318
319/// Number of days guaranteed to be in four years.
320const DAYS_IN_4Y: i64 = 365 * 4 + 1;
321
322/// Number of days guaranteed to be in a hundred years.
323const DAYS_IN_100Y: i64 = 365 * 100 + 24;
324
325/// Number of days guaranteed to be in four hundred years.
326const DAYS_IN_400Y: i64 = 365 * 400 + 97;
327
328/// Number of seconds in a day. As everywhere in this library, leap seconds
329/// are simply ignored.
330const SECONDS_IN_DAY: i64 = 86400;
331
332
333/// Number of days between **1st January, 1970** and **1st March, 2000**.
334///
335/// This might seem like an odd number to calculate, instead of using the
336/// 1st of January as a reference point, but it turs out that by having the
337/// reference point immediately after a possible leap-year day, the maths
338/// needed to calculate the day/week/month of an instant comes out a *lot*
339/// simpler!
340///
341/// The Gregorian calendar operates on a 400-year cycle, so the combination
342/// of having it on a year that’s a multiple of 400, and having the leap
343/// day at the very end of one of these cycles, means that the calculations
344/// are reduced to simple division (of course, with a bit of date-shifting
345/// to base a date around this reference point).
346///
347/// Rust has the luxury of having been started *after* this date. In Win32,
348/// the epoch is midnight, the 1st of January, 1601, for much the same
349/// reasons - except that it was developed before the year 2000, so they
350/// had to go all the way back to the *previous* 400-year multiple.[^win32]
351///
352/// The only problem is that many people assume the Unix epoch to be
353/// midnight on the 1st January 1970, so this value (and any functions that
354/// depend on it) aren’t exposed to users of this library.
355///
356/// [^win32]: http://blogs.msdn.com/b/oldnewthing/archive/2009/03/06/9461176.aspx
357///
358const EPOCH_DIFFERENCE: i64 = 30 * 365 // 30 years between 2000 and 1970...
359 + 7 // plus seven days for leap years...
360 + 31 + 29; // plus all the days in January and February in 2000.
361
362
363/// This rather strange triangle is an array of the number of days elapsed
364/// at the end of each month, starting at the beginning of March (the first
365/// month after the EPOCH above), going backwards, ignoring February.
366const TIME_TRIANGLE: &[i64; 11] =
367 &[31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 + 31, // January
368 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, // December
369 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, // November
370 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, // October
371 31 + 30 + 31 + 30 + 31 + 31 + 30, // September
372 31 + 30 + 31 + 30 + 31 + 31, // August
373 31 + 30 + 31 + 30 + 31, // July
374 31 + 30 + 31 + 30, // June
375 31 + 30 + 31, // May
376 31 + 30, // April
377 31]; // March
378
379
380
381/// A **local date** is a day-long span on the timeline, *without a time
382/// zone*.
383#[derive(Eq, Clone, Copy)]
384pub struct LocalDate {
385 ymd: YMD,
386 yearday: i16,
387 weekday: Weekday,
388}
389
390/// A **local time** is a time on the timeline that recurs once a day,
391/// *without a time zone*.
392#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
393pub struct LocalTime {
394 hour: i8,
395 minute: i8,
396 second: i8,
397 millisecond: i16,
398}
399
400/// A **local date-time** is an exact instant on the timeline, *without a
401/// time zone*.
402#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
403pub struct LocalDateTime {
404 date: LocalDate,
405 time: LocalTime,
406}
407
408
409impl LocalDate {
410
411 /// Creates a new local date instance from the given year, month, and day
412 /// fields.
413 ///
414 /// The values are checked for validity before instantiation, and
415 /// passing in values out of range will return an error.
416 ///
417 /// ### Examples
418 ///
419 /// Instantiate the 20th of July 1969 based on its year,
420 /// week-of-year, and weekday.
421 ///
422 /// ```rust
423 /// use datetime::{LocalDate, Month, DatePiece};
424 ///
425 /// let date = LocalDate::ymd(1969, Month::July, 20).unwrap();
426 /// assert_eq!(date.year(), 1969);
427 /// assert_eq!(date.month(), Month::July);
428 /// assert_eq!(date.day(), 20);
429 ///
430 /// assert!(LocalDate::ymd(2100, Month::February, 29).is_err());
431 /// ```
432 pub fn ymd(year: i64, month: Month, day: i8) -> Result<Self, Error> {
433 YMD { year, month, day }
434 .to_days_since_epoch()
435 .map(|days| Self::from_days_since_epoch(days - EPOCH_DIFFERENCE))
436 }
437
438 /// Creates a new local date instance from the given year and day-of-year
439 /// values.
440 ///
441 /// The values are checked for validity before instantiation, and
442 /// passing in values out of range will return an error.
443 ///
444 /// ### Examples
445 ///
446 /// Instantiate the 13th of September 2015 based on its year
447 /// and day-of-year.
448 ///
449 /// ```rust
450 /// use datetime::{LocalDate, Weekday, Month, DatePiece};
451 ///
452 /// let date = LocalDate::yd(2015, 0x100).unwrap();
453 /// assert_eq!(date.year(), 2015);
454 /// assert_eq!(date.month(), Month::September);
455 /// assert_eq!(date.day(), 13);
456 /// ```
457 pub fn yd(year: i64, yearday: i64) -> Result<Self, Error> {
458 if yearday.is_within(0..367) {
459 let jan_1 = YMD { year, month: January, day: 1 };
460 let days = jan_1.to_days_since_epoch()?;
461 Ok(Self::from_days_since_epoch(days + yearday - 1 - EPOCH_DIFFERENCE))
462 }
463 else {
464 Err(Error::OutOfRange)
465 }
466 }
467
468 /// Creates a new local date instance from the given year, week-of-year,
469 /// and weekday values.
470 ///
471 /// The values are checked for validity before instantiation, and
472 /// passing in values out of range will return an error.
473 ///
474 /// ### Examples
475 ///
476 /// Instantiate the 11th of September 2015 based on its year,
477 /// week-of-year, and weekday.
478 ///
479 /// ```rust
480 /// use datetime::{LocalDate, Weekday, Month, DatePiece};
481 ///
482 /// let date = LocalDate::ywd(2015, 37, Weekday::Friday).unwrap();
483 /// assert_eq!(date.year(), 2015);
484 /// assert_eq!(date.month(), Month::September);
485 /// assert_eq!(date.day(), 11);
486 /// assert_eq!(date.weekday(), Weekday::Friday);
487 /// ```
488 ///
489 /// Note that according to the ISO-8601 standard, the year will change
490 /// when working with dates early in week 1, or late in week 53:
491 ///
492 /// ```rust
493 /// use datetime::{LocalDate, Weekday, Month, DatePiece};
494 ///
495 /// let date = LocalDate::ywd(2009, 1, Weekday::Monday).unwrap();
496 /// assert_eq!(date.year(), 2008);
497 /// assert_eq!(date.month(), Month::December);
498 /// assert_eq!(date.day(), 29);
499 /// assert_eq!(date.weekday(), Weekday::Monday);
500 ///
501 /// let date = LocalDate::ywd(2009, 53, Weekday::Sunday).unwrap();
502 /// assert_eq!(date.year(), 2010);
503 /// assert_eq!(date.month(), Month::January);
504 /// assert_eq!(date.day(), 3);
505 /// assert_eq!(date.weekday(), Weekday::Sunday);
506 /// ```
507 pub fn ywd(year: i64, week: i64, weekday: Weekday) -> Result<Self, Error> {
508 let jan_4 = YMD { year, month: January, day: 4 };
509 let correction = days_to_weekday(jan_4.to_days_since_epoch().unwrap() - EPOCH_DIFFERENCE).days_from_monday_as_one() as i64 + 3;
510
511 let yearday = 7 * week + weekday.days_from_monday_as_one() as i64 - correction;
512
513 if yearday <= 0 {
514 let days_in_year = if Year(year - 1).is_leap_year() { 366 } else { 365 };
515 Self::yd(year - 1, days_in_year + yearday)
516 }
517 else {
518 let days_in_year = if Year(year).is_leap_year() { 366 } else { 365 };
519
520 if yearday >= days_in_year {
521 Self::yd(year + 1, yearday - days_in_year)
522 }
523 else {
524 Self::yd(year, yearday)
525 }
526 }
527 }
528
529 /// Computes a LocalDate - year, month, day, weekday, and yearday -
530 /// given the number of days that have passed since the EPOCH.
531 ///
532 /// This is used by all the other constructor functions.
533 /// ### Examples
534 ///
535 /// Instantiate the 25th of September 2015 given its day-of-year (268).
536 ///
537 /// ```rust
538 /// use datetime::{LocalDate, Month, DatePiece};
539 ///
540 /// let date = LocalDate::yd(2015, 268).unwrap();
541 /// assert_eq!(date.year(), 2015);
542 /// assert_eq!(date.month(), Month::September);
543 /// assert_eq!(date.day(), 25);
544 /// ```
545 ///
546 /// Remember that on leap years, the number of days in a year changes:
547 ///
548 /// ```rust
549 /// use datetime::{LocalDate, Month, DatePiece};
550 ///
551 /// let date = LocalDate::yd(2016, 268).unwrap();
552 /// assert_eq!(date.year(), 2016);
553 /// assert_eq!(date.month(), Month::September);
554 /// assert_eq!(date.day(), 24); // not the 25th!
555 /// ```
556 fn from_days_since_epoch(days: i64) -> Self {
557
558 // The Gregorian calendar works in 400-year cycles, which repeat
559 // themselves ever after.
560 //
561 // This calculation works by finding the number of 400-year,
562 // 100-year, and 4-year cycles, then constantly subtracting the
563 // number of leftover days.
564 let (num_400y_cycles, mut remainder) = split_cycles(days, DAYS_IN_400Y);
565
566 // Calculate the numbers of 100-year cycles, 4-year cycles, and
567 // leftover years, continually reducing the number of days left to
568 // think about.
569 let num_100y_cycles = remainder / DAYS_IN_100Y;
570 remainder -= num_100y_cycles * DAYS_IN_100Y; // remainder is now days left in this 100-year cycle
571
572 let num_4y_cycles = remainder / DAYS_IN_4Y;
573 remainder -= num_4y_cycles * DAYS_IN_4Y; // remainder is now days left in this 4-year cycle
574
575 let mut years = std::cmp::min(remainder / 365, 3);
576 remainder -= years * 365; // remainder is now days left in this year
577
578 // Leap year calculation goes thusly:
579 //
580 // 1. If the year is a multiple of 400, it’s a leap year.
581 // 2. Else, if the year is a multiple of 100, it’s *not* a leap year.
582 // 3. Else, if the year is a multiple of 4, it’s a leap year again!
583 //
584 // We already have the values for the numbers of multiples at this
585 // point, and it’s safe to re-use them.
586 let days_this_year =
587 if years == 0 && !(num_4y_cycles == 0 && num_100y_cycles != 0) { 366 }
588 else { 365 };
589
590 // Find out which number day of the year it is.
591 // The 306 here refers to the number of days in a year excluding
592 // January and February (which are excluded because of the EPOCH)
593 let mut day_of_year = remainder + days_this_year - 306;
594 if day_of_year >= days_this_year {
595 day_of_year -= days_this_year; // wrap around for January and February
596 }
597
598 // Turn all those cycles into an actual number of years.
599 years += 4 * num_4y_cycles
600 + 100 * num_100y_cycles
601 + 400 * num_400y_cycles;
602
603 // Work out the month and number of days into the month by scanning
604 // the time triangle, finding the month that has the correct number
605 // of days elapsed at the end of it.
606 // (it’s “11 - index” below because the triangle goes backwards)
607 let result = TIME_TRIANGLE.iter()
608 .enumerate()
609 .find(|&(_, days)| *days <= remainder);
610
611 let (mut month, month_days) = match result {
612 Some((index, days)) => (11 - index, remainder - *days),
613 None => (0, remainder), // No month found? Then it’s February.
614 };
615
616 // Need to add 2 to the month in order to compensate for the EPOCH
617 // being in March.
618 month += 2;
619
620 if month >= 12 {
621 years += 1; // wrap around for January and February
622 month -= 12; // (yes, again)
623 }
624
625 // The check immediately above means we can `unwrap` this, as the
626 // month number is guaranteed to be in the range (0..12).
627 let month_variant = Month::from_zero(month as i8).unwrap();
628
629 // Finally, adjust the day numbers for human reasons: the first day
630 // of the month is the 1st, rather than the 0th, and the year needs
631 // to be adjusted relative to the EPOCH.
632 Self {
633 yearday: (day_of_year + 1) as i16,
634 weekday: days_to_weekday(days),
635 ymd: YMD {
636 year: years + 2000,
637 month: month_variant,
638 day: (month_days + 1) as i8,
639 },
640 }
641 }
642
643 /// Creates a new datestamp instance with the given year, month, day,
644 /// weekday, and yearday fields.
645 ///
646 /// This function is unsafe because **the values are not checked for
647 /// validity!** It’s possible to pass the wrong values in, such as having
648 /// a wrong day value for a month, or having the yearday value out of
649 /// step. Before using it, check that the values are all correct - or just
650 /// use the `date!()` macro, which does this for you at compile-time.
651 ///
652 /// For this reason, the function is marked as `unsafe`, even though it
653 /// (technically) uses unsafe components.
654 pub unsafe fn _new_with_prefilled_values(year: i64, month: Month, day: i8, weekday: Weekday, yearday: i16) -> Self {
655 Self {
656 ymd: YMD { year, month, day },
657 weekday,
658 yearday,
659 }
660 }
661
662 // I’m not 100% convinced on using `unsafe` for something that doesn’t
663 // technically *need* to be unsafe, but I’ll stick with it for now.
664}
665
666impl DatePiece for LocalDate {
667 fn year(&self) -> i64 { self.ymd.year }
668 fn month(&self) -> Month { self.ymd.month }
669 fn day(&self) -> i8 { self.ymd.day }
670 fn yearday(&self) -> i16 { self.yearday }
671 fn weekday(&self) -> Weekday { self.weekday }
672}
673
674impl fmt::Debug for LocalDate {
675 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
676 write!(f, "LocalDate({})", self.iso())
677 }
678}
679
680impl PartialEq for LocalDate {
681 fn eq(&self, other: &Self) -> bool {
682 self.ymd == other.ymd
683 }
684}
685
686impl PartialOrd for LocalDate {
687 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
688 self.ymd.partial_cmp(&other.ymd)
689 }
690}
691
692impl Ord for LocalDate {
693 fn cmp(&self, other: &Self) -> Ordering {
694 self.ymd.cmp(&other.ymd)
695 }
696}
697
698impl LocalTime {
699
700 /// Computes the number of hours, minutes, and seconds, based on the
701 /// number of seconds that have elapsed since midnight.
702 pub fn from_seconds_since_midnight(seconds: i64) -> Self {
703 Self::from_seconds_and_milliseconds_since_midnight(seconds, 0)
704 }
705
706 /// Computes the number of hours, minutes, and seconds, based on the
707 /// number of seconds that have elapsed since midnight.
708 pub fn from_seconds_and_milliseconds_since_midnight(seconds: i64, millisecond_of_second: i16) -> Self {
709 Self {
710 hour: (seconds / 60 / 60) as i8,
711 minute: (seconds / 60 % 60) as i8,
712 second: (seconds % 60) as i8,
713 millisecond: millisecond_of_second,
714 }
715 }
716
717 /// Returns the time at midnight, with all fields initialised to 0.
718 pub fn midnight() -> Self {
719 Self { hour: 0, minute: 0, second: 0, millisecond: 0 }
720 }
721
722 /// Creates a new timestamp instance with the given hour and minute
723 /// fields. The second and millisecond fields are set to 0.
724 ///
725 /// The values are checked for validity before instantiation, and
726 /// passing in values out of range will return an `Err`.
727 pub fn hm(hour: i8, minute: i8) -> Result<Self, Error> {
728 if (hour.is_within(0..24) && minute.is_within(0..60))
729 || (hour == 24 && minute == 00) {
730 Ok(Self { hour, minute, second: 0, millisecond: 0 })
731 }
732 else {
733 Err(Error::OutOfRange)
734 }
735 }
736
737 /// Creates a new timestamp instance with the given hour, minute, and
738 /// second fields. The millisecond field is set to 0.
739 ///
740 /// The values are checked for validity before instantiation, and
741 /// passing in values out of range will return an `Err`.
742 pub fn hms(hour: i8, minute: i8, second: i8) -> Result<Self, Error> {
743 if (hour.is_within(0..24) && minute.is_within(0..60) && second.is_within(0..60))
744 || (hour == 24 && minute == 00 && second == 00) {
745 Ok(Self { hour, minute, second, millisecond: 0 })
746 }
747 else {
748 Err(Error::OutOfRange)
749 }
750 }
751
752 /// Creates a new timestamp instance with the given hour, minute,
753 /// second, and millisecond fields.
754 ///
755 /// The values are checked for validity before instantiation, and
756 /// passing in values out of range will return an `Err`.
757 pub fn hms_ms(hour: i8, minute: i8, second: i8, millisecond: i16) -> Result<Self, Error> {
758 if hour.is_within(0..24) && minute.is_within(0..60)
759 && second.is_within(0..60) && millisecond.is_within(0..1000)
760 {
761 Ok(Self { hour, minute, second, millisecond })
762 }
763 else {
764 Err(Error::OutOfRange)
765 }
766 }
767
768 /// Calculate the number of seconds since midnight this time is at,
769 /// ignoring milliseconds.
770 pub fn to_seconds(self) -> i64 {
771 self.hour as i64 * 3600
772 + self.minute as i64 * 60
773 + self.second as i64
774 }
775}
776
777impl TimePiece for LocalTime {
778 fn hour(&self) -> i8 { self.hour }
779 fn minute(&self) -> i8 { self.minute }
780 fn second(&self) -> i8 { self.second }
781 fn millisecond(&self) -> i16 { self.millisecond }
782}
783
784impl fmt::Debug for LocalTime {
785 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
786 write!(f, "LocalTime({})", self.iso())
787 }
788}
789
790
791impl LocalDateTime {
792
793 /// Computes a complete date-time based on the values in the given
794 /// Instant parameter.
795 pub fn from_instant(instant: Instant) -> Self {
796 Self::at_ms(instant.seconds(), instant.milliseconds())
797 }
798
799 /// Computes a complete date-time based on the number of seconds that
800 /// have elapsed since **midnight, 1st January, 1970**, setting the
801 /// number of milliseconds to 0.
802 pub fn at(seconds_since_1970_epoch: i64) -> Self {
803 Self::at_ms(seconds_since_1970_epoch, 0)
804 }
805
806 /// Computes a complete date-time based on the number of seconds that
807 /// have elapsed since **midnight, 1st January, 1970**,
808 pub fn at_ms(seconds_since_1970_epoch: i64, millisecond_of_second: i16) -> Self {
809 let seconds = seconds_since_1970_epoch - EPOCH_DIFFERENCE * SECONDS_IN_DAY;
810
811 // Just split the input value into days and seconds, and let
812 // LocalDate and LocalTime do all the hard work.
813 let (days, secs) = split_cycles(seconds, SECONDS_IN_DAY);
814
815 Self {
816 date: LocalDate::from_days_since_epoch(days),
817 time: LocalTime::from_seconds_and_milliseconds_since_midnight(secs, millisecond_of_second),
818 }
819 }
820
821 /// Creates a new local date time from a local date and a local time.
822 pub fn new(date: LocalDate, time: LocalTime) -> Self {
823 Self {
824 date,
825 time,
826 }
827 }
828
829 /// Returns the date portion of this date-time stamp.
830 pub fn date(&self) -> LocalDate {
831 self.date
832 }
833
834 /// Returns the time portion of this date-time stamp.
835 pub fn time(&self) -> LocalTime {
836 self.time
837 }
838
839 /// Creates a new date-time stamp set to the current time.
840 #[cfg_attr(target_os = "redox", allow(unused_unsafe))]
841 pub fn now() -> Self {
842 let (s, ms) = unsafe { sys_time() };
843 Self::at_ms(s, ms)
844 }
845
846 pub fn to_instant(&self) -> Instant {
847 let seconds = self.date.ymd.to_days_since_epoch().unwrap() * SECONDS_IN_DAY + self.time.to_seconds();
848 Instant::at_ms(seconds, self.time.millisecond)
849 }
850
851 pub fn add_seconds(&self, seconds: i64) -> Self {
852 Self::from_instant(self.to_instant() + Duration::of(seconds))
853 }
854}
855
856impl DatePiece for LocalDateTime {
857 fn year(&self) -> i64 { self.date.ymd.year }
858 fn month(&self) -> Month { self.date.ymd.month }
859 fn day(&self) -> i8 { self.date.ymd.day }
860 fn yearday(&self) -> i16 { self.date.yearday }
861 fn weekday(&self) -> Weekday { self.date.weekday }
862}
863
864impl TimePiece for LocalDateTime {
865 fn hour(&self) -> i8 { self.time.hour }
866 fn minute(&self) -> i8 { self.time.minute }
867 fn second(&self) -> i8 { self.time.second }
868 fn millisecond(&self) -> i16 { self.time.millisecond }
869}
870
871impl fmt::Debug for LocalDateTime {
872 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
873 write!(f, "LocalDateTime({})", self.iso())
874 }
875}
876
877impl Add<Duration> for LocalDateTime {
878 type Output = Self;
879
880 fn add(self, duration: Duration) -> Self {
881 Self::from_instant(self.to_instant() + duration)
882 }
883}
884
885impl Sub<Duration> for LocalDateTime {
886 type Output = Self;
887
888 fn sub(self, duration: Duration) -> Self {
889 Self::from_instant(self.to_instant() - duration)
890 }
891}
892
893
894/// A **YMD** is an implementation detail of `LocalDate`. It provides
895/// helper methods relating to the construction of `LocalDate` instances.
896///
897/// The main difference is that while all `LocalDate` values get checked
898/// for validity before they are used, there is no such check for `YMD`.
899/// The interface to `LocalDate` ensures that it should be impossible to
900/// create an instance of the 74th of March, for example, but you’re
901/// free to create such an instance of `YMD`. For this reason, it is not
902/// exposed to implementors of this library.
903#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Debug, Copy)]
904struct YMD {
905 year: i64,
906 month: Month,
907 day: i8,
908}
909
910impl YMD {
911
912 /// Calculates the number of days that have elapsed since the 1st
913 /// January, 1970. Returns the number of days if this datestamp is
914 /// valid; None otherwise.
915 ///
916 /// This method returns a Result instead of exposing is_valid to
917 /// the user, because the leap year calculations are used in both
918 /// functions, so it makes more sense to only do them once.
919 fn to_days_since_epoch(&self) -> Result<i64, Error> {
920 let years = self.year - 2000;
921 let (leap_days_elapsed, is_leap_year) = Year(self.year).leap_year_calculations();
922
923 if !self.is_valid(is_leap_year) {
924 return Err(Error::OutOfRange);
925 }
926
927 // Work out the number of days from the start of 1970 to now,
928 // which is a multiple of the number of years...
929 let days = years * 365
930
931 // Plus the number of days between the start of 2000 and the
932 // start of 1970, to make up the difference because our
933 // dates start at 2000 and instants start at 1970...
934 + 10958
935
936 // Plus the number of leap years that have elapsed between
937 // now and the start of 2000...
938 + leap_days_elapsed
939
940 // Plus the number of days in all the months leading up to
941 // the current month...
942 + self.month.days_before_start() as i64
943
944 // Plus an extra leap day for *this* year...
945 + if is_leap_year && self.month >= March { 1 } else { 0 }
946
947 // Plus the number of days in the month so far! (Days are
948 // 1-indexed, so we make them 0-indexed here)
949 + (self.day - 1) as i64;
950
951 Ok(days)
952 }
953
954 /// Returns whether this datestamp is valid, which basically means
955 /// whether the day is in the range allowed by the month.
956 ///
957 /// Whether the current year is a leap year should already have been
958 /// calculated at this point, so the value is passed in rather than
959 /// calculating it afresh.
960 fn is_valid(&self, is_leap_year: bool) -> bool {
961 self.day >= 1 && self.day <= self.month.days_in_month(is_leap_year)
962 }
963}
964
965/// Computes the weekday, given the number of days that have passed
966/// since the EPOCH.
967fn days_to_weekday(days: i64) -> Weekday {
968 // March 1st, 2000 was a Wednesday, so add 3 to the number of days.
969 let weekday = (days + 3) % 7;
970
971 // We can unwrap since we’ve already done the bounds checking.
972 Weekday::from_zero(if weekday < 0 { weekday + 7 } else { weekday } as i8).unwrap()
973}
974
975/// Split a number of years into a number of year-cycles, and the number
976/// of years left over that don’t fit into a cycle. This is also used
977/// for day-cycles.
978///
979/// This is essentially a division operation with the result and the
980/// remainder, with the difference that a negative value gets ‘wrapped
981/// around’ to be a positive value, owing to the way the modulo operator
982/// works for negative values.
983fn split_cycles(number_of_periods: i64, cycle_length: i64) -> (i64, i64) {
984 let mut cycles = number_of_periods / cycle_length;
985 let mut remainder = number_of_periods % cycle_length;
986
987 if remainder < 0 {
988 remainder += cycle_length;
989 cycles -= 1;
990 }
991
992 (cycles, remainder)
993}
994
995
996#[derive(PartialEq, Debug, Copy, Clone)]
997pub enum Error {
998 OutOfRange,
999}
1000
1001impl fmt::Display for Error {
1002 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1003 write!(f, "datetime field out of range")
1004 }
1005}
1006
1007impl ErrorTrait for Error {
1008}
1009
1010
1011/// A month of the year, starting with January, and ending with December.
1012///
1013/// This is stored as an enum instead of just a number to prevent
1014/// off-by-one errors: is month 2 February (1-indexed) or March (0-indexed)?
1015/// In this case, it’s 1-indexed, to have January become 1 when you use
1016/// `as i32` in code.
1017#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
1018pub enum Month {
1019 January = 1, February = 2, March = 3,
1020 April = 4, May = 5, June = 6,
1021 July = 7, August = 8, September = 9,
1022 October = 10, November = 11, December = 12,
1023}
1024
1025#[allow(clippy::match_same_arms)]
1026impl Month {
1027
1028 /// Returns the number of days in this month, depending on whether it’s
1029 /// a leap year or not.
1030 pub fn days_in_month(self, leap_year: bool) -> i8 {
1031 match self {
1032 January => 31, February => if leap_year { 29 } else { 28 },
1033 March => 31, April => 30,
1034 May => 31, June => 30,
1035 July => 31, August => 31,
1036 September => 30, October => 31,
1037 November => 30, December => 31,
1038 }
1039 }
1040
1041 /// Returns the number of days that have elapsed in a year *before* this
1042 /// month begins, with no leap year check.
1043 fn days_before_start(self) -> i16 {
1044 match self {
1045 January => 0, February => 31, March => 59,
1046 April => 90, May => 120, June => 151,
1047 July => 181, August => 212, September => 243,
1048 October => 273, November => 304, December => 334,
1049 }
1050 }
1051
1052 pub fn months_from_january(self) -> usize {
1053 match self {
1054 January => 0, February => 1, March => 2,
1055 April => 3, May => 4, June => 5,
1056 July => 6, August => 7, September => 8,
1057 October => 9, November => 10, December => 11,
1058 }
1059 }
1060
1061 /// Returns the month based on a number, with January as **Month 1**,
1062 /// February as **Month 2**, and so on.
1063 ///
1064 /// ```rust
1065 /// use datetime::Month;
1066 /// assert_eq!(Month::from_one(5), Ok(Month::May));
1067 /// assert!(Month::from_one(0).is_err());
1068 /// ```
1069 pub fn from_one(month: i8) -> Result<Self, Error> {
1070 Ok(match month {
1071 1 => January, 2 => February, 3 => March,
1072 4 => April, 5 => May, 6 => June,
1073 7 => July, 8 => August, 9 => September,
1074 10 => October, 11 => November, 12 => December,
1075 _ => return Err(Error::OutOfRange),
1076 })
1077 }
1078
1079 /// Returns the month based on a number, with January as **Month 0**,
1080 /// February as **Month 1**, and so on.
1081 ///
1082 /// ```rust
1083 /// use datetime::Month;
1084 /// assert_eq!(Month::from_zero(5), Ok(Month::June));
1085 /// assert!(Month::from_zero(12).is_err());
1086 /// ```
1087 pub fn from_zero(month: i8) -> Result<Self, Error> {
1088 Ok(match month {
1089 0 => January, 1 => February, 2 => March,
1090 3 => April, 4 => May, 5 => June,
1091 6 => July, 7 => August, 8 => September,
1092 9 => October, 10 => November, 11 => December,
1093 _ => return Err(Error::OutOfRange),
1094 })
1095 }
1096}
1097
1098
1099/// A named day of the week.
1100#[derive(PartialEq, Eq, Debug, Clone, Copy)]
1101pub enum Weekday {
1102 Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday,
1103}
1104
1105// Sunday is Day 0. This seems to be a North American thing? It’s pretty
1106// much an arbitrary choice, and as you can’t use the `from_zero` method,
1107// it won’t affect you at all. If you want to change it, the only thing
1108// that should be affected is `LocalDate::days_to_weekday`.
1109//
1110// I’m not going to give weekdays an Ord instance because there’s no
1111// real standard as to whether Sunday should come before Monday, or the
1112// other way around. Luckily, they don’t need one, as the field is
1113// ignored when comparing LocalDates.
1114
1115impl Weekday {
1116 fn days_from_monday_as_one(self) -> i8 {
1117 match self {
1118 Sunday => 7, Monday => 1,
1119 Tuesday => 2, Wednesday => 3,
1120 Thursday => 4, Friday => 5,
1121 Saturday => 6,
1122 }
1123 }
1124
1125 /// Return the weekday based on a number, with Sunday as Day 0, Monday as
1126 /// Day 1, and so on.
1127 ///
1128 /// ```rust
1129 /// use datetime::Weekday;
1130 /// assert_eq!(Weekday::from_zero(4), Ok(Weekday::Thursday));
1131 /// assert!(Weekday::from_zero(7).is_err());
1132 /// ```
1133 pub fn from_zero(weekday: i8) -> Result<Self, Error> {
1134 Ok(match weekday {
1135 0 => Sunday, 1 => Monday, 2 => Tuesday,
1136 3 => Wednesday, 4 => Thursday, 5 => Friday,
1137 6 => Saturday, _ => return Err(Error::OutOfRange),
1138 })
1139 }
1140
1141 pub fn from_one(weekday: i8) -> Result<Self, Error> {
1142 Ok(match weekday {
1143 7 => Sunday, 1 => Monday, 2 => Tuesday,
1144 3 => Wednesday, 4 => Thursday, 5 => Friday,
1145 6 => Saturday, _ => return Err(Error::OutOfRange),
1146 })
1147 }
1148}
1149
1150
1151/// Misc tests that don’t seem to fit anywhere.
1152#[cfg(test)]
1153mod test {
1154 pub(crate) use super::{LocalDateTime, LocalDate, LocalTime, Month};
1155
1156
1157 #[test]
1158 fn some_leap_years() {
1159 for year in [2004,2008,2012,2016].iter() {
1160 assert!(LocalDate::ymd(*year, Month::February, 29).is_ok());
1161 assert!(LocalDate::ymd(*year + 1, Month::February, 29).is_err());
1162 }
1163 assert!(LocalDate::ymd(1600,Month::February,29).is_ok());
1164 assert!(LocalDate::ymd(1601,Month::February,29).is_err());
1165 assert!(LocalDate::ymd(1602,Month::February,29).is_err());
1166 }
1167
1168 #[test]
1169 fn new() {
1170 for year in 1..3000 {
1171 assert!(LocalDate::ymd(year, Month::from_one( 1).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 2).unwrap(), 30).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 3).unwrap(), 32).is_err());
1172 assert!(LocalDate::ymd(year, Month::from_one( 4).unwrap(), 31).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 5).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 6).unwrap(), 31).is_err());
1173 assert!(LocalDate::ymd(year, Month::from_one( 7).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 8).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 9).unwrap(), 31).is_err());
1174 assert!(LocalDate::ymd(year, Month::from_one(10).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one(11).unwrap(), 31).is_err()); assert!(LocalDate::ymd(year, Month::from_one(12).unwrap(), 32).is_err());
1175 }
1176 }
1177
1178 #[test]
1179 fn to_from_days_since_epoch() {
1180 let epoch_difference: i64 = 30 * 365 + 7 + 31 + 29; // see EPOCH_DIFFERENCE
1181 for date in vec![
1182 LocalDate::ymd(1970, Month::from_one(01).unwrap(), 01).unwrap(),
1183 LocalDate::ymd( 01, Month::from_one(01).unwrap(), 01).unwrap(),
1184 LocalDate::ymd(1971, Month::from_one(01).unwrap(), 01).unwrap(),
1185 LocalDate::ymd(1973, Month::from_one(01).unwrap(), 01).unwrap(),
1186 LocalDate::ymd(1977, Month::from_one(01).unwrap(), 01).unwrap(),
1187 LocalDate::ymd(1989, Month::from_one(11).unwrap(), 10).unwrap(),
1188 LocalDate::ymd(1990, Month::from_one( 7).unwrap(), 8).unwrap(),
1189 LocalDate::ymd(2014, Month::from_one( 7).unwrap(), 13).unwrap(),
1190 LocalDate::ymd(2001, Month::from_one( 2).unwrap(), 03).unwrap()
1191 ]{
1192 assert_eq!( date,
1193 LocalDate::from_days_since_epoch(
1194 date.ymd.to_days_since_epoch().unwrap() - epoch_difference));
1195 }
1196 }
1197
1198 mod debug {
1199 use super::*;
1200
1201 #[test]
1202 fn recently() {
1203 let date = LocalDate::ymd(1600, Month::February, 28).unwrap();
1204 let debugged = format!("{:?}", date);
1205
1206 assert_eq!(debugged, "LocalDate(1600-02-28)");
1207 }
1208
1209 #[test]
1210 fn just_then() {
1211 let date = LocalDate::ymd(-753, Month::December, 1).unwrap();
1212 let debugged = format!("{:?}", date);
1213
1214 assert_eq!(debugged, "LocalDate(-0753-12-01)");
1215 }
1216
1217 #[test]
1218 fn far_far_future() {
1219 let date = LocalDate::ymd(10601, Month::January, 31).unwrap();
1220 let debugged = format!("{:?}", date);
1221
1222 assert_eq!(debugged, "LocalDate(+10601-01-31)");
1223 }
1224
1225 #[test]
1226 fn midday() {
1227 let time = LocalTime::hms(12, 0, 0).unwrap();
1228 let debugged = format!("{:?}", time);
1229
1230 assert_eq!(debugged, "LocalTime(12:00:00.000)");
1231 }
1232
1233 #[test]
1234 fn ascending() {
1235 let then = LocalDateTime::new(
1236 LocalDate::ymd(2009, Month::February, 13).unwrap(),
1237 LocalTime::hms(23, 31, 30).unwrap());
1238 let debugged = format!("{:?}", then);
1239
1240 assert_eq!(debugged, "LocalDateTime(2009-02-13T23:31:30.000)");
1241 }
1242 }
1243}