From 920a681d5490c02481d099f5cd4a4a2c02d2323a Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Fri, 28 Nov 2014 23:53:22 +0900 Subject: [PATCH] better documentation, some additional APIs. - `Date/Time - Duration` is now supported. (duh!) - `with_offset` methods have been added. - `LocalResult` now implements common traits. - `LocalResult` has several methods to propagate errors. this makes the initialization from untrusted sources easier (`off.ymd_opt(y,m,d).and_hms_opt(h,n,s).single()`). --- README.md | 181 +++++++++++++++++++++++++++++++++--------- src/date.rs | 30 ++++--- src/datetime.rs | 12 +++ src/lib.rs | 158 +++++++++++++++++++++++++++++++++++- src/naive/date.rs | 5 ++ src/naive/datetime.rs | 5 ++ src/naive/time.rs | 5 ++ src/offset.rs | 75 +++++++++++++++++ src/time.rs | 12 +++ 9 files changed, 435 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index e85fb84..00ff280 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,162 @@ -Rust-chrono -=========== +Chrono +====== -[![Rust-chrono on Travis CI][travis-image]][travis] +[![Chrono on Travis CI][travis-image]][travis] [travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.png [travis]: https://travis-ci.org/lifthrasiir/rust-chrono Date and time handling for Rust. +It aims to be a feature-complete superset of the [time](https://github.com/rust-lang/time) library. +In particular, + +* Chrono strictly adheres to ISO 8601. +* Chrono is timezone-aware by default, with separate timezone-naive types. +* Chrono is space-optimal and (while not being the primary goal) reasonably efficient. + +There were several previous attempts to bring a good date and time library to Rust, +which Chrono builts upon and should acknowledge: + +* [Initial research on the wiki](https://github.com/rust-lang/rust/wiki/Lib-datetime) +* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs) +* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime) [Complete Documentation](https://lifthrasiir.github.io/rust-chrono/chrono/) -```rust -// find out if the doomsday rule is correct! -use chrono::{Weekday, NaiveDate, naive}; -use std::iter::range_inclusive; +## Duration -for y in range_inclusive(naive::date::MIN.year(), naive::date::MAX.year()) { - // even months - let d4 = NaiveDate::from_ymd(y, 4, 4); - let d6 = NaiveDate::from_ymd(y, 6, 6); - let d8 = NaiveDate::from_ymd(y, 8, 8); - let d10 = NaiveDate::from_ymd(y, 10, 10); - let d12 = NaiveDate::from_ymd(y, 12, 12); +Chrono used to have a `Duration` type, which represents the time span. +Now Rust standard library includes it as `std::time::duration::Duration` and +Chrono simply reexports it. - // nine to five, seven-eleven - let d59 = NaiveDate::from_ymd(y, 5, 9); - let d95 = NaiveDate::from_ymd(y, 9, 5); - let d711 = NaiveDate::from_ymd(y, 7, 11); - let d117 = NaiveDate::from_ymd(y, 11, 7); +## Date and Time - // "March 0" - let d30 = NaiveDate::from_ymd(y, 3, 1).pred(); +Chrono provides a `DateTime` type for the combined date and time. - 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)); -} -``` +`DateTime`, among others, is timezone-aware and +must be constructed from the timezone object (`Offset`). +`DateTime`s with different offsets do not mix, but can be converted to each other. -Design Goals ------------- +You can get the current date and time in the UTC timezone (`UTC::now()`) +or in the local timezone (`Local::now()`). -* 1-to-1 correspondence with ISO 8601. -* Timezone-aware by default. -* Space efficient. -* Moderate lookup table size, should not exceed a few KBs. -* Avoid divisions as much as possible. +~~~~ {.rust} +use chrono::{UTC, Local, DateTime}; -References ----------- +let utc: DateTime = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z` +let local: DateTime = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00` +# let _ = utc; let _ = local; +~~~~ -* https://github.com/mozilla/rust/wiki/Lib-datetime -* https://github.com/luisbg/rust-datetime/wiki/Use-Cases +Alternatively, you can create your own date and time. +This is a bit verbose due to Rust's lack of function and method overloading, +but in turn we get a rich combination of initialization methods. + +~~~~ {.rust} +use chrono::{UTC, Offset, Weekday, LocalResult}; + +let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z` +// July 8 is 188th day of the year 2014 (`o` for "ordinal") +assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11)); +// July 8 is Tuesday in ISO week 28 of the year 2014. +assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11)); + +let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z` +assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000)); +assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000)); + +// dynamic verification +assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33), + LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33))); +assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None); +assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); +~~~~ + +Various properties are available to the date and time, and can be altered individually. +Most of them are defined in the traits `Datelike` and `Timelike` which you should `use` before. +Addition and subtraction is also supported. +The following illustrates most supported operations to the date and time: + +~~~~ {.rust} +# /* we intentionally fake the datetime... +use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration}; + +// assume this returned `2014-11-28T21:45:59.324310806+09:00`: +let dt = Local::now(); +# */ // up to here. we now define a fixed datetime for the illustrative purpose. +# use chrono::{UTC, FixedOffset, Offset, Datelike, Timelike, Weekday, Duration}; +# let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806); + +// property accessors +assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); +assert_eq!((dt.month0(), dt.day0()), (10, 27)); // for unfortunate souls +assert_eq!((dt.hour(), dt.minute(), dt.second()), (21, 45, 59)); +assert_eq!(dt.weekday(), Weekday::Fri); +assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sat=7 +assert_eq!(dt.ordinal(), 332); // the day of year +assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1 + +// offset accessor and manipulation +assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9)); +assert_eq!(dt.with_offset(UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); + +// a sample of property manipulations (validates dynamically) +assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday +assert_eq!(dt.with_day(32), None); +assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE + +// arithmetic operations +assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8), + Duration::seconds(-2 * 3600 + 2)); +assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000), + UTC.ymd(2001, 9, 9).and_hms(1, 46, 40)); +assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000), + UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); +~~~~ + +Formatting is done via the `format` method, +which format is equivalent to the familiar `strftime` format. +The default `to_string` method also gives a reasonable representation. + +~~~~ {.rust} +use chrono::{UTC, Offset}; + +let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); +assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09".into_string()); +assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014".into_string()); +assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); + +assert_eq!(dt.to_string(), "2014-11-28T12:00:09Z".into_string()); +~~~~ + +## Individual date and time + +Chrono also provides an individual date type (`Date`) and time type (`Time`). +They also have offsets attached, and have to be constructed via offsets. +Most operations available to `DateTime` are also available to `Date` and `Time` +whenever appropriate. + +~~~~ {.rust} +use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday}; + +# // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;) +assert_eq!(UTC::today(), UTC::now().date()); +assert_eq!(Local::today(), Local::now().date()); + +assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri); +assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None); +assert_eq!(UTC.hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809".into_string()); +~~~~ + +`DateTime` has two methods, `date` and `time`, +which return narrow views to its date and time components respectively. + +## Naive date and time + +Chrono provides naive counterparts to `Date`, `Time` and `DateTime` +as `NaiveDate`, `NaiveTime` and `NaiveDateTime` respectively. + +They have almost equivalent interfaces as their timezone-aware twins, +but are not associated to offsets obviously and can be quite low-level. +They are mostly useful for building blocks for higher-level types. diff --git a/src/date.rs b/src/date.rs index 9dc4d12..358aaaa 100644 --- a/src/date.rs +++ b/src/date.rs @@ -37,7 +37,7 @@ impl Date { Date { date: date, offset: offset } } - /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. + /// Makes a new `DateTime` from the current date and given `NaiveTime`. /// The offset in the current date is preserved. /// /// Fails on invalid datetime. @@ -46,7 +46,7 @@ impl Date { self.offset.from_local_datetime(&self.date.and_time(time)).single() } - /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. + /// Makes a new `DateTime` from the current date, hour, minute and second. /// The offset in the current date is preserved. /// /// Fails on invalid hour, minute and/or second. @@ -55,7 +55,7 @@ impl Date { self.and_hms_opt(hour, min, sec).expect("invalid time") } - /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. + /// Makes a new `DateTime` from the current date, hour, minute and second. /// The offset in the current date is preserved. /// /// Returns `None` on invalid hour, minute and/or second. @@ -64,7 +64,7 @@ impl Date { NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time)) } - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. + /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. /// The millisecond part can exceed 1,000 in order to represent the leap second. /// The offset in the current date is preserved. /// @@ -74,7 +74,7 @@ impl Date { self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time") } - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. + /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. /// The millisecond part can exceed 1,000 in order to represent the leap second. /// The offset in the current date is preserved. /// @@ -85,7 +85,7 @@ impl Date { NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time)) } - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. + /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. /// The microsecond part can exceed 1,000,000 in order to represent the leap second. /// The offset in the current date is preserved. /// @@ -95,7 +95,7 @@ impl Date { self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time") } - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. + /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. /// The microsecond part can exceed 1,000,000 in order to represent the leap second. /// The offset in the current date is preserved. /// @@ -106,7 +106,7 @@ impl Date { NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time)) } - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. + /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. /// The offset in the current date is preserved. /// @@ -116,7 +116,7 @@ impl Date { self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time") } - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. + /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. /// The offset in the current date is preserved. /// @@ -165,6 +165,13 @@ impl Date { &self.offset } + /// Changes the associated offset. + /// This does not change the actual `Date` (but will change the string representation). + #[inline] + pub fn with_offset(&self, offset: Off2) -> Date { + Date::from_utc(self.date, offset) + } + /// Formats the date in the specified format string. /// See the `format` module on the supported escape sequences. #[inline] @@ -274,6 +281,11 @@ impl Sub,Duration> for Date { } } +impl Sub> for Date { + #[inline] + fn sub(&self, rhs: &Duration) -> Date { self.add(&-*rhs) } +} + impl fmt::Show for Date { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.local(), self.offset) diff --git a/src/datetime.rs b/src/datetime.rs index e0b6ad0..c495f79 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -55,6 +55,13 @@ impl DateTime { &self.offset } + /// Changes the associated offset. + /// This does not change the actual `DateTime` (but will change the string representation). + #[inline] + pub fn with_offset(&self, offset: Off2) -> DateTime { + DateTime::from_utc(self.datetime, offset) + } + /// Formats the combined date and time in the specified format string. /// See the `format` module on the supported escape sequences. #[inline] @@ -196,6 +203,11 @@ impl Sub,Duration> for DateTime { } } +impl Sub> for DateTime { + #[inline] + fn sub(&self, rhs: &Duration) -> DateTime { self.add(&-*rhs) } +} + impl fmt::Show for DateTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.local(), self.offset) diff --git a/src/lib.rs b/src/lib.rs index 4ad8948..561719b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,14 +3,168 @@ // See README.md and LICENSE.txt for details. /*! -Experimental date and time handling for Rust. + +# Chrono + +Date and time handling for Rust. +It aims to be a feature-complete superset of the [time](https://github.com/rust-lang/time) library. +In particular, + +* Chrono strictly adheres to ISO 8601. +* Chrono is timezone-aware by default, with separate timezone-naive types. +* Chrono is space-optimal and (while not being the primary goal) reasonably efficient. + +There were several previous attempts to bring a good date and time library to Rust, +which Chrono builts upon and should acknowledge: + +* [Initial research on the wiki](https://github.com/rust-lang/rust/wiki/Lib-datetime) +* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs) +* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime) + +## Duration + +Chrono used to have a `Duration` type, which represents the time span. +Now Rust standard library includes it as `std::time::duration::Duration` and +Chrono simply reexports it. + +## Date and Time + +Chrono provides a `DateTime` type for the combined date and time. + +`DateTime`, among others, is timezone-aware and +must be constructed from the timezone object (`Offset`). +`DateTime`s with different offsets do not mix, but can be converted to each other. + +You can get the current date and time in the UTC timezone (`UTC::now()`) +or in the local timezone (`Local::now()`). + +~~~~ {.rust} +use chrono::{UTC, Local, DateTime}; + +let utc: DateTime = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z` +let local: DateTime = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00` +# let _ = utc; let _ = local; +~~~~ + +Alternatively, you can create your own date and time. +This is a bit verbose due to Rust's lack of function and method overloading, +but in turn we get a rich combination of initialization methods. + +~~~~ {.rust} +use chrono::{UTC, Offset, Weekday, LocalResult}; + +let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z` +// July 8 is 188th day of the year 2014 (`o` for "ordinal") +assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11)); +// July 8 is Tuesday in ISO week 28 of the year 2014. +assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11)); + +let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z` +assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000)); +assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000)); + +// dynamic verification +assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33), + LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33))); +assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None); +assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); +~~~~ + +Various properties are available to the date and time, and can be altered individually. +Most of them are defined in the traits `Datelike` and `Timelike` which you should `use` before. +Addition and subtraction is also supported. +The following illustrates most supported operations to the date and time: + +~~~~ {.rust} +# /* we intentionally fake the datetime... +use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration}; + +// assume this returned `2014-11-28T21:45:59.324310806+09:00`: +let dt = Local::now(); +# */ // up to here. we now define a fixed datetime for the illustrative purpose. +# use chrono::{UTC, FixedOffset, Offset, Datelike, Timelike, Weekday, Duration}; +# let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806); + +// property accessors +assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); +assert_eq!((dt.month0(), dt.day0()), (10, 27)); // for unfortunate souls +assert_eq!((dt.hour(), dt.minute(), dt.second()), (21, 45, 59)); +assert_eq!(dt.weekday(), Weekday::Fri); +assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sat=7 +assert_eq!(dt.ordinal(), 332); // the day of year +assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1 + +// offset accessor and manipulation +assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9)); +assert_eq!(dt.with_offset(UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); + +// a sample of property manipulations (validates dynamically) +assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday +assert_eq!(dt.with_day(32), None); +assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE + +// arithmetic operations +assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8), + Duration::seconds(-2 * 3600 + 2)); +assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000), + UTC.ymd(2001, 9, 9).and_hms(1, 46, 40)); +assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000), + UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); +~~~~ + +Formatting is done via the `format` method, +which format is equivalent to the familiar `strftime` format. +The default `to_string` method also gives a reasonable representation. + +~~~~ {.rust} +use chrono::{UTC, Offset}; + +let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); +assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09".into_string()); +assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014".into_string()); +assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); + +assert_eq!(dt.to_string(), "2014-11-28T12:00:09Z".into_string()); +~~~~ + +## Individual date and time + +Chrono also provides an individual date type (`Date`) and time type (`Time`). +They also have offsets attached, and have to be constructed via offsets. +Most operations available to `DateTime` are also available to `Date` and `Time` +whenever appropriate. + +~~~~ {.rust} +use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday}; + +# // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;) +assert_eq!(UTC::today(), UTC::now().date()); +assert_eq!(Local::today(), Local::now().date()); + +assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri); +assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None); +assert_eq!(UTC.hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809".into_string()); +~~~~ + +`DateTime` has two methods, `date` and `time`, +which return narrow views to its date and time components respectively. + +## Naive date and time + +Chrono provides naive counterparts to `Date`, `Time` and `DateTime` +as `NaiveDate`, `NaiveTime` and `NaiveDateTime` respectively. + +They have almost equivalent interfaces as their timezone-aware twins, +but are not associated to offsets obviously and can be quite low-level. +They are mostly useful for building blocks for higher-level types. + */ #![comment = "Date and time library for Rust"] #![license = "MIT"] #![doc(html_root_url = "https://lifthrasiir.github.io/rust-chrono/")] -#![feature(macro_rules)] +#![feature(macro_rules, associated_types)] #![deny(missing_docs)] extern crate "time" as stdtime; diff --git a/src/naive/date.rs b/src/naive/date.rs index 1375306..7007e27 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -412,6 +412,11 @@ impl Sub for NaiveDate { } } +impl Sub for NaiveDate { + #[inline] + fn sub(&self, rhs: &Duration) -> NaiveDate { self.add(&-*rhs) } +} + impl fmt::Show for NaiveDate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let year = self.year(); diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index c6fd427..92e8d7f 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -194,6 +194,11 @@ impl Sub for NaiveDateTime { } } +impl Sub for NaiveDateTime { + #[inline] + fn sub(&self, rhs: &Duration) -> NaiveDateTime { self.add(&-*rhs) } +} + impl fmt::Show for NaiveDateTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}T{}", self.date, self.time) diff --git a/src/naive/time.rs b/src/naive/time.rs index cdfeb5b..a0dab9a 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -212,6 +212,11 @@ impl Sub for NaiveTime { } } +impl Sub for NaiveTime { + #[inline] + fn sub(&self, rhs: &Duration) -> NaiveTime { self.add(&-*rhs) } +} + impl fmt::Show for NaiveTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (hour, min, sec) = self.hms(); diff --git a/src/offset.rs b/src/offset.rs index ea14c22..e760cdd 100644 --- a/src/offset.rs +++ b/src/offset.rs @@ -21,6 +21,7 @@ use time::Time; use datetime::DateTime; /// The conversion result from the local time to the timezone-aware datetime types. +#[deriving(Clone, PartialEq, Show)] pub enum LocalResult { /// Given local time representation is invalid. /// This can occur when, for example, the positive timezone transition. @@ -49,6 +50,80 @@ impl LocalResult { } } +impl LocalResult> { + /// Makes a new `DateTime` from the current date and given `NaiveTime`. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_time(self, time: NaiveTime) -> LocalResult> { + match self { + LocalResult::Single(d) => d.and_time(time) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute and second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult> { + match self { + LocalResult::Single(d) => d.and_hms_opt(hour, min, sec) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. + /// The millisecond part can exceed 1,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_milli_opt(self, hour: u32, min: u32, sec: u32, + milli: u32) -> LocalResult> { + match self { + LocalResult::Single(d) => d.and_hms_milli_opt(hour, min, sec, milli) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. + /// The microsecond part can exceed 1,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_micro_opt(self, hour: u32, min: u32, sec: u32, + micro: u32) -> LocalResult> { + match self { + LocalResult::Single(d) => d.and_hms_micro_opt(hour, min, sec, micro) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. + /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_nano_opt(self, hour: u32, min: u32, sec: u32, + nano: u32) -> LocalResult> { + match self { + LocalResult::Single(d) => d.and_hms_nano_opt(hour, min, sec, nano) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + +} + impl LocalResult { /// Returns the single unique conversion result, or fails accordingly. pub fn unwrap(self) -> T { diff --git a/src/time.rs b/src/time.rs index a344d00..994ce05 100644 --- a/src/time.rs +++ b/src/time.rs @@ -35,6 +35,13 @@ impl Time { &self.offset } + /// Changes the associated offset. + /// This does not change the actual `Time` (but will change the string representation). + #[inline] + pub fn with_offset(&self, offset: Off2) -> Time { + Time::from_utc(self.time, offset) + } + /// Formats the time in the specified format string. /// See the `format` module on the supported escape sequences. #[inline] @@ -124,6 +131,11 @@ impl Sub,Duration> for Time { } } +impl Sub> for Time { + #[inline] + fn sub(&self, rhs: &Duration) -> Time { self.add(&-*rhs) } +} + impl fmt::Show for Time { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.local(), self.offset)