From c9ec9ee63fde0bb73c8ae618baa2201bfa603bcd Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Sun, 30 Mar 2014 05:13:44 +0900 Subject: [PATCH] DateTimeZ now supports arithmetic operations. --- src/chrono/date.rs | 50 +++++++++++++++++++++++++++++++++--- src/chrono/datetime.rs | 58 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/chrono/date.rs b/src/chrono/date.rs index 09fa5dd..570a049 100644 --- a/src/chrono/date.rs +++ b/src/chrono/date.rs @@ -5,8 +5,10 @@ use std::fmt; use duration::Duration; -use self::internals::{Of, Mdf, YearFlags}; -use self::internals::{DateImpl, MIN_YEAR, MAX_YEAR}; +use self::internals::{DateImpl, Of, Mdf, YearFlags}; + +pub static MAX_YEAR: int = internals::MAX_YEAR as int; +pub static MIN_YEAR: int = internals::MIN_YEAR as int; /// The day of week (DOW). #[deriving(Eq, TotalEq, FromPrimitive, Show)] @@ -209,7 +211,7 @@ pub struct DateZ { impl DateZ { /// The internal constructor with the verification. fn new(year: int, mdf: Mdf) -> Option { - if year >= MIN_YEAR as int && year <= MAX_YEAR as int && mdf.valid() { + if year >= MIN_YEAR && year <= MAX_YEAR && mdf.valid() { let Mdf(mdf) = mdf; Some(DateZ { ymdf: ((year << 13) as DateImpl) | (mdf as DateImpl) }) } else { @@ -294,6 +296,18 @@ impl DateZ { None } } + + #[inline] + pub fn succ(&self) -> Option { + let mdf = self.of().succ().to_mdf(); + self.with_mdf(mdf).or_else(|| DateZ::from_ymd(self.year() + 1, 1, 1)) + } + + #[inline] + pub fn pred(&self) -> Option { + let mdf = self.of().pred().to_mdf(); + self.with_mdf(mdf).or_else(|| DateZ::from_ymd(self.year() - 1, 12, 31)) + } } impl Datelike for DateZ { @@ -621,6 +635,24 @@ mod tests { } } + #[test] + fn test_date_succ() { + assert_eq!(DateZ::from_ymd(2014, 5, 6).unwrap().succ(), DateZ::from_ymd(2014, 5, 7)); + assert_eq!(DateZ::from_ymd(2014, 5, 31).unwrap().succ(), DateZ::from_ymd(2014, 6, 1)); + assert_eq!(DateZ::from_ymd(2014, 12, 31).unwrap().succ(), DateZ::from_ymd(2015, 1, 1)); + assert_eq!(DateZ::from_ymd(2016, 2, 28).unwrap().succ(), DateZ::from_ymd(2016, 2, 29)); + assert_eq!(DateZ::from_ymd(MAX_YEAR, 12, 31).unwrap().succ(), None); + } + + #[test] + fn test_date_pred() { + assert_eq!(DateZ::from_ymd(2016, 3, 1).unwrap().pred(), DateZ::from_ymd(2016, 2, 29)); + assert_eq!(DateZ::from_ymd(2015, 1, 1).unwrap().pred(), DateZ::from_ymd(2014, 12, 31)); + assert_eq!(DateZ::from_ymd(2014, 6, 1).unwrap().pred(), DateZ::from_ymd(2014, 5, 31)); + assert_eq!(DateZ::from_ymd(2014, 5, 7).unwrap().pred(), DateZ::from_ymd(2014, 5, 6)); + assert_eq!(DateZ::from_ymd(MIN_YEAR, 1, 1).unwrap().pred(), None); + } + #[test] fn test_date_add() { fn check((y1,m1,d1): (int, uint, uint), rhs: Duration, (y,m,d): (int, uint, uint)) { @@ -1012,6 +1044,18 @@ mod internals { pub fn to_mdf(&self) -> Mdf { Mdf::from_of(*self) } + + #[inline] + pub fn succ(&self) -> Of { + let Of(of) = *self; + Of(of + (1 << 4)) + } + + #[inline] + pub fn pred(&self) -> Of { + let Of(of) = *self; + Of(of - (1 << 4)) + } } impl fmt::Show for Of { diff --git a/src/chrono/datetime.rs b/src/chrono/datetime.rs index d3a785f..a847ce8 100644 --- a/src/chrono/datetime.rs +++ b/src/chrono/datetime.rs @@ -3,6 +3,7 @@ */ use std::fmt; +use duration::Duration; use time::{Timelike, TimeZ}; use date::{Datelike, DateZ, Weekday}; @@ -127,6 +128,33 @@ impl Timelike for DateTimeZ { } } +impl Add for DateTimeZ { + fn add(&self, rhs: &Duration) -> DateTimeZ { + let mut date = self.date + *rhs; + let time = self.time + *rhs; + if time < self.time { + // since the time portion of the duration is always positive and bounded, + // this condition always means that the time part has been overflowed. + date = date.succ().unwrap(); + } + DateTimeZ { date: date, time: time } + } +} + +/* +// Rust issue #7590, the current coherence checker can't handle multiple Add impls +impl Add for Duration { + #[inline] + fn add(&self, rhs: &DateTimeZ) -> DateTimeZ { rhs.add(self) } +} +*/ + +impl Sub for DateTimeZ { + fn sub(&self, rhs: &DateTimeZ) -> Duration { + (self.date - rhs.date) + (self.time - rhs.time) + } +} + impl fmt::Show for DateTimeZ { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f.buf, "{}T{}", self.date, self.time) @@ -136,9 +164,37 @@ impl fmt::Show for DateTimeZ { #[cfg(test)] mod tests { use super::*; + use duration::Duration; #[test] - fn test_time_nseconds_from_unix_epoch() { + fn test_datetime_add() { + let ymdhms = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap(); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) + Duration::seconds(3600 + 60 + 1), + ymdhms(2014, 5, 6, 8, 9, 10)); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) + Duration::seconds(86399), + ymdhms(2014, 5, 7, 7, 8, 8)); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) + Duration::seconds(86400 * 10), + ymdhms(2014, 5, 16, 7, 8, 9)); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) + Duration::seconds(-86400 * 10), + ymdhms(2014, 4, 26, 7, 8, 9)); + } + + #[test] + fn test_datetime_sub() { + let ymdhms = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap(); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) - ymdhms(2014, 5, 6, 7, 8, 9), Duration::zero()); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 10) - ymdhms(2014, 5, 6, 7, 8, 9), + Duration::seconds(1)); + assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) - ymdhms(2014, 5, 6, 7, 8, 10), + Duration::seconds(-1)); + assert_eq!(ymdhms(2014, 5, 7, 7, 8, 9) - ymdhms(2014, 5, 6, 7, 8, 10), + Duration::seconds(86399)); + assert_eq!(ymdhms(2001, 9, 9, 1, 46, 39) - ymdhms(1970, 1, 1, 0, 0, 0), + Duration::seconds(999_999_999)); + } + + #[test] + fn test_datetime_nseconds_from_unix_epoch() { let to_timestamp = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap().nseconds_from_unix_epoch(); assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1);