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
This commit is contained in:
Kang Seonghoon 2014-07-25 17:05:26 +09:00
parent f7065f1625
commit 110c7de7d3
3 changed files with 103 additions and 16 deletions

View File

@ -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<DateZ> {
@ -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<Duration,DateZ> for DateZ {
fn add(&self, rhs: &Duration) -> DateZ {
// TODO overflow
@ -478,11 +497,12 @@ impl Sub<DateZ,Duration> 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] = [

View File

@ -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<Duration> {
/// 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<Duration> {
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 {

View File

@ -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);