added Duration; DateZ::fmt now formats the out-of-range years correctly.

This commit is contained in:
Kang Seonghoon 2014-03-29 17:20:39 +09:00
parent 715a35caa6
commit 27d4daad11
3 changed files with 265 additions and 1 deletions

View File

@ -363,7 +363,13 @@ impl Datelike for DateZ {
impl fmt::Show for DateZ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f.buf, "{:04}-{:02}-{:02}", self.year(), self.month(), self.day())
let year = self.year();
if 0 <= year && year <= 9999 {
write!(f.buf, "{:04}-{:02}-{:02}", year, self.month(), self.day())
} else {
// ISO 8601 requires the explicit sign for out-of-range years
write!(f.buf, "{:+05}-{:02}-{:02}", year, self.month(), self.day())
}
}
}
@ -569,6 +575,14 @@ mod tests {
DateZ::from_ymd(year - 1, 12, 31).unwrap().ndays_from_ce() + 1);
}
}
#[test]
fn test_date_fmt() {
assert_eq!(DateZ::from_ymd(2012, 3, 4).unwrap().to_str(), ~"2012-03-04");
assert_eq!(DateZ::from_ymd(0, 3, 4).unwrap().to_str(), ~"0000-03-04");
assert_eq!(DateZ::from_ymd(-307, 3, 4).unwrap().to_str(), ~"-0307-03-04");
assert_eq!(DateZ::from_ymd(12345, 3, 4).unwrap().to_str(), ~"+12345-03-04");
}
}
/**

249
src/chrono/duration.rs Normal file
View File

@ -0,0 +1,249 @@
/*!
* ISO 8601 duration.
*/
use std::{fmt, num};
static NANOS_PER_SEC: int = 1_000_000_000;
static SECS_PER_DAY: int = 86400;
#[deriving(Eq, TotalEq, Ord, TotalOrd)]
pub struct Duration {
priv days: int,
priv secs: u32,
priv nanos: u32,
}
impl Duration {
pub fn new(days: int, secs: int, nanos: int) -> Option<Duration> {
let mut secs_ = nanos / NANOS_PER_SEC;
let mut nanos = nanos % NANOS_PER_SEC;
if nanos < 0 { secs_ -= 1; nanos += NANOS_PER_SEC; }
let secs = match secs.checked_add(&secs_) { Some(v) => v, None => return None };
let mut days_ = secs / SECS_PER_DAY;
let mut secs = secs % SECS_PER_DAY;
if secs < 0 { days_ -= 1; secs += SECS_PER_DAY; }
let days = match days.checked_add(&days_) { Some(v) => v, None => return None };
Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 })
}
#[inline]
pub fn zero() -> Duration {
Duration { days: 0, secs: 0, nanos: 0 }
}
#[inline]
pub fn weeks(weeks: int) -> Duration {
Duration::days(weeks * 7)
}
#[inline]
pub fn days(days: int) -> Duration {
Duration { days: days, secs: 0, nanos: 0 }
}
#[inline]
pub fn hours(hours: int) -> Duration {
Duration::seconds(hours * 3600)
}
#[inline]
pub fn minutes(mins: int) -> Duration {
Duration::seconds(mins * 60)
}
#[inline]
pub fn seconds(secs: int) -> Duration {
let mut days = secs / SECS_PER_DAY;
let mut secs = secs % SECS_PER_DAY;
if secs < 0 { days -= 1; secs += SECS_PER_DAY; }
Duration { secs: secs as u32, ..Duration::days(days) }
}
#[inline]
pub fn milliseconds(millis: int) -> Duration {
Duration::nanoseconds(millis * 1_000_000)
}
#[inline]
pub fn microseconds(micros: int) -> Duration {
Duration::nanoseconds(micros * 1_000)
}
#[inline]
pub fn nanoseconds(nanos: int) -> Duration {
let mut secs = nanos / NANOS_PER_SEC;
let mut nanos = nanos % NANOS_PER_SEC;
if nanos < 0 { secs -= 1; nanos += NANOS_PER_SEC; }
Duration { nanos: nanos as u32, ..Duration::seconds(secs) }
}
}
impl num::Zero for Duration {
#[inline]
fn zero() -> Duration {
Duration { days: 0, secs: 0, nanos: 0 }
}
#[inline]
fn is_zero(&self) -> bool {
self.days == 0 && self.secs == 0 && self.nanos == 0
}
}
impl Neg<Duration> for Duration {
fn neg(&self) -> Duration {
let mut days = -self.days;
let mut secs = -(self.secs as int);
let mut nanos = -(self.nanos as int);
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
if secs < 0 {
secs += SECS_PER_DAY;
days -= 1;
}
Duration { days: days, secs: secs as u32, nanos: nanos as u32 }
}
}
impl Add<Duration,Duration> for Duration {
fn add(&self, rhs: &Duration) -> Duration {
let mut days = self.days + rhs.days;
let mut secs = self.secs + rhs.secs;
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC as u32 {
nanos -= NANOS_PER_SEC as u32;
secs += 1;
}
if secs >= SECS_PER_DAY as u32 {
secs -= SECS_PER_DAY as u32;
days += 1;
}
Duration { days: days, secs: secs, nanos: nanos }
}
}
impl num::CheckedAdd for Duration {
fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
let mut days = match self.days.checked_add(&rhs.days) { Some(v) => v, None => return None };
let mut secs = self.secs + rhs.secs;
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC as u32 {
nanos -= NANOS_PER_SEC as u32;
secs += 1;
}
if secs >= SECS_PER_DAY as u32 {
secs -= SECS_PER_DAY as u32;
days = match days.checked_add(&1) { Some(v) => v, None => return None };
}
Some(Duration { days: days, secs: secs, nanos: nanos })
}
}
impl Sub<Duration,Duration> for Duration {
fn sub(&self, rhs: &Duration) -> Duration {
let mut days = self.days - rhs.days;
let mut secs = self.secs as int - rhs.secs as int;
let mut nanos = self.nanos as int - rhs.nanos as int;
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
if secs < 0 {
secs += SECS_PER_DAY;
days -= 1;
}
Duration { days: days, secs: secs as u32, nanos: nanos as u32 }
}
}
impl num::CheckedSub for Duration {
fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
let mut days = match self.days.checked_sub(&rhs.days) { Some(v) => v, None => return None };
let mut secs = self.secs as int - rhs.secs as int;
let mut nanos = self.nanos as int - rhs.nanos as int;
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
if secs < 0 {
secs += SECS_PER_DAY;
days = match days.checked_sub(&1) { Some(v) => v, None => return None };
}
Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 })
}
}
impl fmt::Show for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let hasdate = self.days != 0;
let hastime = (self.secs != 0 || self.nanos != 0) || !hasdate;
try!('P'.fmt(f));
if hasdate {
// technically speaking the negative part is not the valid ISO 8601,
// but we need to print it anyway.
try!(write!(f.buf, "{}D", self.days));
}
if hastime {
if self.nanos == 0 {
try!(write!(f.buf, "T{}S", self.secs));
} else if self.nanos % 1_000_000 == 0 {
try!(write!(f.buf, "T{},{:03}S", self.secs, self.nanos / 1_000_000));
} else if self.nanos % 1_000 == 0 {
try!(write!(f.buf, "T{},{:06}S", self.secs, self.nanos / 1_000));
} else {
try!(write!(f.buf, "T{},{:09}S", self.secs, self.nanos));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::int;
#[test]
fn test_duration() {
assert_eq!(Duration::zero(), Duration::zero());
assert!(Duration::zero() != Duration::seconds(1));
assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3));
assert_eq!(Duration::seconds(86399) + Duration::seconds(4),
Duration::days(1) + Duration::seconds(3));
assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000));
assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000));
assert_eq!(Duration::days(2) + Duration::seconds(86391) + Duration::nanoseconds(9876543210),
Duration::days(3) + Duration::nanoseconds(876543210));
assert_eq!(-Duration::days(3), Duration::days(-3));
assert_eq!(-(Duration::days(3) + Duration::seconds(70)),
Duration::days(-4) + Duration::seconds(86400-70));
}
#[test]
fn test_duration_checked_ops() {
assert_eq!(Duration::days(int::MAX).checked_add(&Duration::seconds(86399)),
Some(Duration::days(int::MAX - 1) + Duration::seconds(86400+86399)));
assert!(Duration::days(int::MAX).checked_add(&Duration::seconds(86400)).is_none());
assert_eq!(Duration::days(int::MIN).checked_sub(&Duration::seconds(0)),
Some(Duration::days(int::MIN)));
assert!(Duration::days(int::MIN).checked_sub(&Duration::seconds(1)).is_none());
}
#[test]
fn test_duration_fmt() {
assert_eq!(Duration::zero().to_str(), ~"PT0S");
assert_eq!(Duration::days(42).to_str(), ~"P42D");
assert_eq!(Duration::days(-42).to_str(), ~"P-42D");
assert_eq!(Duration::seconds(42).to_str(), ~"PT42S");
assert_eq!(Duration::milliseconds(42).to_str(), ~"PT0,042S");
assert_eq!(Duration::microseconds(42).to_str(), ~"PT0,000042S");
assert_eq!(Duration::nanoseconds(42).to_str(), ~"PT0,000000042S");
assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_str(), ~"P7DT6,543S");
}
}

View File

@ -3,6 +3,7 @@
#[feature(globs, macro_rules)];
pub mod duration;
pub mod date;
pub mod time;
pub mod datetime;