TimeZ supports arithmetic operations.

This commit is contained in:
Kang Seonghoon 2014-03-30 00:12:50 +09:00
parent 27d4daad11
commit 877ceaeca0
2 changed files with 113 additions and 3 deletions

View File

@ -77,6 +77,21 @@ impl Duration {
if nanos < 0 { secs -= 1; nanos += NANOS_PER_SEC; } if nanos < 0 { secs -= 1; nanos += NANOS_PER_SEC; }
Duration { nanos: nanos as u32, ..Duration::seconds(secs) } Duration { nanos: nanos as u32, ..Duration::seconds(secs) }
} }
#[inline]
pub fn ndays(&self) -> int {
self.days
}
#[inline]
pub fn nseconds(&self) -> uint {
self.secs as uint
}
#[inline]
pub fn nnanoseconds(&self) -> uint {
self.nanos as uint
}
} }
impl num::Zero for Duration { impl num::Zero for Duration {

View File

@ -3,6 +3,7 @@
*/ */
use std::fmt; use std::fmt;
use duration::Duration;
pub trait Timelike { pub trait Timelike {
/// Returns the hour number from 0 to 23. /// Returns the hour number from 0 to 23.
@ -133,6 +134,52 @@ impl Timelike for TimeZ {
} }
} }
impl Add<Duration,TimeZ> for TimeZ {
fn add(&self, rhs: &Duration) -> TimeZ {
let mut secs = self.nseconds_from_midnight() as int + rhs.nseconds() as int;
let mut nanos = self.frac + rhs.nnanoseconds() as u32;
// always ignore leap seconds after the current whole second
let maxnanos = if self.frac >= 1_000_000_000 {2_000_000_000} else {1_000_000_000};
if nanos >= maxnanos {
nanos -= maxnanos;
secs += 1;
}
let (s, mins) = (secs % 60, secs / 60);
let (m, hours) = (mins % 60, mins / 60);
let h = hours % 24;
TimeZ { hour: h as u8, min: m as u8, sec: s as u8, frac: nanos }
}
}
/*
// Rust issue #7590, the current coherence checker can't handle multiple Add impls
impl Add<TimeZ,TimeZ> for Duration {
#[inline]
fn add(&self, rhs: &TimeZ) -> TimeZ { rhs.add(self) }
}
*/
impl Sub<TimeZ,Duration> for TimeZ {
fn sub(&self, rhs: &TimeZ) -> Duration {
// the number of whole non-leap seconds
let secs = (self.hour as int - rhs.hour as int) * 3600 +
(self.min as int - rhs.min as int) * 60 +
(self.sec as int - rhs.sec as int) - 1;
// the fractional second from the rhs to the next non-leap second
let maxnanos = if rhs.frac >= 1_000_000_000 {2_000_000_000} else {1_000_000_000};
let nanos1 = maxnanos - rhs.frac;
// the fractional second from the last leap or non-leap second to the lhs
let lastfrac = if self.frac >= 1_000_000_000 {1_000_000_000} else {0};
let nanos2 = self.frac - lastfrac;
Duration::seconds(secs) + Duration::nanoseconds(nanos1 as int + nanos2 as int)
}
}
impl fmt::Show for TimeZ { impl fmt::Show for TimeZ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (sec, nano) = if self.frac >= 1_000_000_000 { let (sec, nano) = if self.frac >= 1_000_000_000 {
@ -157,12 +204,60 @@ impl fmt::Show for TimeZ {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use duration::Duration;
fn hmsm(hour: uint, min: uint, sec: uint, millis: uint) -> TimeZ {
TimeZ::from_hms_milli(hour, min, sec, millis).unwrap()
}
#[test]
fn test_time_add() {
fn check(lhs: TimeZ, rhs: Duration, sum: TimeZ) {
assert_eq!(lhs + rhs, sum);
//assert_eq!(rhs + lhs, sum);
}
check(hmsm(3, 5, 7, 900), Duration::zero(), hmsm(3, 5, 7, 900));
check(hmsm(3, 5, 7, 900), Duration::milliseconds(100), hmsm(3, 5, 8, 0));
check(hmsm(3, 5, 7, 1_300), Duration::milliseconds(800), hmsm(3, 5, 8, 100));
check(hmsm(3, 5, 7, 900), Duration::seconds(86399), hmsm(3, 5, 6, 900)); // overwrap
check(hmsm(3, 5, 7, 900), Duration::seconds(-86399), hmsm(3, 5, 8, 900));
check(hmsm(3, 5, 7, 900), Duration::days(12345), hmsm(3, 5, 7, 900));
}
#[test]
fn test_time_sub() {
fn check(lhs: TimeZ, rhs: TimeZ, diff: Duration) {
// `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration`
assert_eq!(lhs - rhs, diff);
assert_eq!(rhs - lhs, -diff);
}
check(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), Duration::zero());
check(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), Duration::milliseconds(300));
check(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), Duration::seconds(3600 + 60 + 1));
check(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 300),
Duration::seconds(3600 + 60) + Duration::milliseconds(900));
// treats the leap second as if it coincides with the prior non-leap second,
// as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence.
check(hmsm(3, 5, 7, 200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(400));
check(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(400));
check(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), Duration::milliseconds(400));
// additional equality: `time1 + duration = time2` is equivalent to
// `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second.
assert_eq!(hmsm(3, 5, 6, 800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200));
assert_eq!(hmsm(3, 5, 6, 1_800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200));
}
#[test] #[test]
fn test_time_fmt() { fn test_time_fmt() {
assert_eq!(TimeZ::from_hms_milli(23, 59, 59, 999).unwrap().to_str(), ~"23:59:59,999"); assert_eq!(hmsm(23, 59, 59, 999).to_str(), ~"23:59:59,999");
assert_eq!(TimeZ::from_hms_milli(23, 59, 59, 1_000).unwrap().to_str(), ~"23:59:60"); assert_eq!(hmsm(23, 59, 59, 1_000).to_str(), ~"23:59:60");
assert_eq!(TimeZ::from_hms_milli(23, 59, 59, 1_001).unwrap().to_str(), ~"23:59:60,001"); assert_eq!(hmsm(23, 59, 59, 1_001).to_str(), ~"23:59:60,001");
assert_eq!(TimeZ::from_hms_micro(0, 0, 0, 43210).unwrap().to_str(), ~"00:00:00,043210");
assert_eq!(TimeZ::from_hms_nano(0, 0, 0, 6543210).unwrap().to_str(), ~"00:00:00,006543210");
} }
} }