Skip to content

Commit

Permalink
added docs to Duration; added Duration::new_opt; removed `{MIN,MA…
Browse files Browse the repository at this point in the history
…X}_{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
  • Loading branch information
lifthrasiir committed Jul 25, 2014
1 parent f7065f1 commit 110c7de
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 16 deletions.
38 changes: 29 additions & 9 deletions src/date.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
}
}
}
Expand Down Expand Up @@ -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] = [
Expand Down
75 changes: 71 additions & 4 deletions src/duration.rs
Expand Up @@ -9,104 +9,171 @@
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,
secs: u32,
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);
let days = earlyexit!(days.checked_add(&days_).and_then(|v| v.to_i32()));
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));
let secs = hours * 3600;
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));
let secs = mins * 60;
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));
let nanos = millis * 1_000_000;
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));
let nanos = micros * 1_000;
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 {
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 110c7de

Please sign in to comment.