DateTimeZ now supports arithmetic operations.

This commit is contained in:
Kang Seonghoon 2014-03-30 05:13:44 +09:00
parent 49737e992f
commit c9ec9ee63f
2 changed files with 104 additions and 4 deletions

View File

@ -5,8 +5,10 @@
use std::fmt; use std::fmt;
use duration::Duration; use duration::Duration;
use self::internals::{Of, Mdf, YearFlags}; use self::internals::{DateImpl, Of, Mdf, YearFlags};
use self::internals::{DateImpl, MIN_YEAR, MAX_YEAR};
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). /// The day of week (DOW).
#[deriving(Eq, TotalEq, FromPrimitive, Show)] #[deriving(Eq, TotalEq, FromPrimitive, Show)]
@ -209,7 +211,7 @@ pub struct DateZ {
impl DateZ { impl DateZ {
/// The internal constructor with the verification. /// The internal constructor with the verification.
fn new(year: int, mdf: Mdf) -> Option<DateZ> { fn new(year: int, mdf: Mdf) -> Option<DateZ> {
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; let Mdf(mdf) = mdf;
Some(DateZ { ymdf: ((year << 13) as DateImpl) | (mdf as DateImpl) }) Some(DateZ { ymdf: ((year << 13) as DateImpl) | (mdf as DateImpl) })
} else { } else {
@ -294,6 +296,18 @@ impl DateZ {
None None
} }
} }
#[inline]
pub fn succ(&self) -> Option<DateZ> {
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<DateZ> {
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 { 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] #[test]
fn test_date_add() { fn test_date_add() {
fn check((y1,m1,d1): (int, uint, uint), rhs: Duration, (y,m,d): (int, uint, uint)) { 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 { pub fn to_mdf(&self) -> Mdf {
Mdf::from_of(*self) 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 { impl fmt::Show for Of {

View File

@ -3,6 +3,7 @@
*/ */
use std::fmt; use std::fmt;
use duration::Duration;
use time::{Timelike, TimeZ}; use time::{Timelike, TimeZ};
use date::{Datelike, DateZ, Weekday}; use date::{Datelike, DateZ, Weekday};
@ -127,6 +128,33 @@ impl Timelike for DateTimeZ {
} }
} }
impl Add<Duration,DateTimeZ> 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<DateTimeZ,DateTimeZ> for Duration {
#[inline]
fn add(&self, rhs: &DateTimeZ) -> DateTimeZ { rhs.add(self) }
}
*/
impl Sub<DateTimeZ,Duration> for DateTimeZ {
fn sub(&self, rhs: &DateTimeZ) -> Duration {
(self.date - rhs.date) + (self.time - rhs.time)
}
}
impl fmt::Show for DateTimeZ { impl fmt::Show for DateTimeZ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f.buf, "{}T{}", self.date, self.time) write!(f.buf, "{}T{}", self.date, self.time)
@ -136,9 +164,37 @@ impl fmt::Show for DateTimeZ {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use duration::Duration;
#[test] #[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 = let to_timestamp =
|y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap().nseconds_from_unix_epoch(); |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); assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1);