major API surgeries.

- added a new example.
- reexported all public APIs in the crate root.
- made all constructors fail on the invalid arguments by default;
  `*_opt()` variants have been added for the original behavior.
- same for `DateZ::{succ,pred}`.
- fixed a missing overflow check from `TimeZ::from_hms_{milli,micro}`.
This commit is contained in:
Kang Seonghoon 2014-07-20 02:51:57 +09:00
parent dce4ec8f45
commit b79f6b302b
5 changed files with 332 additions and 138 deletions

View File

@ -8,6 +8,34 @@ Rust-chrono
Date and time handling for Rust.
```rust
// find out if the doomsday rule is correct!
use chrono::{MIN_YEAR, MAX_YEAR, Weekday, DateZ};
use std::iter::range_inclusive;
for y in range_inclusive(MIN_YEAR, MAX_YEAR) {
// even months
let d4 = DateZ::from_ymd(y, 4, 4);
let d6 = DateZ::from_ymd(y, 6, 6);
let d8 = DateZ::from_ymd(y, 8, 8);
let d10 = DateZ::from_ymd(y, 10, 10);
let d12 = DateZ::from_ymd(y, 12, 12);
// nine to five, seven-eleven
let d59 = DateZ::from_ymd(y, 5, 9);
let d95 = DateZ::from_ymd(y, 9, 5);
let d711 = DateZ::from_ymd(y, 7, 11);
let d117 = DateZ::from_ymd(y, 11, 7);
// "March 0"
let d30 = DateZ::from_ymd(y, 3, 1).pred();
let weekday = d30.weekday();
let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117];
assert!(other_dates.iter().all(|d| d.weekday() == weekday));
}
```
Design Goals
------------

View File

@ -229,30 +229,55 @@ impl DateZ {
DateZ::from_of(year, mdf.to_of())
}
/// Makes a new `DateZ` from year, month and day.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// Fails on the out-of-range date, invalid month and/or day.
pub fn from_ymd(year: i32, month: u32, day: u32) -> DateZ {
DateZ::from_ymd_opt(year, month, day).expect("invalid or out-of-range date")
}
/// Makes a new `DateZ` from year, month and day.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// Returns `None` on the out-of-range date, invalid month and/or day.
pub fn from_ymd(year: i32, month: u32, day: u32) -> Option<DateZ> {
pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<DateZ> {
let flags = YearFlags::from_year(year);
DateZ::from_mdf(year, Mdf::new(month, day, flags))
}
/// Makes a new `DateZ` from year and day of year (DOY or "ordinal").
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// Fails on the out-of-range date and/or invalid DOY.
pub fn from_yo(year: i32, ordinal: u32) -> DateZ {
DateZ::from_yo_opt(year, ordinal).expect("invalid or out-of-range date")
}
/// Makes a new `DateZ` from year and day of year (DOY or "ordinal").
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// Returns `None` on the out-of-range date and/or invalid DOY.
pub fn from_yo(year: i32, ordinal: u32) -> Option<DateZ> {
pub fn from_yo_opt(year: i32, ordinal: u32) -> Option<DateZ> {
let flags = YearFlags::from_year(year);
DateZ::from_of(year, Of::new(ordinal, flags))
}
/// Makes a new `DateZ` from ISO week date (year and week number) and day of the week (DOW).
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
/// The resulting `DateZ` may have a different year from the input year.
///
/// Fails on the out-of-range date and/or invalid week number.
pub fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> DateZ {
DateZ::from_isoywd_opt(year, week, weekday).expect("invalid or out-of-range date")
}
/// Makes a new `DateZ` from ISO week date (year and week number) and day of the week (DOW).
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
/// The resulting `DateZ` may have a different year from the input year.
///
/// Returns `None` on the out-of-range date and/or invalid week number.
pub fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> Option<DateZ> {
pub fn from_isoywd_opt(year: i32, week: u32, weekday: Weekday) -> Option<DateZ> {
let flags = YearFlags::from_year(year);
let nweeks = flags.nisoweeks();
if 1 <= week && week <= nweeks {
@ -310,14 +335,36 @@ impl DateZ {
}
}
/// Makes a new `DateZ` for the next date.
///
/// Fails when `self` is the last representable date.
#[inline]
pub fn succ(&self) -> Option<DateZ> {
self.with_of(self.of().succ()).or_else(|| DateZ::from_ymd(self.year() + 1, 1, 1))
pub fn succ(&self) -> DateZ {
self.succ_opt().expect("out of bound")
}
/// Makes a new `DateZ` for the next date.
///
/// Returns `None` when `self` is the last representable date.
#[inline]
pub fn pred(&self) -> Option<DateZ> {
self.with_of(self.of().pred()).or_else(|| DateZ::from_ymd(self.year() - 1, 12, 31))
pub fn succ_opt(&self) -> Option<DateZ> {
self.with_of(self.of().succ()).or_else(|| DateZ::from_ymd_opt(self.year() + 1, 1, 1))
}
/// Makes a new `DateZ` for the prior date.
///
/// Fails when `self` is the first representable date.
#[inline]
pub fn pred(&self) -> DateZ {
self.pred_opt().expect("out of bound")
}
/// Makes a new `DateZ` for the prior date.
///
/// Returns `None` when `self` is the first representable date.
#[inline]
pub fn pred_opt(&self) -> Option<DateZ> {
self.with_of(self.of().pred()).or_else(|| DateZ::from_ymd_opt(self.year() - 1, 12, 31))
}
}
@ -450,69 +497,69 @@ mod tests {
#[test]
fn test_date_from_ymd() {
assert!(DateZ::from_ymd(2012, 0, 1).is_none());
assert!(DateZ::from_ymd(2012, 1, 1).is_some());
assert!(DateZ::from_ymd(2012, 2, 29).is_some());
assert!(DateZ::from_ymd(2014, 2, 29).is_none());
assert!(DateZ::from_ymd(2014, 3, 0).is_none());
assert!(DateZ::from_ymd(2014, 3, 1).is_some());
assert!(DateZ::from_ymd(2014, 3, 31).is_some());
assert!(DateZ::from_ymd(2014, 3, 32).is_none());
assert!(DateZ::from_ymd(2014, 12, 31).is_some());
assert!(DateZ::from_ymd(2014, 13, 1).is_none());
assert!(DateZ::from_ymd_opt(2012, 0, 1).is_none());
assert!(DateZ::from_ymd_opt(2012, 1, 1).is_some());
assert!(DateZ::from_ymd_opt(2012, 2, 29).is_some());
assert!(DateZ::from_ymd_opt(2014, 2, 29).is_none());
assert!(DateZ::from_ymd_opt(2014, 3, 0).is_none());
assert!(DateZ::from_ymd_opt(2014, 3, 1).is_some());
assert!(DateZ::from_ymd_opt(2014, 3, 31).is_some());
assert!(DateZ::from_ymd_opt(2014, 3, 32).is_none());
assert!(DateZ::from_ymd_opt(2014, 12, 31).is_some());
assert!(DateZ::from_ymd_opt(2014, 13, 1).is_none());
}
#[test]
fn test_date_from_yo() {
assert!(DateZ::from_yo(2012, 0).is_none());
assert_eq!(DateZ::from_yo(2012, 1), DateZ::from_ymd(2012, 1, 1));
assert_eq!(DateZ::from_yo(2012, 2), DateZ::from_ymd(2012, 1, 2));
assert_eq!(DateZ::from_yo(2012, 32), DateZ::from_ymd(2012, 2, 1));
assert_eq!(DateZ::from_yo(2012, 60), DateZ::from_ymd(2012, 2, 29));
assert_eq!(DateZ::from_yo(2012, 61), DateZ::from_ymd(2012, 3, 1));
assert_eq!(DateZ::from_yo(2012, 100), DateZ::from_ymd(2012, 4, 9));
assert_eq!(DateZ::from_yo(2012, 200), DateZ::from_ymd(2012, 7, 18));
assert_eq!(DateZ::from_yo(2012, 300), DateZ::from_ymd(2012, 10, 26));
assert_eq!(DateZ::from_yo(2012, 366), DateZ::from_ymd(2012, 12, 31));
assert!(DateZ::from_yo(2012, 367).is_none());
assert_eq!(DateZ::from_yo_opt(2012, 0), None);
assert_eq!(DateZ::from_yo_opt(2012, 1), Some(DateZ::from_ymd(2012, 1, 1)));
assert_eq!(DateZ::from_yo_opt(2012, 2), Some(DateZ::from_ymd(2012, 1, 2)));
assert_eq!(DateZ::from_yo_opt(2012, 32), Some(DateZ::from_ymd(2012, 2, 1)));
assert_eq!(DateZ::from_yo_opt(2012, 60), Some(DateZ::from_ymd(2012, 2, 29)));
assert_eq!(DateZ::from_yo_opt(2012, 61), Some(DateZ::from_ymd(2012, 3, 1)));
assert_eq!(DateZ::from_yo_opt(2012, 100), Some(DateZ::from_ymd(2012, 4, 9)));
assert_eq!(DateZ::from_yo_opt(2012, 200), Some(DateZ::from_ymd(2012, 7, 18)));
assert_eq!(DateZ::from_yo_opt(2012, 300), Some(DateZ::from_ymd(2012, 10, 26)));
assert_eq!(DateZ::from_yo_opt(2012, 366), Some(DateZ::from_ymd(2012, 12, 31)));
assert_eq!(DateZ::from_yo_opt(2012, 367), None);
assert!(DateZ::from_yo(2014, 0).is_none());
assert_eq!(DateZ::from_yo(2014, 1), DateZ::from_ymd(2014, 1, 1));
assert_eq!(DateZ::from_yo(2014, 2), DateZ::from_ymd(2014, 1, 2));
assert_eq!(DateZ::from_yo(2014, 32), DateZ::from_ymd(2014, 2, 1));
assert_eq!(DateZ::from_yo(2014, 59), DateZ::from_ymd(2014, 2, 28));
assert_eq!(DateZ::from_yo(2014, 60), DateZ::from_ymd(2014, 3, 1));
assert_eq!(DateZ::from_yo(2014, 100), DateZ::from_ymd(2014, 4, 10));
assert_eq!(DateZ::from_yo(2014, 200), DateZ::from_ymd(2014, 7, 19));
assert_eq!(DateZ::from_yo(2014, 300), DateZ::from_ymd(2014, 10, 27));
assert_eq!(DateZ::from_yo(2014, 365), DateZ::from_ymd(2014, 12, 31));
assert!(DateZ::from_yo(2014, 366).is_none());
assert_eq!(DateZ::from_yo_opt(2014, 0), None);
assert_eq!(DateZ::from_yo_opt(2014, 1), Some(DateZ::from_ymd(2014, 1, 1)));
assert_eq!(DateZ::from_yo_opt(2014, 2), Some(DateZ::from_ymd(2014, 1, 2)));
assert_eq!(DateZ::from_yo_opt(2014, 32), Some(DateZ::from_ymd(2014, 2, 1)));
assert_eq!(DateZ::from_yo_opt(2014, 59), Some(DateZ::from_ymd(2014, 2, 28)));
assert_eq!(DateZ::from_yo_opt(2014, 60), Some(DateZ::from_ymd(2014, 3, 1)));
assert_eq!(DateZ::from_yo_opt(2014, 100), Some(DateZ::from_ymd(2014, 4, 10)));
assert_eq!(DateZ::from_yo_opt(2014, 200), Some(DateZ::from_ymd(2014, 7, 19)));
assert_eq!(DateZ::from_yo_opt(2014, 300), Some(DateZ::from_ymd(2014, 10, 27)));
assert_eq!(DateZ::from_yo_opt(2014, 365), Some(DateZ::from_ymd(2014, 12, 31)));
assert_eq!(DateZ::from_yo_opt(2014, 366), None);
}
#[test]
fn test_date_from_isoywd() {
assert!(DateZ::from_isoywd(2004, 0, Sun).is_none());
assert_eq!(DateZ::from_isoywd(2004, 1, Mon), DateZ::from_ymd(2003, 12, 29));
assert_eq!(DateZ::from_isoywd(2004, 1, Sun), DateZ::from_ymd(2004, 1, 4));
assert_eq!(DateZ::from_isoywd(2004, 2, Mon), DateZ::from_ymd(2004, 1, 5));
assert_eq!(DateZ::from_isoywd(2004, 2, Sun), DateZ::from_ymd(2004, 1, 11));
assert_eq!(DateZ::from_isoywd(2004, 52, Mon), DateZ::from_ymd(2004, 12, 20));
assert_eq!(DateZ::from_isoywd(2004, 52, Sun), DateZ::from_ymd(2004, 12, 26));
assert_eq!(DateZ::from_isoywd(2004, 53, Mon), DateZ::from_ymd(2004, 12, 27));
assert_eq!(DateZ::from_isoywd(2004, 53, Sun), DateZ::from_ymd(2005, 1, 2));
assert!(DateZ::from_isoywd(2004, 54, Mon).is_none());
assert_eq!(DateZ::from_isoywd_opt(2004, 0, Sun), None);
assert_eq!(DateZ::from_isoywd_opt(2004, 1, Mon), Some(DateZ::from_ymd(2003, 12, 29)));
assert_eq!(DateZ::from_isoywd_opt(2004, 1, Sun), Some(DateZ::from_ymd(2004, 1, 4)));
assert_eq!(DateZ::from_isoywd_opt(2004, 2, Mon), Some(DateZ::from_ymd(2004, 1, 5)));
assert_eq!(DateZ::from_isoywd_opt(2004, 2, Sun), Some(DateZ::from_ymd(2004, 1, 11)));
assert_eq!(DateZ::from_isoywd_opt(2004, 52, Mon), Some(DateZ::from_ymd(2004, 12, 20)));
assert_eq!(DateZ::from_isoywd_opt(2004, 52, Sun), Some(DateZ::from_ymd(2004, 12, 26)));
assert_eq!(DateZ::from_isoywd_opt(2004, 53, Mon), Some(DateZ::from_ymd(2004, 12, 27)));
assert_eq!(DateZ::from_isoywd_opt(2004, 53, Sun), Some(DateZ::from_ymd(2005, 1, 2)));
assert_eq!(DateZ::from_isoywd_opt(2004, 54, Mon), None);
assert!(DateZ::from_isoywd(2011, 0, Sun).is_none());
assert_eq!(DateZ::from_isoywd(2011, 1, Mon), DateZ::from_ymd(2011, 1, 3));
assert_eq!(DateZ::from_isoywd(2011, 1, Sun), DateZ::from_ymd(2011, 1, 9));
assert_eq!(DateZ::from_isoywd(2011, 2, Mon), DateZ::from_ymd(2011, 1, 10));
assert_eq!(DateZ::from_isoywd(2011, 2, Sun), DateZ::from_ymd(2011, 1, 16));
assert_eq!(DateZ::from_isoywd_opt(2011, 0, Sun), None);
assert_eq!(DateZ::from_isoywd_opt(2011, 1, Mon), Some(DateZ::from_ymd(2011, 1, 3)));
assert_eq!(DateZ::from_isoywd_opt(2011, 1, Sun), Some(DateZ::from_ymd(2011, 1, 9)));
assert_eq!(DateZ::from_isoywd_opt(2011, 2, Mon), Some(DateZ::from_ymd(2011, 1, 10)));
assert_eq!(DateZ::from_isoywd_opt(2011, 2, Sun), Some(DateZ::from_ymd(2011, 1, 16)));
assert_eq!(DateZ::from_isoywd(2018, 51, Mon), DateZ::from_ymd(2018, 12, 17));
assert_eq!(DateZ::from_isoywd(2018, 51, Sun), DateZ::from_ymd(2018, 12, 23));
assert_eq!(DateZ::from_isoywd(2018, 52, Mon), DateZ::from_ymd(2018, 12, 24));
assert_eq!(DateZ::from_isoywd(2018, 52, Sun), DateZ::from_ymd(2018, 12, 30));
assert!(DateZ::from_isoywd(2018, 53, Mon).is_none());
assert_eq!(DateZ::from_isoywd_opt(2018, 51, Mon), Some(DateZ::from_ymd(2018, 12, 17)));
assert_eq!(DateZ::from_isoywd_opt(2018, 51, Sun), Some(DateZ::from_ymd(2018, 12, 23)));
assert_eq!(DateZ::from_isoywd_opt(2018, 52, Mon), Some(DateZ::from_ymd(2018, 12, 24)));
assert_eq!(DateZ::from_isoywd_opt(2018, 52, Sun), Some(DateZ::from_ymd(2018, 12, 30)));
assert_eq!(DateZ::from_isoywd_opt(2018, 53, Mon), None);
}
#[test]
@ -520,7 +567,7 @@ mod tests {
for year in range_inclusive(2000i32, 2400) {
for week in range_inclusive(1u32, 53) {
for &weekday in [Mon, Tue, Wed, Thu, Fri, Sat, Sun].iter() {
let d = DateZ::from_isoywd(year, week, weekday);
let d = DateZ::from_isoywd_opt(year, week, weekday);
if d.is_some() {
let d = d.unwrap();
assert_eq!(d.weekday(), weekday);
@ -536,11 +583,11 @@ mod tests {
for year in range_inclusive(2000i32, 2400) {
for month in range_inclusive(1u32, 12) {
for day in range_inclusive(1u32, 31) {
let d = DateZ::from_ymd(year, month, day);
let d = DateZ::from_ymd_opt(year, month, day);
if d.is_some() {
let d = d.unwrap();
let (year_, week_, weekday_) = d.isoweekdate();
let d_ = DateZ::from_isoywd(year_, week_, weekday_).unwrap();
let d_ = DateZ::from_isoywd(year_, week_, weekday_);
assert_eq!(d, d_);
}
}
@ -552,18 +599,16 @@ mod tests {
fn test_date_fields() {
fn check(year: i32, month: u32, day: u32, ordinal: u32) {
let d1 = DateZ::from_ymd(year, month, day);
assert!(d1.is_some());
assert_eq!(d1.unwrap().year(), year);
assert_eq!(d1.unwrap().month(), month);
assert_eq!(d1.unwrap().day(), day);
assert_eq!(d1.unwrap().ordinal(), ordinal);
assert_eq!(d1.year(), year);
assert_eq!(d1.month(), month);
assert_eq!(d1.day(), day);
assert_eq!(d1.ordinal(), ordinal);
let d2 = DateZ::from_yo(year, ordinal);
assert!(d2.is_some());
assert_eq!(d2.unwrap().year(), year);
assert_eq!(d2.unwrap().month(), month);
assert_eq!(d2.unwrap().day(), day);
assert_eq!(d2.unwrap().ordinal(), ordinal);
assert_eq!(d2.year(), year);
assert_eq!(d2.month(), month);
assert_eq!(d2.day(), day);
assert_eq!(d2.ordinal(), ordinal);
assert_eq!(d1, d2);
}
@ -591,83 +636,83 @@ mod tests {
#[test]
fn test_date_weekday() {
assert_eq!(DateZ::from_ymd(1582, 10, 15).unwrap().weekday(), Fri);
assert_eq!(DateZ::from_ymd(1875, 5, 20).unwrap().weekday(), Thu); // ISO 8601 reference date
assert_eq!(DateZ::from_ymd(2000, 1, 1).unwrap().weekday(), Sat);
assert_eq!(DateZ::from_ymd(1582, 10, 15).weekday(), Fri);
assert_eq!(DateZ::from_ymd(1875, 5, 20).weekday(), Thu); // ISO 8601 reference date
assert_eq!(DateZ::from_ymd(2000, 1, 1).weekday(), Sat);
}
#[test]
fn test_date_with_fields() {
let d = DateZ::from_ymd(2000, 2, 29).unwrap();
assert_eq!(d.with_year(-400), DateZ::from_ymd(-400, 2, 29));
let d = DateZ::from_ymd(2000, 2, 29);
assert_eq!(d.with_year(-400), Some(DateZ::from_ymd(-400, 2, 29)));
assert_eq!(d.with_year(-100), None);
assert_eq!(d.with_year(1600), DateZ::from_ymd(1600, 2, 29));
assert_eq!(d.with_year(1600), Some(DateZ::from_ymd(1600, 2, 29)));
assert_eq!(d.with_year(1900), None);
assert_eq!(d.with_year(2000), DateZ::from_ymd(2000, 2, 29));
assert_eq!(d.with_year(2000), Some(DateZ::from_ymd(2000, 2, 29)));
assert_eq!(d.with_year(2001), None);
assert_eq!(d.with_year(2004), DateZ::from_ymd(2004, 2, 29));
assert_eq!(d.with_year(2004), Some(DateZ::from_ymd(2004, 2, 29)));
assert_eq!(d.with_year(i32::MAX), None);
let d = DateZ::from_ymd(2000, 4, 30).unwrap();
let d = DateZ::from_ymd(2000, 4, 30);
assert_eq!(d.with_month(0), None);
assert_eq!(d.with_month(1), DateZ::from_ymd(2000, 1, 30));
assert_eq!(d.with_month(1), Some(DateZ::from_ymd(2000, 1, 30)));
assert_eq!(d.with_month(2), None);
assert_eq!(d.with_month(3), DateZ::from_ymd(2000, 3, 30));
assert_eq!(d.with_month(4), DateZ::from_ymd(2000, 4, 30));
assert_eq!(d.with_month(12), DateZ::from_ymd(2000, 12, 30));
assert_eq!(d.with_month(3), Some(DateZ::from_ymd(2000, 3, 30)));
assert_eq!(d.with_month(4), Some(DateZ::from_ymd(2000, 4, 30)));
assert_eq!(d.with_month(12), Some(DateZ::from_ymd(2000, 12, 30)));
assert_eq!(d.with_month(13), None);
assert_eq!(d.with_month(u32::MAX), None);
let d = DateZ::from_ymd(2000, 2, 8).unwrap();
let d = DateZ::from_ymd(2000, 2, 8);
assert_eq!(d.with_day(0), None);
assert_eq!(d.with_day(1), DateZ::from_ymd(2000, 2, 1));
assert_eq!(d.with_day(29), DateZ::from_ymd(2000, 2, 29));
assert_eq!(d.with_day(1), Some(DateZ::from_ymd(2000, 2, 1)));
assert_eq!(d.with_day(29), Some(DateZ::from_ymd(2000, 2, 29)));
assert_eq!(d.with_day(30), None);
assert_eq!(d.with_day(u32::MAX), None);
let d = DateZ::from_ymd(2000, 5, 5).unwrap();
let d = DateZ::from_ymd(2000, 5, 5);
assert_eq!(d.with_ordinal(0), None);
assert_eq!(d.with_ordinal(1), DateZ::from_ymd(2000, 1, 1));
assert_eq!(d.with_ordinal(60), DateZ::from_ymd(2000, 2, 29));
assert_eq!(d.with_ordinal(61), DateZ::from_ymd(2000, 3, 1));
assert_eq!(d.with_ordinal(366), DateZ::from_ymd(2000, 12, 31));
assert_eq!(d.with_ordinal(1), Some(DateZ::from_ymd(2000, 1, 1)));
assert_eq!(d.with_ordinal(60), Some(DateZ::from_ymd(2000, 2, 29)));
assert_eq!(d.with_ordinal(61), Some(DateZ::from_ymd(2000, 3, 1)));
assert_eq!(d.with_ordinal(366), Some(DateZ::from_ymd(2000, 12, 31)));
assert_eq!(d.with_ordinal(367), None);
assert_eq!(d.with_ordinal(u32::MAX), None);
}
#[test]
fn test_date_ndays_from_ce() {
assert_eq!(DateZ::from_ymd(1, 1, 1).unwrap().ndays_from_ce(), 1);
assert_eq!(DateZ::from_ymd(1, 1, 1).ndays_from_ce(), 1);
for year in range_inclusive(-9999i32, 10000) {
assert_eq!(DateZ::from_ymd(year, 1, 1).unwrap().ndays_from_ce(),
DateZ::from_ymd(year - 1, 12, 31).unwrap().ndays_from_ce() + 1);
assert_eq!(DateZ::from_ymd(year, 1, 1).ndays_from_ce(),
DateZ::from_ymd(year - 1, 12, 31).ndays_from_ce() + 1);
}
}
#[test]
fn test_date_succ() {
assert_eq!(DateZ::from_ymd(2014, 5, 6).unwrap().succ(), DateZ::from_ymd(2014, 5, 7));
assert_eq!(DateZ::from_ymd(2014, 5, 31).unwrap().succ(), DateZ::from_ymd(2014, 6, 1));
assert_eq!(DateZ::from_ymd(2014, 12, 31).unwrap().succ(), DateZ::from_ymd(2015, 1, 1));
assert_eq!(DateZ::from_ymd(2016, 2, 28).unwrap().succ(), DateZ::from_ymd(2016, 2, 29));
assert_eq!(DateZ::from_ymd(MAX_YEAR, 12, 31).unwrap().succ(), None);
assert_eq!(DateZ::from_ymd(2014, 5, 6).succ_opt(), Some(DateZ::from_ymd(2014, 5, 7)));
assert_eq!(DateZ::from_ymd(2014, 5, 31).succ_opt(), Some(DateZ::from_ymd(2014, 6, 1)));
assert_eq!(DateZ::from_ymd(2014, 12, 31).succ_opt(), Some(DateZ::from_ymd(2015, 1, 1)));
assert_eq!(DateZ::from_ymd(2016, 2, 28).succ_opt(), Some(DateZ::from_ymd(2016, 2, 29)));
assert_eq!(DateZ::from_ymd(MAX_YEAR, 12, 31).succ_opt(), None);
}
#[test]
fn test_date_pred() {
assert_eq!(DateZ::from_ymd(2016, 3, 1).unwrap().pred(), DateZ::from_ymd(2016, 2, 29));
assert_eq!(DateZ::from_ymd(2015, 1, 1).unwrap().pred(), DateZ::from_ymd(2014, 12, 31));
assert_eq!(DateZ::from_ymd(2014, 6, 1).unwrap().pred(), DateZ::from_ymd(2014, 5, 31));
assert_eq!(DateZ::from_ymd(2014, 5, 7).unwrap().pred(), DateZ::from_ymd(2014, 5, 6));
assert_eq!(DateZ::from_ymd(MIN_YEAR, 1, 1).unwrap().pred(), None);
assert_eq!(DateZ::from_ymd(2016, 3, 1).pred_opt(), Some(DateZ::from_ymd(2016, 2, 29)));
assert_eq!(DateZ::from_ymd(2015, 1, 1).pred_opt(), Some(DateZ::from_ymd(2014, 12, 31)));
assert_eq!(DateZ::from_ymd(2014, 6, 1).pred_opt(), Some(DateZ::from_ymd(2014, 5, 31)));
assert_eq!(DateZ::from_ymd(2014, 5, 7).pred_opt(), Some(DateZ::from_ymd(2014, 5, 6)));
assert_eq!(DateZ::from_ymd(MIN_YEAR, 1, 1).pred_opt(), None);
}
#[test]
fn test_date_add() {
fn check((y1,m1,d1): (i32, u32, u32), rhs: Duration, (y,m,d): (i32, u32, u32)) {
let lhs = DateZ::from_ymd(y1, m1, d1).unwrap();
let sum = DateZ::from_ymd(y, m, d).unwrap();
let lhs = DateZ::from_ymd(y1, m1, d1);
let sum = DateZ::from_ymd(y, m, d);
assert_eq!(lhs + rhs, sum);
//assert_eq!(rhs + lhs, sum);
}
@ -686,8 +731,8 @@ mod tests {
#[test]
fn test_date_sub() {
fn check((y1,m1,d1): (i32, u32, u32), (y2,m2,d2): (i32, u32, u32), diff: Duration) {
let lhs = DateZ::from_ymd(y1, m1, d1).unwrap();
let rhs = DateZ::from_ymd(y2, m2, d2).unwrap();
let lhs = DateZ::from_ymd(y1, m1, d1);
let rhs = DateZ::from_ymd(y2, m2, d2);
assert_eq!(lhs - rhs, diff);
assert_eq!(rhs - lhs, -diff);
}
@ -702,10 +747,10 @@ mod tests {
#[test]
fn test_date_fmt() {
assert_eq!(DateZ::from_ymd(2012, 3, 4).unwrap().to_string(), "2012-03-04".to_string());
assert_eq!(DateZ::from_ymd(0, 3, 4).unwrap().to_string(), "0000-03-04".to_string());
assert_eq!(DateZ::from_ymd(-307, 3, 4).unwrap().to_string(), "-0307-03-04".to_string());
assert_eq!(DateZ::from_ymd(12345, 3, 4).unwrap().to_string(), "+12345-03-04".to_string());
assert_eq!(DateZ::from_ymd(2012, 3, 4).to_string(), "2012-03-04".to_string());
assert_eq!(DateZ::from_ymd(0, 3, 4).to_string(), "0000-03-04".to_string());
assert_eq!(DateZ::from_ymd(-307, 3, 4).to_string(), "-0307-03-04".to_string());
assert_eq!(DateZ::from_ymd(12345, 3, 4).to_string(), "+12345-03-04".to_string());
}
}

View File

@ -25,8 +25,15 @@ impl DateTimeZ {
#[inline]
pub fn from_ymdhms(year: i32, month: u32, day: u32,
hour: u32, min: u32, sec: u32) -> Option<DateTimeZ> {
match (DateZ::from_ymd(year, month, day), TimeZ::from_hms(hour, min, sec)) {
hour: u32, min: u32, sec: u32) -> DateTimeZ {
let dt = DateTimeZ::from_ymdhms_opt(year, month, day, hour, min, sec);
dt.expect("invalid or out-of-range date or time")
}
#[inline]
pub fn from_ymdhms_opt(year: i32, month: u32, day: u32,
hour: u32, min: u32, sec: u32) -> Option<DateTimeZ> {
match (DateZ::from_ymd_opt(year, month, day), TimeZ::from_hms_opt(hour, min, sec)) {
(Some(d), Some(t)) => Some(DateTimeZ::new(d, t)),
(_, _) => None,
}
@ -34,8 +41,15 @@ impl DateTimeZ {
#[inline]
pub fn from_yohms(year: i32, ordinal: u32,
hour: u32, min: u32, sec: u32) -> Option<DateTimeZ> {
match (DateZ::from_yo(year, ordinal), TimeZ::from_hms(hour, min, sec)) {
hour: u32, min: u32, sec: u32) -> DateTimeZ {
let dt = DateTimeZ::from_yohms_opt(year, ordinal, hour, min, sec);
dt.expect("invalid or out-of-range date or time")
}
#[inline]
pub fn from_yohms_opt(year: i32, ordinal: u32,
hour: u32, min: u32, sec: u32) -> Option<DateTimeZ> {
match (DateZ::from_yo_opt(year, ordinal), TimeZ::from_hms_opt(hour, min, sec)) {
(Some(d), Some(t)) => Some(DateTimeZ::new(d, t)),
(_, _) => None,
}
@ -43,8 +57,15 @@ impl DateTimeZ {
#[inline]
pub fn from_isoywdhms(year: i32, week: u32, weekday: Weekday,
hour: u32, min: u32, sec: u32) -> Option<DateTimeZ> {
match (DateZ::from_isoywd(year, week, weekday), TimeZ::from_hms(hour, min, sec)) {
hour: u32, min: u32, sec: u32) -> DateTimeZ {
let dt = DateTimeZ::from_isoywdhms_opt(year, week, weekday, hour, min, sec);
dt.expect("invalid or out-of-range date or time")
}
#[inline]
pub fn from_isoywdhms_opt(year: i32, week: u32, weekday: Weekday,
hour: u32, min: u32, sec: u32) -> Option<DateTimeZ> {
match (DateZ::from_isoywd_opt(year, week, weekday), TimeZ::from_hms_opt(hour, min, sec)) {
(Some(d), Some(t)) => Some(DateTimeZ::new(d, t)),
(_, _) => None,
}
@ -151,7 +172,7 @@ impl Add<Duration,DateTimeZ> for DateTimeZ {
if time < self.time {
// since the time portion of the duration is always positive and bounded,
// this condition always means that the time part has been overflowed.
date = date.succ().unwrap();
date = date.succ();
}
DateTimeZ { date: date, time: time }
}
@ -184,7 +205,7 @@ mod tests {
#[test]
fn test_datetime_add() {
let ymdhms = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap();
let ymdhms = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s);
assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) + Duration::seconds(3600 + 60 + 1),
ymdhms(2014, 5, 6, 8, 9, 10));
assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) + Duration::seconds(86399),
@ -197,7 +218,7 @@ mod tests {
#[test]
fn test_datetime_sub() {
let ymdhms = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap();
let ymdhms = |y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s);
assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) - ymdhms(2014, 5, 6, 7, 8, 9), Duration::zero());
assert_eq!(ymdhms(2014, 5, 6, 7, 8, 10) - ymdhms(2014, 5, 6, 7, 8, 9),
Duration::seconds(1));
@ -212,7 +233,7 @@ mod tests {
#[test]
fn test_datetime_nseconds_from_unix_epoch() {
let to_timestamp =
|y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).unwrap().nseconds_from_unix_epoch();
|y,m,d,h,n,s| DateTimeZ::from_ymdhms(y,m,d,h,n,s).nseconds_from_unix_epoch();
assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1);
assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0);
assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1);

View File

@ -9,8 +9,41 @@
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 date::{Datelike, DateZ};
pub use time::{Timelike, TimeZ};
pub use datetime::DateTimeZ;
pub mod duration;
pub mod date;
pub mod time;
pub mod datetime;
#[test]
fn test_readme_doomsday() {
use std::iter::range_inclusive;
for y in range_inclusive(MIN_YEAR, MAX_YEAR) {
// even months
let d4 = DateZ::from_ymd(y, 4, 4);
let d6 = DateZ::from_ymd(y, 6, 6);
let d8 = DateZ::from_ymd(y, 8, 8);
let d10 = DateZ::from_ymd(y, 10, 10);
let d12 = DateZ::from_ymd(y, 12, 12);
// nine to five, seven-eleven
let d59 = DateZ::from_ymd(y, 5, 9);
let d95 = DateZ::from_ymd(y, 9, 5);
let d711 = DateZ::from_ymd(y, 7, 11);
let d117 = DateZ::from_ymd(y, 11, 7);
// "March 0"
let d30 = DateZ::from_ymd(y, 3, 1).pred();
let weekday = d30.weekday();
let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117];
assert!(other_dates.iter().all(|d| d.weekday() == weekday));
}
}

View File

@ -72,12 +72,29 @@ pub struct TimeZ {
}
impl TimeZ {
/// Makes a new `TimeZ` from hour, minute and second.
///
/// Fails on invalid hour, minute and/or second.
#[inline]
pub fn from_hms(hour: u32, min: u32, sec: u32) -> TimeZ {
TimeZ::from_hms_opt(hour, min, sec).expect("invalid time")
}
/// Makes a new `TimeZ` from hour, minute and second.
///
/// Returns `None` on invalid hour, minute and/or second.
#[inline]
pub fn from_hms(hour: u32, min: u32, sec: u32) -> Option<TimeZ> {
TimeZ::from_hms_nano(hour, min, sec, 0)
pub fn from_hms_opt(hour: u32, min: u32, sec: u32) -> Option<TimeZ> {
TimeZ::from_hms_nano_opt(hour, min, sec, 0)
}
/// Makes a new `TimeZ` from hour, minute, second and millisecond.
/// The millisecond part can exceed 1,000 in order to represent the leap second.
///
/// Fails on invalid hour, minute, second and/or millisecond.
#[inline]
pub fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> TimeZ {
TimeZ::from_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
}
/// Makes a new `TimeZ` from hour, minute, second and millisecond.
@ -85,8 +102,18 @@ impl TimeZ {
///
/// Returns `None` on invalid hour, minute, second and/or millisecond.
#[inline]
pub fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> Option<TimeZ> {
TimeZ::from_hms_nano(hour, min, sec, milli * 1_000_000)
pub fn from_hms_milli_opt(hour: u32, min: u32, sec: u32, milli: u32) -> Option<TimeZ> {
milli.checked_mul(&1_000_000).and_then(|nano| TimeZ::from_hms_nano_opt(hour, min, sec,
nano))
}
/// Makes a new `TimeZ` from hour, minute, second and microsecond.
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
///
/// Fails on invalid hour, minute, second and/or microsecond.
#[inline]
pub fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> TimeZ {
TimeZ::from_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
}
/// Makes a new `TimeZ` from hour, minute, second and microsecond.
@ -94,15 +121,23 @@ impl TimeZ {
///
/// Returns `None` on invalid hour, minute, second and/or microsecond.
#[inline]
pub fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> Option<TimeZ> {
TimeZ::from_hms_nano(hour, min, sec, micro * 1_000)
pub fn from_hms_micro_opt(hour: u32, min: u32, sec: u32, micro: u32) -> Option<TimeZ> {
micro.checked_mul(&1_000).and_then(|nano| TimeZ::from_hms_nano_opt(hour, min, sec, nano))
}
/// Makes a new `TimeZ` from hour, minute, second and nanosecond.
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
///
/// Fails on invalid hour, minute, second and/or nanosecond.
pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> TimeZ {
TimeZ::from_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
}
/// Makes a new `TimeZ` from hour, minute, second and nanosecond.
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
///
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> Option<TimeZ> {
pub fn from_hms_nano_opt(hour: u32, min: u32, sec: u32, nano: u32) -> Option<TimeZ> {
if hour >= 24 || min >= 60 || sec >= 60 || nano >= 2_000_000_000 { return None; }
Some(TimeZ { hour: hour as u8, min: min as u8, sec: sec as u8, frac: nano as u32 })
}
@ -210,9 +245,34 @@ impl fmt::Show for TimeZ {
mod tests {
use super::TimeZ;
use duration::Duration;
use std::u32;
fn hmsm(hour: u32, min: u32, sec: u32, millis: u32) -> TimeZ {
TimeZ::from_hms_milli(hour, min, sec, millis).unwrap()
#[test]
fn test_time_from_hms_milli() {
assert_eq!(TimeZ::from_hms_milli_opt(3, 5, 7, 0),
Some(TimeZ::from_hms_nano(3, 5, 7, 0)));
assert_eq!(TimeZ::from_hms_milli_opt(3, 5, 7, 777),
Some(TimeZ::from_hms_nano(3, 5, 7, 777_000_000)));
assert_eq!(TimeZ::from_hms_milli_opt(3, 5, 7, 1_999),
Some(TimeZ::from_hms_nano(3, 5, 7, 1_999_000_000)));
assert_eq!(TimeZ::from_hms_milli_opt(3, 5, 7, 2_000), None);
assert_eq!(TimeZ::from_hms_milli_opt(3, 5, 7, 5_000), None); // overflow check
assert_eq!(TimeZ::from_hms_milli_opt(3, 5, 7, u32::MAX), None);
}
#[test]
fn test_time_from_hms_micro() {
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, 0),
Some(TimeZ::from_hms_nano(3, 5, 7, 0)));
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, 333),
Some(TimeZ::from_hms_nano(3, 5, 7, 333_000)));
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, 777_777),
Some(TimeZ::from_hms_nano(3, 5, 7, 777_777_000)));
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, 1_999_999),
Some(TimeZ::from_hms_nano(3, 5, 7, 1_999_999_000)));
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, 2_000_000), None);
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, 5_000_000), None); // overflow check
assert_eq!(TimeZ::from_hms_micro_opt(3, 5, 7, u32::MAX), None);
}
#[test]
@ -222,6 +282,8 @@ mod tests {
//assert_eq!(rhs + lhs, sum);
}
let hmsm = |h,m,s,mi| TimeZ::from_hms_milli(h, m, s, mi);
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));
@ -238,6 +300,8 @@ mod tests {
assert_eq!(rhs - lhs, -diff);
}
let hmsm = |h,m,s,mi| TimeZ::from_hms_milli(h, m, s, mi);
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));
@ -258,12 +322,15 @@ mod tests {
#[test]
fn test_time_fmt() {
assert_eq!(hmsm(23, 59, 59, 999).to_string(), "23:59:59,999".to_string());
assert_eq!(hmsm(23, 59, 59, 1_000).to_string(), "23:59:60".to_string());
assert_eq!(hmsm(23, 59, 59, 1_001).to_string(), "23:59:60,001".to_string());
assert_eq!(TimeZ::from_hms_micro(0, 0, 0, 43210).unwrap().to_string(),
assert_eq!(TimeZ::from_hms_milli(23, 59, 59, 999).to_string(),
"23:59:59,999".to_string());
assert_eq!(TimeZ::from_hms_milli(23, 59, 59, 1_000).to_string(),
"23:59:60".to_string());
assert_eq!(TimeZ::from_hms_milli(23, 59, 59, 1_001).to_string(),
"23:59:60,001".to_string());
assert_eq!(TimeZ::from_hms_micro(0, 0, 0, 43210).to_string(),
"00:00:00,043210".to_string());
assert_eq!(TimeZ::from_hms_nano(0, 0, 0, 6543210).unwrap().to_string(),
assert_eq!(TimeZ::from_hms_nano(0, 0, 0, 6543210).to_string(),
"00:00:00,006543210".to_string());
}
}