From 110c7de7d36bc24cbd2358f9a2238f561f8f3fb7 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Fri, 25 Jul 2014 17:05:26 +0900 Subject: [PATCH] added docs to `Duration`; added `Duration::new_opt`; removed `{MIN,MAX}_{DAYS,YEAR}`. the minimum and maximum for `DateZ` and `Duration` is now provided via dedicated constants `{date,duration}::{MIN,MAX}`, much like built-in `std::int` and others. they also now implements `std::num::Bounded`. cf. rust-lang/rust#15934 --- src/date.rs | 38 +++++++++++++++++++------ src/duration.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 6 ++-- 3 files changed, 103 insertions(+), 16 deletions(-) diff --git a/src/date.rs b/src/date.rs index f1cfdaf..7605911 100644 --- a/src/date.rs +++ b/src/date.rs @@ -6,14 +6,14 @@ * ISO 8601 calendar date. */ -use std::fmt; +use std::{fmt, num}; use num::Integer; use duration::Duration; use self::internals::{DateImpl, Of, Mdf, YearFlags}; -pub static MAX_YEAR: i32 = internals::MAX_YEAR as i32; -pub static MIN_YEAR: i32 = internals::MIN_YEAR as i32; +static MAX_YEAR: i32 = internals::MAX_YEAR as i32; +static MIN_YEAR: i32 = internals::MIN_YEAR as i32; /// The day of week (DOW). #[deriving(PartialEq, Eq, FromPrimitive, Show)] @@ -213,6 +213,20 @@ pub struct DateZ { ymdf: DateImpl, // (year << 13) | of } +/// The minimum possible `DateZ`. +pub static MIN: DateZ = DateZ { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o07 /*FE*/ }; +/// The maximum possible `DateZ`. +pub static MAX: DateZ = DateZ { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o17 /*F*/ }; + +// as it is hard to verify year flags in `MIN` and `MAX`, we use a separate run-time test. +#[test] +fn test_datez_bounds() { + let calculated_min = DateZ::from_ymd(MIN_YEAR, 1, 1); + let calculated_max = DateZ::from_ymd(MAX_YEAR, 12, 31); + assert!(MIN == calculated_min, "`MIN` should have a year flag {}", calculated_min.of().flags()); + assert!(MAX == calculated_max, "`MAX` should have a year flag {}", calculated_max.of().flags()); +} + impl DateZ { /// Makes a new `DateZ` from year and packed ordinal-flags, with a verification. fn from_of(year: i32, of: Of) -> Option { @@ -438,6 +452,11 @@ impl Datelike for DateZ { } } +impl num::Bounded for DateZ { + #[inline] fn min_value() -> DateZ { MIN } + #[inline] fn max_value() -> DateZ { MAX } +} + impl Add for DateZ { fn add(&self, rhs: &Duration) -> DateZ { // TODO overflow @@ -478,11 +497,12 @@ impl Sub for DateZ { impl fmt::Show for DateZ { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let year = self.year(); + let mdf = self.mdf(); if 0 <= year && year <= 9999 { - write!(f, "{:04}-{:02}-{:02}", year, self.month(), self.day()) + write!(f, "{:04}-{:02}-{:02}", year, mdf.month(), mdf.day()) } else { // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{:+05}-{:02}-{:02}", year, self.month(), self.day()) + write!(f, "{:+05}-{:02}-{:02}", year, mdf.month(), mdf.day()) } } } @@ -914,10 +934,10 @@ mod internals { } } - static MIN_OL: u32 = 1 << 1; - static MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` - static MIN_MDL: u32 = (1 << 6) | (1 << 1); - static MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; + pub static MIN_OL: u32 = 1 << 1; + pub static MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` + pub static MIN_MDL: u32 = (1 << 6) | (1 << 1); + pub static MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; static XX: i8 = -128; static MDL_TO_OL: [i8, ..MAX_MDL+1] = [ diff --git a/src/duration.rs b/src/duration.rs index 4d9493a..cfb8e48 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -9,16 +9,22 @@ use std::{fmt, num, i32}; use num::Integer; -pub static MIN_DAYS: i32 = i32::MIN; -pub static MAX_DAYS: i32 = i32::MAX; +/// `Duration`'s `days` component should have no more than this value. +static MIN_DAYS: i32 = i32::MIN; +/// `Duration`'s `days` component should have no less than this value. +static MAX_DAYS: i32 = i32::MAX; +/// The number of nanoseconds in seconds. static NANOS_PER_SEC: i32 = 1_000_000_000; +/// The number of (non-leap) seconds in days. static SECS_PER_DAY: i32 = 86400; macro_rules! earlyexit( ($e:expr) => (match $e { Some(v) => v, None => return None }) ) +/// ISO 8601 time duration with nanosecond precision. +/// This also allows for the negative duration; see individual methods for details. #[deriving(PartialEq, Eq, PartialOrd, Ord)] pub struct Duration { days: i32, @@ -26,8 +32,24 @@ pub struct Duration { nanos: u32, } +/// The minimum possible `Duration`. +pub static MIN: Duration = Duration { days: MIN_DAYS, secs: 0, nanos: 0 }; +/// The maximum possible `Duration`. +pub static MAX: Duration = Duration { days: MAX_DAYS, secs: SECS_PER_DAY as u32 - 1, + nanos: NANOS_PER_SEC as u32 - 1 }; + impl Duration { - pub fn new(days: i32, secs: i32, nanos: i32) -> Option { + /// Makes a new `Duration` with given number of days, seconds and nanoseconds. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn new(days: i32, secs: i32, nanos: i32) -> Duration { + Duration::new_opt(days, secs, nanos).expect("Duration::new out of bounds") + } + + /// Makes a new `Duration` with given number of days, seconds and nanoseconds. + /// Returns `None` when the duration is out of bounds. + pub fn new_opt(days: i32, secs: i32, nanos: i32) -> Option { let (secs_, nanos) = nanos.div_mod_floor(&NANOS_PER_SEC); let secs = earlyexit!(secs.checked_add(&secs_)); let (days_, secs) = secs.div_mod_floor(&SECS_PER_DAY); @@ -35,22 +57,36 @@ impl Duration { Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 }) } + /// Makes a new `Duration` with zero seconds. #[inline] pub fn zero() -> Duration { Duration { days: 0, secs: 0, nanos: 0 } } + /// Makes a new `Duration` with given number of weeks. + /// Equivalent to `Duration::new(weeks * 7, 0, 0)` with overflow checks. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn weeks(weeks: i32) -> Duration { - Duration::days(weeks * 7) + let days = weeks.checked_mul(&7).expect("Duration::weeks out of bounds"); + Duration::days(days) } + /// Makes a new `Duration` with given number of days. + /// Equivalent to `Duration::new(days, 0, 0)`. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn days(days: i32) -> Duration { let days = days.to_i32().expect("Duration::days out of bounds"); Duration { days: days, secs: 0, nanos: 0 } } + /// Makes a new `Duration` with given number of hours. + /// Equivalent to `Duration::new(0, hours * 3600, 0)` with overflow checks. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn hours(hours: i32) -> Duration { let (days, hours) = hours.div_mod_floor(&(SECS_PER_DAY / 3600)); @@ -58,6 +94,10 @@ impl Duration { Duration { secs: secs as u32, ..Duration::days(days) } } + /// Makes a new `Duration` with given number of minutes. + /// Equivalent to `Duration::new(0, mins * 60, 0)` with overflow checks. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn minutes(mins: i32) -> Duration { let (days, mins) = mins.div_mod_floor(&(SECS_PER_DAY / 60)); @@ -65,12 +105,20 @@ impl Duration { Duration { secs: secs as u32, ..Duration::days(days) } } + /// Makes a new `Duration` with given number of seconds. + /// Equivalent to `Duration::new(0, secs, 0)`. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn seconds(secs: i32) -> Duration { let (days, secs) = secs.div_mod_floor(&SECS_PER_DAY); Duration { secs: secs as u32, ..Duration::days(days) } } + /// Makes a new `Duration` with given number of milliseconds. + /// Equivalent to `Duration::new(0, 0, millis * 1_000_000)` with overflow checks. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn milliseconds(millis: i32) -> Duration { let (secs, millis) = millis.div_mod_floor(&(NANOS_PER_SEC / 1_000_000)); @@ -78,6 +126,10 @@ impl Duration { Duration { nanos: nanos as u32, ..Duration::seconds(secs) } } + /// Makes a new `Duration` with given number of microseconds. + /// Equivalent to `Duration::new(0, 0, micros * 1_000)` with overflow checks. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn microseconds(micros: i32) -> Duration { let (secs, micros) = micros.div_mod_floor(&(NANOS_PER_SEC / 1_000)); @@ -85,28 +137,43 @@ impl Duration { Duration { nanos: nanos as u32, ..Duration::seconds(secs) } } + /// Makes a new `Duration` with given number of nanoseconds. + /// Equivalent to `Duration::new(0, 0, nanos)`. + /// + /// Fails when the duration is out of bounds. #[inline] pub fn nanoseconds(nanos: i32) -> Duration { let (secs, nanos) = nanos.div_mod_floor(&NANOS_PER_SEC); Duration { nanos: nanos as u32, ..Duration::seconds(secs) } } + /// Returns the number of days in the duration. + /// For the negative duration, this is a largest integral number of days smaller than `self`. #[inline] pub fn ndays(&self) -> i32 { self.days as i32 } + /// Returns the number of (non-leap) seconds in the duration. + /// This never goes negative even when the duration is negative. #[inline] pub fn nseconds(&self) -> u32 { self.secs as u32 } + /// Returns the number of nanoseconds in the duration. + /// This never goes negative even when the duration is negative. #[inline] pub fn nnanoseconds(&self) -> u32 { self.nanos as u32 } } +impl num::Bounded for Duration { + #[inline] fn min_value() -> Duration { MIN } + #[inline] fn max_value() -> Duration { MAX } +} + impl num::Zero for Duration { #[inline] fn zero() -> Duration { diff --git a/src/lib.rs b/src/lib.rs index 99e5ce0..995a6d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,8 @@ extern crate num; -pub use duration::{MIN_DAYS, MAX_DAYS, Duration}; -pub use date::{MAX_YEAR, MIN_YEAR, Weekday, Mon, Tue, Wed, Thu, Fri, Sat, Sun}; +pub use duration::Duration; +pub use date::{Weekday, Mon, Tue, Wed, Thu, Fri, Sat, Sun}; pub use date::{Datelike, DateZ}; pub use time::{Timelike, TimeZ}; pub use datetime::DateTimeZ; @@ -24,7 +24,7 @@ pub mod datetime; fn test_readme_doomsday() { use std::iter::range_inclusive; - for y in range_inclusive(MIN_YEAR, MAX_YEAR) { + for y in range_inclusive(date::MIN.year(), date::MAX.year()) { // even months let d4 = DateZ::from_ymd(y, 4, 4); let d6 = DateZ::from_ymd(y, 6, 6);