From 881e6c2dccfdcffda5d858d16d1625a9b2703f7b Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Tue, 4 Oct 2016 19:07:11 -0400 Subject: [PATCH 01/53] Clarify that Duration is an "accurate" duration The ISO 8601 format includes both "nominal" (year, month, week, and day) and "accurate" (hour, minute, and second) components. However, the `Duration` type only represents an "accurate" duration because arithmetic with nominal components is not defined in ISO 8601. --- src/lib.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 68f390f..ad35171 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,11 +49,13 @@ //! //! ### Duration //! -//! [**`Duration`**](./struct.Duration.html) -//! represents the magnitude of a time span. `Duration` used to be provided by Chrono. -//! It has been moved to the `time` crate as the -//! [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is -//! still re-exported from Chrono. +//! [**`Duration`**](./struct.Duration.html) represents the magnitude of a time +//! span. Note that this is an "accurate" duration represented as seconds and +//! nanoseconds and does not represent "nominal" components such as days or +//! months. `Duration` used to be provided by Chrono. It has been moved to the +//! `time` crate as the +//! [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) +//! type, but is still re-exported from Chrono. //! //! ### Date and Time //! @@ -351,7 +353,12 @@ macro_rules! try_opt { mod div; pub mod duration { - //! ISO 8601 duration. + //! ISO 8601 accurate duration. + //! + //! Note that this is an "accurate" (i.e. "exact") duration represented as + //! seconds and nanoseconds. It does not include the "nominal" components + //! (years, months, weeks, and days) in the ISO 8601 duration format + //! because arithmetic with nominal components is not defined in ISO 8601. //! //! This used to be a part of rust-chrono, //! but has been subsequently merged into Rust's standard library. From a4c1cc6ed21e5fc67725064d452c03ae8c265216 Mon Sep 17 00:00:00 2001 From: Brandon W Maister Date: Sat, 12 Nov 2016 16:09:08 -0500 Subject: [PATCH 02/53] Improve docs around constructing DateTime objects This provides examples for most of the constructor-like methods on `TimeZone`, examples on the various `Offset` impls, and links `NaiveDateTime` to `TimeZone` so that it's more obvious how you're supposed to do things. This is related to #88, which is something that I ran into when I started using rust-chrono. --- src/datetime.rs | 13 ++++++++++++ src/naive/datetime.rs | 9 ++++++--- src/offset/fixed.rs | 25 +++++++++++++++++++++++ src/offset/local.rs | 13 ++++++++++++ src/offset/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++++++- src/offset/utc.rs | 15 ++++++++++++++ 6 files changed, 118 insertions(+), 4 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index c0a5a19..55b69fd 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -23,6 +23,10 @@ use format::{Item, Numeric, Pad, Fixed}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; /// ISO 8601 combined date and time with time zone. +/// +/// There are some constructors implemented here (the `from_*` methods), but +/// the general-purpose constructors are all via the methods on the +/// [`TimeZone`](../trait.TimeZone.html) implementations. #[derive(Clone)] pub struct DateTime { datetime: NaiveDateTime, @@ -32,6 +36,15 @@ pub struct DateTime { impl DateTime { /// Makes a new `DateTime` with given *UTC* datetime and offset. /// The local datetime should be constructed via the `TimeZone` trait. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{DateTime, TimeZone, NaiveDateTime, UTC}; + /// + /// let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), UTC); + /// assert_eq!(UTC.timestamp(61, 0), dt); + /// ~~~~ // // note: this constructor is purposedly not named to `new` to discourage the direct usage. #[inline] diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index c611f40..2beadee 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -82,9 +82,12 @@ impl NaiveDateTime { /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") /// and the number of nanoseconds since the last whole non-leap second. /// - /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](../time/index.html#leap-second-handling). - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) + /// For a non-naive version of this function see + /// [`TimeZone::timestamp`](../../offset/trait.TimeZone.html#method.timestamp). + /// + /// The nanosecond part can exceed 1,000,000,000 in order to represent the + /// [leap second](../time/index.html#leap-second-handling). (The true "UNIX + /// timestamp" cannot represent a leap second unambiguously.) /// /// Panics on the out-of-range number of seconds and/or invalid nanosecond. /// diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 7300427..c1a7d6d 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -15,6 +15,11 @@ use naive::datetime::NaiveDateTime; use super::{TimeZone, Offset, LocalResult}; /// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. +/// +/// Using the [`TimeZone`](../../../chrono/offset/trait.TimeZone.html) methods +/// on a `FixedOffset` struct is the preferred way to construct +/// `DateTime` instances. See the [`east`](#method.east) and +/// [`west`](#method.west) methods for examples. #[derive(Copy, Clone, PartialEq, Eq)] pub struct FixedOffset { local_minus_utc: i32, @@ -42,6 +47,16 @@ impl FixedOffset { /// The negative `secs` means the Western Hemisphere. /// /// Panics on the out-of-bound `secs`. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{FixedOffset, TimeZone}; + /// let hour = 3600; + /// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08) + /// .and_hms(0, 0, 0); + /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00") + /// ~~~~ pub fn east(secs: i32) -> FixedOffset { FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds") } @@ -62,6 +77,16 @@ impl FixedOffset { /// The negative `secs` means the Eastern Hemisphere. /// /// Panics on the out-of-bound `secs`. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{FixedOffset, TimeZone}; + /// let hour = 3600; + /// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08) + /// .and_hms(0, 0, 0); + /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00") + /// ~~~~ pub fn west(secs: i32) -> FixedOffset { FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds") } diff --git a/src/offset/local.rs b/src/offset/local.rs index 594b6c9..2377168 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -69,6 +69,19 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> stdtime::Timespec { } /// The local timescale. This is implemented via the standard `time` crate. +/// +/// Using the [`TimeZone`](../../../chrono/offset/trait.TimeZone.html) methods +/// on the Local struct is the preferred way to construct `DateTime` +/// instances. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::{Local, DateTime, TimeZone}; +/// +/// let dt: DateTime = Local::now(); +/// let dt: DateTime = Local.timestamp(0, 0); +/// ~~~~ #[derive(Copy, Clone)] #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub struct Local; diff --git a/src/offset/mod.rs b/src/offset/mod.rs index e0a102d..fdb2432 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -5,11 +5,12 @@ /*! * The time zone, which calculates offsets from the local time to UTC. * - * There are three operations provided by the `TimeZone` trait: + * There are four operations provided by the `TimeZone` trait: * * 1. Converting the local `NaiveDateTime` to `DateTime` * 2. Converting the UTC `NaiveDateTime` to `DateTime` * 3. Converting `DateTime` to the local `NaiveDateTime` + * 4. Constructing `DateTime` objects from various offsets * * 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types. * 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type @@ -178,6 +179,9 @@ pub trait Offset: Sized + Clone + fmt::Debug { } /// The time zone. +/// +/// The methods here are the primarily constructors for [`Date`](../date/struct.Date.html) and +/// [`DateTime`](../datetime/struct.DateTime.html) types. pub trait TimeZone: Sized + Clone { /// An associated offset type. /// This type is used to store the actual offset in date and time types. @@ -191,6 +195,14 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Panics on the out-of-range date, invalid month and/or day. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{UTC, TimeZone}; + /// + /// assert_eq!(UTC.ymd(2015, 5, 15).to_string(), "2015-05-15UTC"); + /// ~~~~ fn ymd(&self, year: i32, month: u32, day: u32) -> Date { self.ymd_opt(year, month, day).unwrap() } @@ -202,6 +214,15 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Returns `None` on the out-of-range date, invalid month and/or day. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{UTC, LocalResult, TimeZone}; + /// + /// assert_eq!(UTC.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC"); + /// assert_eq!(UTC.ymd_opt(2000, 0, 0), LocalResult::None); + /// ~~~~ fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult> { match NaiveDate::from_ymd_opt(year, month, day) { Some(d) => self.from_local_date(&d), @@ -216,6 +237,14 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Panics on the out-of-range date and/or invalid DOY. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{UTC, TimeZone}; + /// + /// assert_eq!(UTC.yo(2015, 135).to_string(), "2015-05-15UTC"); + /// ~~~~ fn yo(&self, year: i32, ordinal: u32) -> Date { self.yo_opt(year, ordinal).unwrap() } @@ -243,6 +272,14 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Panics on the out-of-range date and/or invalid week number. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{UTC, Weekday, TimeZone}; + /// + /// assert_eq!(UTC.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC"); + /// ~~~~ fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date { self.isoywd_opt(year, week, weekday).unwrap() } @@ -268,6 +305,14 @@ pub trait TimeZone: Sized + Clone { /// and the number of nanoseconds since the last whole non-leap second. /// /// Panics on the out-of-range number of seconds and/or invalid nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{UTC, TimeZone}; + /// + /// assert_eq!(UTC.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC"); + /// ~~~~ fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime { self.timestamp_opt(secs, nsecs).unwrap() } diff --git a/src/offset/utc.rs b/src/offset/utc.rs index 0b2fbe8..4071397 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -18,6 +18,21 @@ use super::{TimeZone, Offset, LocalResult}; /// The UTC time zone. This is the most efficient time zone when you don't need the local time. /// It is also used as an offset (which is also a dummy type). +/// +/// Using the [`TimeZone`](../../../chrono/offset/trait.TimeZone.html) methods +/// on the UTC struct is the preferred way to construct `DateTime` +/// instances. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::{DateTime, TimeZone, NaiveDateTime, UTC}; +/// +/// let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), UTC); +/// +/// assert_eq!(UTC.timestamp(61, 0), dt); +/// assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); +/// ~~~~ #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub struct UTC; From cebb2483d97e8a2fa126e68935e5243a638e9afa Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Mon, 14 Nov 2016 08:53:58 -0700 Subject: [PATCH 03/53] Added #[derive(Hash)] for Weekday enum This is so it can at least be used in a HashSet, if not in a BTreeSet because it does not impl Ord. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ad35171..7f2bb0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -384,7 +384,7 @@ pub mod format; /// The order of the days of week depends on the context. /// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.) /// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result. -#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub enum Weekday { /// Monday. From d2cd4c981584fa51b13129611d911b8df5d6616b Mon Sep 17 00:00:00 2001 From: Richard Petrie Date: Fri, 2 Dec 2016 12:54:32 -0600 Subject: [PATCH 04/53] Documentation had the wrong relative path Feel free to destroy this PR and fix in a branch as you see fit. --- src/offset/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/offset/mod.rs b/src/offset/mod.rs index fdb2432..75a86a8 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -331,7 +331,7 @@ pub trait TimeZone: Sized + Clone { /// Parses a string with the specified format string and /// returns a `DateTime` with the current offset. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// If the format does not include offsets, the current offset is assumed; From 538f303ed5e87fd50f43ac42f9d2f66ed26c1570 Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Tue, 6 Dec 2016 20:07:39 -0700 Subject: [PATCH 05/53] Added impl FromStr, Serialize, Deserialize for Weekday --- src/lib.rs | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7f2bb0b..d32cb68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -546,6 +546,124 @@ impl num::traits::FromPrimitive for Weekday { } } +use std::str::FromStr; + +impl FromStr for Weekday { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_string().to_lowercase().as_ref() { + "mon" | "monday" => Ok(Weekday::Mon), + "tue" | "tuesday" => Ok(Weekday::Tue), + "wed" | "wednesday" => Ok(Weekday::Wed), + "thu" | "thur" | "thursday" => Ok(Weekday::Thu), + "fri" | "friday" => Ok(Weekday::Fri), + "sat" | "saturday" => Ok(Weekday::Sat), + "sun" | "sunday" => Ok(Weekday::Sun), + other => Err(format!("unknown Weekday {}", other)), + } + } +} + +#[cfg(feature = "serde")] +mod weekday_serde { + use super::Weekday; + use serde::{ser, de}; + + impl ser::Serialize for Weekday { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer + { + serializer.serialize_str(&format!("{:?}", self)) + } + } + + struct WeekdayVisitor; + + impl de::Visitor for WeekdayVisitor { + type Value = Weekday; + + fn visit_str(&mut self, value: &str) -> Result + where E: de::Error + { + value.parse().map_err(|err| de::Error::custom(err)) + } + } + + impl de::Deserialize for Weekday { + fn deserialize(deserializer: &mut D) -> Result + where D: de::Deserializer + { + deserializer.deserialize_str(WeekdayVisitor) + } + } + + #[cfg(test)] + extern crate serde_json; + + #[test] + fn test_serde_serialize() { + use self::serde_json::to_string; + use Weekday::*; + + let cases: Vec<(Weekday, &str)> = vec![ + (Mon, "\"Mon\""), + (Tue, "\"Tue\""), + (Wed, "\"Wed\""), + (Thu, "\"Thu\""), + (Fri, "\"Fri\""), + (Sat, "\"Sat\""), + (Sun, "\"Sun\""), + ]; + + for (weekday, expected_str) in cases { + let string = to_string(&weekday).unwrap(); + assert_eq!(string, expected_str); + } + } + + #[test] + fn test_serde_deserialize() { + use self::serde_json::from_str; + use Weekday::*; + + let cases: Vec<(&str, Weekday)> = vec![ + ("\"mon\"", Mon), + ("\"MONDAY\"", Mon), + ("\"MonDay\"", Mon), + ("\"mOn\"", Mon), + ("\"tue\"", Tue), + ("\"tuesday\"", Tue), + ("\"wed\"", Wed), + ("\"wednesday\"", Wed), + ("\"thu\"", Thu), + ("\"thursday\"", Thu), + ("\"thur\"", Thu), + ("\"fri\"", Fri), + ("\"friday\"", Fri), + ("\"sat\"", Sat), + ("\"saturday\"", Sat), + ("\"sun\"", Sun), + ("\"sunday\"", Sun), + ]; + + for (str, expected_weekday) in cases { + let weekday = from_str::(str).unwrap(); + assert_eq!(weekday, expected_weekday); + } + + let errors: Vec<&str> = vec![ + "\"not a weekday\"", + "\"monDAYs\"", + "\"mond\"", + "mon", + ]; + + for str in errors { + from_str::(str).unwrap_err(); + } + } +} /// The common set of methods for date component. pub trait Datelike: Sized { From d411f050321eac8440340e905e25d4f1c46b6831 Mon Sep 17 00:00:00 2001 From: Jonas mg Date: Thu, 8 Dec 2016 18:17:26 +0000 Subject: [PATCH 06/53] Update mod.rs Derive trait `Eq` in `ParseError`. --- src/format/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index cf10831..ad5ddb9 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -184,10 +184,10 @@ macro_rules! nums { ($x:ident) => (Item::Numeric(Numeric::$x, Pad::Space)) } macro_rules! fix { ($x:ident) => (Item::Fixed(Fixed::$x)) } /// An error from the `parse` function. -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ParseError(ParseErrorKind); -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] enum ParseErrorKind { /// Given field is out of permitted range. OutOfRange, From 898e266e025fb6052fd01d8b48481aa327f27d86 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 28 Jan 2017 15:19:12 +1000 Subject: [PATCH 07/53] update for serde 0.9 --- Cargo.toml | 6 +++--- src/datetime.rs | 16 +++++++++++----- src/naive/date.rs | 12 +++++++++--- src/naive/datetime.rs | 12 +++++++++--- src/naive/time.rs | 12 +++++++++--- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 364e575..9332886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ name = "chrono" time = "0.1" num = { version = "0.1", default-features = false } rustc-serialize = { version = "0.3", optional = true } -serde = { version = "<0.9", optional = true } +serde = { version = "0.9", optional = true } [dev-dependencies] -serde_json = { version = ">=0.7.0" } -bincode = { version = "0.6", features = ["serde"], default-features = false } +serde_json = { version = ">=0.9.0" } +bincode = { version = "1.0.0-alpha1", features = ["serde"], default-features = false } diff --git a/src/datetime.rs b/src/datetime.rs index 55b69fd..15170ec 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -472,6 +472,7 @@ mod rustc_serialize { #[cfg(feature = "serde")] mod serde { + use std::fmt; use super::DateTime; use offset::TimeZone; use offset::utc::UTC; @@ -485,7 +486,7 @@ mod serde { impl ser::Serialize for DateTime where Tz::Offset: Display { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { // Debug formatting is correct RFC3339, and it allows Zulu. @@ -498,7 +499,12 @@ mod serde { impl de::Visitor for DateTimeVisitor { type Value = DateTime; - fn visit_str(&mut self, value: &str) -> Result, E> + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a formatted date and time string") + } + + fn visit_str(self, value: &str) -> Result, E> where E: de::Error { value.parse().map_err(|err| E::custom(format!("{}", err))) @@ -506,7 +512,7 @@ mod serde { } impl de::Deserialize for DateTime { - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where D: de::Deserializer { deserializer.deserialize_str(DateTimeVisitor) @@ -514,7 +520,7 @@ mod serde { } impl de::Deserialize for DateTime { - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where D: de::Deserializer { deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&UTC)) @@ -522,7 +528,7 @@ mod serde { } impl de::Deserialize for DateTime { - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where D: de::Deserializer { deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local)) diff --git a/src/naive/date.rs b/src/naive/date.rs index 85cde84..591c249 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1562,13 +1562,14 @@ mod rustc_serialize { #[cfg(feature = "serde")] mod serde { + use std::fmt; use super::NaiveDate; use serde::{ser, de}; // TODO not very optimized for space (binary formats would want something better) impl ser::Serialize for NaiveDate { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { serializer.serialize_str(&format!("{:?}", self)) @@ -1580,7 +1581,12 @@ mod serde { impl de::Visitor for NaiveDateVisitor { type Value = NaiveDate; - fn visit_str(&mut self, value: &str) -> Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a formatted date string") + } + + fn visit_str(self, value: &str) -> Result where E: de::Error { value.parse().map_err(|err| E::custom(format!("{}", err))) @@ -1588,7 +1594,7 @@ mod serde { } impl de::Deserialize for NaiveDate { - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where D: de::Deserializer { deserializer.deserialize_str(NaiveDateVisitor) diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 2beadee..e14f1fa 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1416,13 +1416,14 @@ mod rustc_serialize { #[cfg(feature = "serde")] mod serde { + use std::fmt; use super::NaiveDateTime; use serde::{ser, de}; // TODO not very optimized for space (binary formats would want something better) impl ser::Serialize for NaiveDateTime { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { serializer.serialize_str(&format!("{:?}", self)) @@ -1434,7 +1435,12 @@ mod serde { impl de::Visitor for NaiveDateTimeVisitor { type Value = NaiveDateTime; - fn visit_str(&mut self, value: &str) -> Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a formatted date and time string") + } + + fn visit_str(self, value: &str) -> Result where E: de::Error { value.parse().map_err(|err| E::custom(format!("{}", err))) @@ -1442,7 +1448,7 @@ mod serde { } impl de::Deserialize for NaiveDateTime { - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where D: de::Deserializer { deserializer.deserialize_str(NaiveDateTimeVisitor) diff --git a/src/naive/time.rs b/src/naive/time.rs index ec8f564..2765951 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1309,6 +1309,7 @@ mod rustc_serialize { #[cfg(feature = "serde")] mod serde { + use std::fmt; use super::NaiveTime; use serde::{ser, de}; @@ -1316,7 +1317,7 @@ mod serde { // TODO round-trip for general leap seconds (not just those with second = 60) impl ser::Serialize for NaiveTime { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { serializer.serialize_str(&format!("{:?}", self)) @@ -1328,7 +1329,12 @@ mod serde { impl de::Visitor for NaiveTimeVisitor { type Value = NaiveTime; - fn visit_str(&mut self, value: &str) -> Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a formatted time string") + } + + fn visit_str(self, value: &str) -> Result where E: de::Error { value.parse().map_err(|err| E::custom(format!("{}", err))) @@ -1336,7 +1342,7 @@ mod serde { } impl de::Deserialize for NaiveTime { - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where D: de::Deserializer { deserializer.deserialize_str(NaiveTimeVisitor) From 66198fbb2e145598a092ae383ab01d4eca20456e Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 3 Feb 2017 17:30:17 +1000 Subject: [PATCH 08/53] set min version to 1.13 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f683dc7..76d570c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false rust: # 1.8.0 is the earliest known version that Cargo does work for the current crates.io-index repo. # probably older versions work, but we are forced to use this as the minimum for Cargo... - - 1.8.0 + - 1.13.0 - stable - beta - nightly From 7ed6caa4119776fbf008c98da1073fa4d282390b Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 3 Feb 2017 18:00:52 +1000 Subject: [PATCH 09/53] set min version to 1.13 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f682bb4..241d049 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ environment: matrix: - - TARGET: 1.8.0-x86_64-pc-windows-gnu + - TARGET: 1.13.0-x86_64-pc-windows-gnu - TARGET: nightly-x86_64-pc-windows-msvc - TARGET: nightly-i686-pc-windows-msvc - TARGET: nightly-x86_64-pc-windows-gnu From ebdc0220454b1b339b94db547cd56195e66caa17 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 04:38:09 +0900 Subject: [PATCH 10/53] Added an initial list of categories to Cargo.toml Closes #118. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 9332886..75084b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ homepage = "https://github.com/lifthrasiir/rust-chrono" documentation = "https://lifthrasiir.github.io/rust-chrono/" repository = "https://github.com/lifthrasiir/rust-chrono" keywords = ["date", "time", "calendar"] +categories = ["date-and-time"] readme = "README.md" license = "MIT/Apache-2.0" From e9401d92661169ab662c0c3649392667a992e562 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 05:03:01 +0900 Subject: [PATCH 11/53] Fixed an warning and adjusted CI config for min supported version. --- .travis.yml | 7 +++---- src/naive/datetime.rs | 1 - src/naive/time.rs | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 76d570c..beb65cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: rust sudo: false rust: - # 1.8.0 is the earliest known version that Cargo does work for the current crates.io-index repo. - # probably older versions work, but we are forced to use this as the minimum for Cargo... + # 1.13.0 is the earliest version that Serde 0.9 tests, so we follow suit - 1.13.0 - stable - beta @@ -23,8 +22,8 @@ script: - cargo test -v - cargo build -v --features rustc-serialize - cargo test -v --features rustc-serialize --lib - - cargo build -v --features serde - - cargo test -v --features serde --lib + - cargo build -v --features 'serde bincode' + - cargo test -v --features 'serde bincode' --lib notifications: email: false irc: diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index e14f1fa..3a33ea3 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1486,7 +1486,6 @@ mod serde { #[test] fn test_serde_deserialize() { use naive::date::{self, NaiveDate}; - use self::serde_json::from_str; let from_str = |s: &str| serde_json::from_str::(s); diff --git a/src/naive/time.rs b/src/naive/time.rs index 2765951..1790c8d 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1376,8 +1376,6 @@ mod serde { #[test] fn test_serde_deserialize() { - use self::serde_json::from_str; - let from_str = |s: &str| serde_json::from_str::(s); assert_eq!(from_str(r#""00:00:00""#).ok(), From 04fd1413a8b8baf77d5ba43a710b88cd22517444 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 05:49:32 +0900 Subject: [PATCH 12/53] Chrono is now under the Chronotope org; switch to docs.rs. All CI accounts are now moved to the new organization (unfortunately Appveyor does not automatically move the build history though). Since it's a mess to redirect everything to chronotope.github.io, I've taken this as an opportunity to switch to docs.rs---this seems to be better than the manual management nowadays. Updated other files as accordingly. --- AUTHORS.txt | 7 +++++ Cargo.toml | 10 ++++--- LICENSE.txt | 3 ++- Makefile | 18 ++++++------- README.md | 78 +++++++++++++++++++++++++++-------------------------- src/lib.rs | 2 +- 6 files changed, 66 insertions(+), 52 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index fb74dbe..cd72901 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,23 +1,30 @@ Chrono is mainly written by Kang Seonghoon , and also the following people (in ascending order): +Alex Mikhalev Alexander Bulaev +Ashley Mannix Ben Eills +Brandon W Maister Colin Ray Corey Farwell Dan Danilo Bargen David Hewson David Ross +David Tolnay David Willie Eunchong Yu Huon Wilson +Jim Turner Jisoo Park Joe Wilm John Heitmann John Nagle +János Illés Ken Tossell Martin Risell Lilja +Richard Petrie Ryan Lewis Sergey V. Galtsev Steve Klabnik diff --git a/Cargo.toml b/Cargo.toml index 75084b8..328cb55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,14 +4,18 @@ version = "0.2.25" authors = ["Kang Seonghoon "] description = "Date and time library for Rust" -homepage = "https://github.com/lifthrasiir/rust-chrono" -documentation = "https://lifthrasiir.github.io/rust-chrono/" -repository = "https://github.com/lifthrasiir/rust-chrono" +homepage = "https://github.com/chronotope/chrono" +documentation = "https://docs.rs/chrono/" +repository = "https://github.com/chronotope/chrono" keywords = ["date", "time", "calendar"] categories = ["date-and-time"] readme = "README.md" license = "MIT/Apache-2.0" +[badges] +travis-ci = { repository = "chronotope/chrono" } +appveyor = { repository = "chronotope/chrono" } + [lib] name = "chrono" diff --git a/LICENSE.txt b/LICENSE.txt index d140088..924ff57 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,6 @@ Rust-chrono is dual-licensed under The MIT License [1] and -Apache 2.0 License [2]. Copyright (c) 2014, Kang Seonghoon. +Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and +contributors. Nota Bene: This is same as the Rust Project's own license. diff --git a/Makefile b/Makefile index 742543b..8930e15 100644 --- a/Makefile +++ b/Makefile @@ -24,28 +24,28 @@ README.md: src/lib.rs echo '[![Chrono on Appveyor][appveyor-image]][appveyor]' >> $@ echo '[![Chrono on crates.io][cratesio-image]][cratesio]' >> $@ echo >> $@ - echo '[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.svg?branch=master' >> $@ - echo '[travis]: https://travis-ci.org/lifthrasiir/rust-chrono' >> $@ - echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/o83jn08389si56fy/branch/master?svg=true' >> $@ - echo '[appveyor]: https://ci.appveyor.com/project/lifthrasiir/rust-chrono/branch/master' >> $@ + echo '[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master' >> $@ + echo '[travis]: https://travis-ci.org/chronotope/chrono' >> $@ + echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true' >> $@ + echo '[appveyor]: https://ci.appveyor.com/project/chronotope/chrono' >> $@ echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg' >> $@ echo '[cratesio]: https://crates.io/crates/chrono' >> $@ awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \ - sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@ + sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'"$$(cargo pkgid | cut -d: -f3)"'\/chrono\//g' >> $@ echo '***[Complete Documentation][doc]***' >> $@ echo >> $@ - echo '[doc]: https://lifthrasiir.github.io/rust-chrono/' >> $@ + echo '[doc]: https://docs.rs/chrono/'"$$(cargo pkgid | cut -d: -f3)"'/' >> $@ echo >> $@ awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \ - sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@ + sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'"$$(cargo pkgid | cut -d: -f3)"'\/chrono\//g' >> $@ .PHONY: test test: - cargo test --features 'serde rustc-serialize' + cargo test --features 'serde rustc-serialize bincode' .PHONY: doc doc: authors readme - cargo doc --features 'serde rustc-serialize' + cargo doc --features 'serde rustc-serialize bincode' .PHONY: doc-publish doc-publish: doc diff --git a/README.md b/README.md index 5ee6b0e..753bbf2 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ [![Chrono on Appveyor][appveyor-image]][appveyor] [![Chrono on crates.io][cratesio-image]][cratesio] -[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.svg?branch=master -[travis]: https://travis-ci.org/lifthrasiir/rust-chrono -[appveyor-image]: https://ci.appveyor.com/api/projects/status/o83jn08389si56fy/branch/master?svg=true -[appveyor]: https://ci.appveyor.com/project/lifthrasiir/rust-chrono/branch/master +[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master +[travis]: https://travis-ci.org/chronotope/chrono +[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true +[appveyor]: https://ci.appveyor.com/project/chronotope/chrono [cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio]: https://crates.io/crates/chrono @@ -31,7 +31,7 @@ which Chrono builts upon and should acknowledge: ***[Complete Documentation][doc]*** -[doc]: https://lifthrasiir.github.io/rust-chrono/ +[doc]: https://docs.rs/chrono/0.2.25/ ## Usage @@ -61,16 +61,18 @@ extern crate chrono; ### Duration -[**`Duration`**](https://lifthrasiir.github.io/rust-chrono/chrono/struct.Duration.html) -represents the magnitude of a time span. `Duration` used to be provided by Chrono. -It has been moved to the `time` crate as the -[`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is -still re-exported from Chrono. +[**`Duration`**](https://docs.rs/chrono/0.2.25/chrono/struct.Duration.html) represents the magnitude of a time +span. Note that this is an "accurate" duration represented as seconds and +nanoseconds and does not represent "nominal" components such as days or +months. `Duration` used to be provided by Chrono. It has been moved to the +`time` crate as the +[`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) +type, but is still re-exported from Chrono. ### Date and Time Chrono provides a -[**`DateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html) +[**`DateTime`**](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html) type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping @@ -81,15 +83,15 @@ which tracks your system clock, or is an opaque but monotonically-increasing representation of a moment in time. `DateTime` is timezone-aware and must be constructed from -the [**`TimeZone`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html) object, +the [**`TimeZone`**](https://docs.rs/chrono/0.2.25/chrono/offset/trait.TimeZone.html) object, which defines how the local date is converted to and back from the UTC date. There are three well-known `TimeZone` implementations: -* [**`UTC`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. +* [**`UTC`**](https://docs.rs/chrono/0.2.25/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. -* [**`Local`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/local/struct.Local.html) specifies the system local time zone. +* [**`Local`**](https://docs.rs/chrono/0.2.25/chrono/offset/local/struct.Local.html) specifies the system local time zone. -* [**`FixedOffset`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/fixed/struct.FixedOffset.html) specifies +* [**`FixedOffset`**](https://docs.rs/chrono/0.2.25/chrono/offset/fixed/struct.FixedOffset.html) specifies an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. This often results from the parsed textual date and time. Since it stores the most information and does not depend on the system environment, @@ -97,12 +99,12 @@ There are three well-known `TimeZone` implementations: `DateTime`s with different `TimeZone` types are distinct and do not mix, but can be converted to each other using -the [`DateTime::with_timezone`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.with_timezone) method. +the [`DateTime::with_timezone`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.with_timezone) method. You can get the current date and time in the UTC time zone -([`UTC::now()`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/utc/struct.UTC.html#method.now)) +([`UTC::now()`](https://docs.rs/chrono/0.2.25/chrono/offset/utc/struct.UTC.html#method.now)) or in the local time zone -([`Local::now()`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/local/struct.Local.html#method.now)). +([`Local::now()`](https://docs.rs/chrono/0.2.25/chrono/offset/local/struct.Local.html#method.now)). ~~~~ {.rust} use chrono::*; @@ -142,8 +144,8 @@ assert_eq!(dt, fixed_dt); ~~~~ Various properties are available to the date and time, and can be altered individually. -Most of them are defined in the traits [`Datelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Datelike.html) and -[`Timelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Timelike.html) which you should `use` before. +Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.2.25/chrono/trait.Datelike.html) and +[`Timelike`](https://docs.rs/chrono/0.2.25/chrono/trait.Timelike.html) which you should `use` before. Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: @@ -181,14 +183,14 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00 UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); ~~~~ -Formatting is done via the [`format`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.format) method, +Formatting is done via the [`format`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.format) method, which format is equivalent to the familiar `strftime` format. -(See the [`format::strftime` module documentation](https://lifthrasiir.github.io/rust-chrono/chrono/format/strftime/index.html#specifiers) +(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.2.25/chrono/format/strftime/index.html#specifiers) for full syntax.) The default `to_string` method and `{:?}` specifier also give a reasonable representation. -Chrono also provides [`to_rfc2822`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and -[`to_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods +Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and +[`to_rfc3339`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods for well-known formats. ~~~~ {.rust} @@ -214,23 +216,23 @@ Parsing can be done with three methods: ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) format specifier prints, and requires the offset to be present. -2. [`DateTime::parse_from_str`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses +2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses a date and time with offsets and returns `DateTime`. This should be used when the offset is a part of input and the caller cannot guess that. It *cannot* be used when the offset can be missing. - [`DateTime::parse_from_rfc2822`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) + [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) and - [`DateTime::parse_from_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) + [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) are similar but for well-known formats. -3. [`Offset::datetime_from_str`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is +3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.2.25/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is similar but returns `DateTime` of given offset. When the explicit offset is missing from the input, it simply uses given offset. It issues an error when the input contains an explicit offset different from the current offset. More detailed control over the parsing process is available via -[`format`](https://lifthrasiir.github.io/rust-chrono/chrono/format/index.html) module. +[`format`](https://docs.rs/chrono/0.2.25/chrono/format/index.html) module. ~~~~ {.rust} use chrono::*; @@ -264,7 +266,7 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e ### Individual date -Chrono also provides an individual date type ([**`Date`**](https://lifthrasiir.github.io/rust-chrono/chrono/date/struct.Date.html)). +Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.2.25/chrono/date/struct.Date.html)). It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. @@ -282,26 +284,26 @@ assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_ There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. -`DateTime` has [`date`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.date) method +`DateTime` has [`date`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.date) method which returns a `Date` which represents its date component. -There is also a [`time`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.time) method, +There is also a [`time`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.time) method, which simply returns a naive local time described below. ### Naive date and time Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -as [**`NaiveDate`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/date/struct.NaiveDate.html), -[**`NaiveTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/struct.NaiveTime.html) and -[**`NaiveDateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. +as [**`NaiveDate`**](https://docs.rs/chrono/0.2.25/chrono/naive/date/struct.NaiveDate.html), +[**`NaiveTime`**](https://docs.rs/chrono/0.2.25/chrono/naive/time/struct.NaiveTime.html) and +[**`NaiveDateTime`**](https://docs.rs/chrono/0.2.25/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. They have almost equivalent interfaces as their timezone-aware twins, but are not associated to time zones obviously and can be quite low-level. They are mostly useful for building blocks for higher-level types. Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -[`naive_local`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.naive_local) returns +[`naive_local`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.naive_local) returns a view to the naive local time, -and [`naive_utc`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.naive_utc) returns +and [`naive_utc`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.naive_utc) returns a view to the naive UTC time. ## Limitations @@ -313,7 +315,7 @@ Date types are limited in about +/- 262,000 years from the common epoch. Time types are limited in the nanosecond accuracy. [Leap seconds are supported in the representation but -Chrono doesn't try to make use of them](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/index.html#leap-second-handling). +Chrono doesn't try to make use of them](https://docs.rs/chrono/0.2.25/chrono/naive/time/index.html#leap-second-handling). (The main reason is that leap seconds are not really predictable.) Almost *every* operation over the possible leap seconds will ignore them. Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale diff --git a/src/lib.rs b/src/lib.rs index 7f2bb0b..5b86bb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -322,7 +322,7 @@ //! //! Advanced time zone handling is not yet supported (but is planned in 0.3). -#![doc(html_root_url = "https://lifthrasiir.github.io/rust-chrono/")] +#![doc(html_root_url = "https://docs.rs/chrono/0.2.25/")] #![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507 #![deny(missing_docs)] From de4df91421cb166c8b1a95e33a3cca32a023a497 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 06:15:57 +0900 Subject: [PATCH 13/53] Removed all remaining mentions of rust-chrono (very old name). --- CHANGELOG.md | 2 +- Makefile | 63 +++++++++++++++++------------------------- README.md | 13 +++++---- src/date.rs | 3 +- src/datetime.rs | 3 +- src/div.rs | 5 ++-- src/format/mod.rs | 3 +- src/format/parse.rs | 3 +- src/format/parsed.rs | 3 +- src/format/scan.rs | 3 +- src/format/strftime.rs | 3 +- src/lib.rs | 7 ++--- src/naive/date.rs | 3 +- src/naive/datetime.rs | 3 +- src/naive/time.rs | 3 +- src/offset/fixed.rs | 3 +- src/offset/local.rs | 3 +- src/offset/mod.rs | 3 +- src/offset/utc.rs | 3 +- 19 files changed, 52 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6768b12..93f1dd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ChangeLog for Chrono ==================== -This documents all notable changes to [Chrono](https://github.com/lifthrasiir/rust-chrono). +This documents all notable changes to [Chrono](https://github.com/chronotope/chrono). Chrono obeys the principle of [Semantic Versioning](http://semver.org/). diff --git a/Makefile b/Makefile index 8930e15..e39ffcf 100644 --- a/Makefile +++ b/Makefile @@ -17,27 +17,31 @@ readme: README.md README.md: src/lib.rs # really, really sorry for this mess. - awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< > $@ - awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< | sed 's/./=/g' >> $@ - echo >> $@ - echo '[![Chrono on Travis CI][travis-image]][travis]' >> $@ - echo '[![Chrono on Appveyor][appveyor-image]][appveyor]' >> $@ - echo '[![Chrono on crates.io][cratesio-image]][cratesio]' >> $@ - echo >> $@ - echo '[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master' >> $@ - echo '[travis]: https://travis-ci.org/chronotope/chrono' >> $@ - echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true' >> $@ - echo '[appveyor]: https://ci.appveyor.com/project/chronotope/chrono' >> $@ - echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg' >> $@ - echo '[cratesio]: https://crates.io/crates/chrono' >> $@ - awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \ - sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'"$$(cargo pkgid | cut -d: -f3)"'\/chrono\//g' >> $@ - echo '***[Complete Documentation][doc]***' >> $@ - echo >> $@ - echo '[doc]: https://docs.rs/chrono/'"$$(cargo pkgid | cut -d: -f3)"'/' >> $@ - echo >> $@ - awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \ - sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'"$$(cargo pkgid | cut -d: -f3)"'\/chrono\//g' >> $@ + ( \ + VERSION="$$(cargo pkgid | cut -d: -f3)"; \ + awk '/^\/\/! # Chrono /{print "[Chrono][docsrs]",$$4}' $<; \ + awk '/^\/\/! # Chrono /{print "[Chrono][docsrs]",$$4}' $< | sed 's/./=/g'; \ + echo; \ + echo '[![Chrono on Travis CI][travis-image]][travis]'; \ + echo '[![Chrono on Appveyor][appveyor-image]][appveyor]'; \ + echo '[![Chrono on crates.io][cratesio-image]][cratesio]'; \ + echo '[![Chrono on docs.rs][docsrs-image]][docsrs]'; \ + echo; \ + echo '[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master'; \ + echo '[travis]: https://travis-ci.org/chronotope/chrono'; \ + echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true'; \ + echo '[appveyor]: https://ci.appveyor.com/project/chronotope/chrono'; \ + echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg'; \ + echo '[cratesio]: https://crates.io/crates/chrono'; \ + echo '[docsrs-image]: https://docs.rs/chrono/badge.svg?version='$$VERSION; \ + echo '[docsrs]: https://docs.rs/chrono/'$$VERSION'/'; \ + echo; \ + awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \ + sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$$VERSION'\/chrono\//g'; \ + echo; \ + awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \ + sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$$VERSION'\/chrono\//g' \ + ) > $@ .PHONY: test test: @@ -47,20 +51,3 @@ test: doc: authors readme cargo doc --features 'serde rustc-serialize bincode' -.PHONY: doc-publish -doc-publish: doc - ( \ - PKGID="$$(cargo pkgid)"; \ - PKGNAMEVER="$${PKGID#*#}"; \ - PKGNAME="$${PKGNAMEVER%:*}"; \ - REMOTE="$$(git config --get remote.origin.url)"; \ - cd target/doc && \ - rm -rf .git && \ - git init && \ - git checkout --orphan gh-pages && \ - echo '' > index.html && \ - git add . && \ - git commit -m 'updated docs.' && \ - git push "$$REMOTE" gh-pages -f; \ - ) - diff --git a/README.md b/README.md index 753bbf2..f7e28d9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -[Chrono][doc] 0.2.25 -==================== +[Chrono][docsrs] 0.2.25 +======================= [![Chrono on Travis CI][travis-image]][travis] [![Chrono on Appveyor][appveyor-image]][appveyor] [![Chrono on crates.io][cratesio-image]][cratesio] +[![Chrono on docs.rs][docsrs-image]][docsrs] [travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master [travis]: https://travis-ci.org/chronotope/chrono @@ -11,8 +12,11 @@ [appveyor]: https://ci.appveyor.com/project/chronotope/chrono [cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio]: https://crates.io/crates/chrono +[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.2.25 +[docsrs]: https://docs.rs/chrono/0.2.25/ -Date and time handling for Rust. (also known as `rust-chrono`) + +Date and time handling for Rust. It aims to be a feature-complete superset of the [time](https://github.com/rust-lang-deprecated/time) library. In particular, @@ -29,9 +33,6 @@ which Chrono builts upon and should acknowledge: * 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][doc]*** - -[doc]: https://docs.rs/chrono/0.2.25/ ## Usage diff --git a/src/date.rs b/src/date.rs index 364ee95..3e76a51 100644 --- a/src/date.rs +++ b/src/date.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/datetime.rs b/src/datetime.rs index 15170ec..f865118 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/div.rs b/src/div.rs index 718c3a1..f21cd66 100644 --- a/src/div.rs +++ b/src/div.rs @@ -1,6 +1,5 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014, Kang Seonghoon. -// Copyright 2013-2014 The Rust Project Developers. +// This is a part of Chrono. +// Portions Copyright 2013-2014 The Rust Project Developers. // See README.md and LICENSE.txt for details. //! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/)) diff --git a/src/format/mod.rs b/src/format/mod.rs index cf10831..c37f84a 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. //! Formatting utilities for date and time. diff --git a/src/format/parse.rs b/src/format/parse.rs index ab1e3d6..5256829 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // Portions copyright (c) 2015, John Nagle. // See README.md and LICENSE.txt for details. diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 3d40191..42fe939 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. //! A collection of parsed date and time items. diff --git a/src/format/scan.rs b/src/format/scan.rs index 4b6bbb0..345b842 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/format/strftime.rs b/src/format/strftime.rs index a4acfdb..1553aad 100644 --- a/src/format/strftime.rs +++ b/src/format/strftime.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/lib.rs b/src/lib.rs index 5b86bb0..236d0a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,9 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. //! # Chrono 0.2.25 //! -//! Date and time handling for Rust. (also known as `rust-chrono`) +//! Date and time handling for Rust. //! It aims to be a feature-complete superset of //! the [time](https://github.com/rust-lang-deprecated/time) library. //! In particular, @@ -360,7 +359,7 @@ pub mod duration { //! (years, months, weeks, and days) in the ISO 8601 duration format //! because arithmetic with nominal components is not defined in ISO 8601. //! - //! This used to be a part of rust-chrono, + //! This used to be a part of Chrono, //! but has been subsequently merged into Rust's standard library. pub use stdtime::Duration; } diff --git a/src/naive/date.rs b/src/naive/date.rs index 591c249..1b3b96b 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. //! ISO 8601 calendar date without timezone. diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 3a33ea3..296f5d8 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. //! ISO 8601 date and time without timezone. diff --git a/src/naive/time.rs b/src/naive/time.rs index 1790c8d..3899ec1 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. //! ISO 8601 time without timezone. diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index c1a7d6d..c56cebe 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/offset/local.rs b/src/offset/local.rs index 2377168..2b5cda7 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/offset/mod.rs b/src/offset/mod.rs index 75a86a8..4ae3d71 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2014-2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! diff --git a/src/offset/utc.rs b/src/offset/utc.rs index 4071397..55c7079 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -1,5 +1,4 @@ -// This is a part of rust-chrono. -// Copyright (c) 2015, Kang Seonghoon. +// This is a part of Chrono. // See README.md and LICENSE.txt for details. /*! From c63ef147342f2886919e01458b07aeccca2220ef Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 08:17:01 +0900 Subject: [PATCH 14/53] `time::Duration` is no longer the sole duration type described. Due to the backward compatibility we won't be going to remove support for `time::Duration` in 0.3, and the initial 0.3.0 release won't have proper `std::time::Duration` support (haven't finalized the logics). However we will reserve proper names and signatures for the upcoming `std::time::Duration` support---the "older" duration type will be referred as "signed" in the names. - Added a `chrono::prelude` module. This does not have the (old) `Duration` type reexported, so the documentation has now correctly replaced all occurrences of `chrono::Duration`. The existing `chrono::Duration` reexport itself remains for the compatibility. - Avoided using a plain `Duration` type in the signature, to avoid any ambiguity. - Renamed `checked_{add,sub}` to `checked_{add,sub}_signed`. - Subtraction operator between two instants has been removed and replaced with `signed_duration_since`. This follows the naming chosen by `std::time::SystemTime` etc., and the version for newer `std::time::Duration` will be named to `duration_since`. --- README.md | 50 +++++-- src/date.rs | 64 ++++++--- src/datetime.rs | 50 ++++--- src/format/mod.rs | 8 +- src/format/parsed.rs | 17 ++- src/lib.rs | 84 ++++++++---- src/naive/date.rs | 148 ++++++++++++--------- src/naive/datetime.rs | 303 +++++++++++++++++++++++++++--------------- src/naive/time.rs | 277 ++++++++++++++++++++++++-------------- src/offset/fixed.rs | 8 +- src/offset/local.rs | 26 ++-- src/offset/mod.rs | 10 +- src/offset/utc.rs | 8 +- 13 files changed, 654 insertions(+), 399 deletions(-) diff --git a/README.md b/README.md index f7e28d9..ccbe54d 100644 --- a/README.md +++ b/README.md @@ -58,17 +58,34 @@ Then put this in your crate root: extern crate chrono; ``` +Avoid using `use chrono::*;` as Chrono exports several modules other than types. +If you prefer the glob imports, use the following instead: + +```rust +use chrono::prelude::*; +``` + ## Overview ### Duration -[**`Duration`**](https://docs.rs/chrono/0.2.25/chrono/struct.Duration.html) represents the magnitude of a time -span. Note that this is an "accurate" duration represented as seconds and +Chrono currently uses +the [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type +from the `time` crate to represent the magnitude of a time span. +Since this has the same name to the newer, standard type for duration, +the reference will refer this type as `OldDuration`. +Note that this is an "accurate" duration represented as seconds and nanoseconds and does not represent "nominal" components such as days or -months. `Duration` used to be provided by Chrono. It has been moved to the -`time` crate as the -[`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) -type, but is still re-exported from Chrono. +months. + +Chrono does not yet natively support +the standard [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) type, +but it will be supported in the future. +Meanwhile you can convert between two types with +[`Duration::from_std`](https://doc.rust-lang.org/time/time/struct.Duration.html#method.from_std) +and +[`Duration::to_std`](https://doc.rust-lang.org/time/time/struct.Duration.html#method.to_std) +methods. ### Date and Time @@ -108,7 +125,7 @@ or in the local time zone ([`Local::now()`](https://docs.rs/chrono/0.2.25/chrono/offset/local/struct.Local.html#method.now)). ~~~~ {.rust} -use chrono::*; +use chrono::prelude::*; 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` @@ -119,7 +136,8 @@ 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::*; +use chrono::prelude::*; +use chrono::offset::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") @@ -151,7 +169,8 @@ Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: ~~~~ {.rust} -use chrono::*; +use chrono::prelude::*; +use time::Duration; // assume this returned `2014-11-28T21:45:59.324310806+09:00`: let dt = Local::now(); @@ -176,8 +195,10 @@ 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)); +let dt1 = UTC.ymd(2014, 11, 14).and_hms(8, 9, 10); +let dt2 = UTC.ymd(2014, 11, 14).and_hms(10, 9, 8); +assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2)); +assert_eq!(dt2.signed_duration_since(dt1), 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), @@ -195,7 +216,7 @@ Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.2.25/chrono/datetim for well-known formats. ~~~~ {.rust} -use chrono::*; +use chrono::prelude::*; 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"); @@ -236,7 +257,7 @@ More detailed control over the parsing process is available via [`format`](https://docs.rs/chrono/0.2.25/chrono/format/index.html) module. ~~~~ {.rust} -use chrono::*; +use chrono::prelude::*; let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600)); @@ -272,7 +293,8 @@ It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. ~~~~ {.rust} -use chrono::*; +use chrono::prelude::*; +use chrono::offset::LocalResult; assert_eq!(UTC::today(), UTC::now().date()); assert_eq!(Local::today(), Local::now().date()); diff --git a/src/date.rs b/src/date.rs index 3e76a51..7b8e5f1 100644 --- a/src/date.rs +++ b/src/date.rs @@ -1,16 +1,14 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -/*! - * ISO 8601 calendar date with time zone. - */ +//! ISO 8601 calendar date with time zone. use std::{fmt, hash}; use std::cmp::Ordering; use std::ops::{Add, Sub}; +use oldtime::Duration as OldDuration; use {Weekday, Datelike}; -use duration::Duration; use offset::{TimeZone, Offset}; use offset::utc::UTC; use naive; @@ -209,8 +207,8 @@ impl Date { /// /// Returns `None` when it will result in overflow. #[inline] - pub fn checked_add(self, rhs: Duration) -> Option> { - let date = try_opt!(self.date.checked_add(rhs)); + pub fn checked_add_signed(self, rhs: OldDuration) -> Option> { + let date = try_opt!(self.date.checked_add_signed(rhs)); Some(Date { date: date, offset: self.offset }) } @@ -218,11 +216,39 @@ impl Date { /// /// Returns `None` when it will result in overflow. #[inline] - pub fn checked_sub(self, rhs: Duration) -> Option> { - let date = try_opt!(self.date.checked_sub(rhs)); + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option> { + let date = try_opt!(self.date.checked_sub_signed(rhs)); Some(Date { date: date, offset: self.offset }) } + /// Subtracts another `Date` from the current date. + /// Returns a `Duration` of integral numbers. + /// + /// This does not overflow or underflow at all, + /// as all possible output fits in the range of `Duration`. + #[inline] + pub fn signed_duration_since(self, rhs: Date) -> OldDuration { + self.date.signed_duration_since(rhs.date) + } + + /// Same to [`Date::checked_add_signed`](#method.checked_add_signed). + #[inline] + #[deprecated(since = "0.2.26", + note = "Renamed to `checked_add_signed`, \ + will be replaced with a version with `std::time::Duration`")] + pub fn checked_add(self, rhs: OldDuration) -> Option> { + self.checked_add_signed(rhs) + } + + /// Same to [`Date::checked_sub_signed`](#method.checked_sub_signed). + #[inline] + #[deprecated(since = "0.2.26", + note = "Renamed to `checked_sub_signed`, \ + will be replaced with a version with `std::time::Duration`")] + pub fn checked_sub(self, rhs: OldDuration) -> Option> { + self.checked_sub_signed(rhs) + } + /// Returns a view to the naive UTC date. #[inline] pub fn naive_utc(&self) -> NaiveDate { @@ -331,28 +357,32 @@ impl hash::Hash for Date { fn hash(&self, state: &mut H) { self.date.hash(state) } } -impl Add for Date { +impl Add for Date { type Output = Date; #[inline] - fn add(self, rhs: Duration) -> Date { - self.checked_add(rhs).expect("`Date + Duration` overflowed") + fn add(self, rhs: OldDuration) -> Date { + self.checked_add_signed(rhs).expect("`Date + Duration` overflowed") } } +// XXX this does not really work yet +#[deprecated(since = "0.2.26", note = "Use `signed_duration_since` method instead")] impl Sub> for Date { - type Output = Duration; + type Output = OldDuration; #[inline] - fn sub(self, rhs: Date) -> Duration { self.date - rhs.date } + fn sub(self, rhs: Date) -> OldDuration { + self.signed_duration_since(rhs) + } } -impl Sub for Date { +impl Sub for Date { type Output = Date; #[inline] - fn sub(self, rhs: Duration) -> Date { - self.checked_sub(rhs).expect("`Date - Duration` overflowed") + fn sub(self, rhs: OldDuration) -> Date { + self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed") } } @@ -423,9 +453,9 @@ mod rustc_serialize { #[cfg(test)] mod tests { use std::fmt; + use oldtime::Duration; use Datelike; - use duration::Duration; use naive::date::NaiveDate; use naive::datetime::NaiveDateTime; use offset::{TimeZone, Offset, LocalResult}; diff --git a/src/datetime.rs b/src/datetime.rs index f865118..ff0e58b 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,20 +1,18 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -/*! - * ISO 8601 date and time with time zone. - */ +//! ISO 8601 date and time with time zone. use std::{str, fmt, hash}; use std::cmp::Ordering; use std::ops::{Add, Sub}; +use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; use offset::{TimeZone, Offset, add_with_leapsecond}; use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; -use duration::Duration; use naive::time::NaiveTime; use naive::datetime::NaiveDateTime; use date::Date; @@ -130,8 +128,8 @@ impl DateTime { /// /// Returns `None` when it will result in overflow. #[inline] - pub fn checked_add(self, rhs: Duration) -> Option> { - let datetime = try_opt!(self.datetime.checked_add(rhs)); + pub fn checked_add_signed(self, rhs: OldDuration) -> Option> { + let datetime = try_opt!(self.datetime.checked_add_signed(rhs)); Some(DateTime { datetime: datetime, offset: self.offset }) } @@ -139,11 +137,18 @@ impl DateTime { /// /// Returns `None` when it will result in overflow. #[inline] - pub fn checked_sub(self, rhs: Duration) -> Option> { - let datetime = try_opt!(self.datetime.checked_sub(rhs)); + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option> { + let datetime = try_opt!(self.datetime.checked_sub_signed(rhs)); Some(DateTime { datetime: datetime, offset: self.offset }) } + /// Subtracts another `DateTime` from the current date and time. + /// This does not overflow or underflow at all. + #[inline] + pub fn signed_duration_since(self, rhs: DateTime) -> OldDuration { + self.datetime.signed_duration_since(rhs.datetime) + } + /// Returns a view to the naive UTC datetime. #[inline] pub fn naive_utc(&self) -> NaiveDateTime { @@ -327,28 +332,21 @@ impl hash::Hash for DateTime { fn hash(&self, state: &mut H) { self.datetime.hash(state) } } -impl Add for DateTime { +impl Add for DateTime { type Output = DateTime; #[inline] - fn add(self, rhs: Duration) -> DateTime { - self.checked_add(rhs).expect("`DateTime + Duration` overflowed") + fn add(self, rhs: OldDuration) -> DateTime { + self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed") } } -impl Sub> for DateTime { - type Output = Duration; - - #[inline] - fn sub(self, rhs: DateTime) -> Duration { self.datetime - rhs.datetime } -} - -impl Sub for DateTime { +impl Sub for DateTime { type Output = DateTime; #[inline] - fn sub(self, rhs: Duration) -> DateTime { - self.checked_sub(rhs).expect("`DateTime - Duration` overflowed") + fn sub(self, rhs: OldDuration) -> DateTime { + self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed") } } @@ -578,11 +576,11 @@ mod tests { use Datelike; use naive::time::NaiveTime; use naive::date::NaiveDate; - use duration::Duration; use offset::TimeZone; use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; + use oldtime::Duration; #[test] #[allow(non_snake_case)] @@ -618,10 +616,10 @@ mod tests { assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(23, 59, 59)), "2014-05-06T23:59:59+09:00"); - assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9), EDT.ymd(2014, 5, 6).and_hms(3, 8, 9)); - assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + Duration::seconds(3600 + 60 + 1), - UTC.ymd(2014, 5, 6).and_hms(8, 9, 10)); - assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) - EDT.ymd(2014, 5, 6).and_hms(10, 11, 12), + let dt = UTC.ymd(2014, 5, 6).and_hms(7, 8, 9); + assert_eq!(dt, EDT.ymd(2014, 5, 6).and_hms(3, 8, 9)); + assert_eq!(dt + Duration::seconds(3600 + 60 + 1), UTC.ymd(2014, 5, 6).and_hms(8, 9, 10)); + assert_eq!(dt.signed_duration_since(EDT.ymd(2014, 5, 6).and_hms(10, 11, 12)), Duration::seconds(-7*3600 - 3*60 - 3)); assert_eq!(*UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), UTC); diff --git a/src/format/mod.rs b/src/format/mod.rs index c37f84a..3f36251 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -5,10 +5,10 @@ use std::fmt; use std::error::Error; +use oldtime::Duration as OldDuration; use {Datelike, Timelike}; use div::{div_floor, mod_floor}; -use duration::Duration; use offset::{Offset, add_with_leapsecond}; use naive::date::NaiveDate; use naive::time::NaiveTime; @@ -252,7 +252,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>, - off: Option<&(String, Duration)>, items: I) -> fmt::Result + off: Option<&(String, OldDuration)>, items: I) -> fmt::Result where I: Iterator> { // full and abbreviated month and weekday names static SHORT_MONTHS: [&'static str; 12] = @@ -332,7 +332,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt /// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. /// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true. - fn write_local_minus_utc(w: &mut fmt::Formatter, off: Duration, + fn write_local_minus_utc(w: &mut fmt::Formatter, off: OldDuration, allow_zulu: bool, use_colon: bool) -> fmt::Result { let off = off.num_minutes(); if !allow_zulu || off != 0 { @@ -452,7 +452,7 @@ pub struct DelayedFormat { /// The time view, if any. time: Option, /// The name and local-to-UTC difference for the offset (timezone), if any. - off: Option<(String, Duration)>, + off: Option<(String, OldDuration)>, /// An iterator returning formatting items. items: I, } diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 42fe939..0cfe72f 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -5,11 +5,11 @@ //! They can be constructed incrementally while being checked for consistency. use num::traits::ToPrimitive; +use oldtime::Duration as OldDuration; use {Datelike, Timelike}; use Weekday; use div::div_rem; -use duration::Duration; use offset::{TimeZone, Offset, LocalResult}; use offset::fixed::FixedOffset; use naive::date::NaiveDate; @@ -382,7 +382,7 @@ impl Parsed { if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow? let ndays = firstweek + (week_from_sun as i32 - 1) * 7 + weekday.num_days_from_sunday() as i32; - let date = try!(newyear.checked_add(Duration::days(ndays as i64)) + let date = try!(newyear.checked_add_signed(OldDuration::days(ndays as i64)) .ok_or(OUT_OF_RANGE)); if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error @@ -407,7 +407,7 @@ impl Parsed { if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow? let ndays = firstweek + (week_from_mon as i32 - 1) * 7 + weekday.num_days_from_monday() as i32; - let date = try!(newyear.checked_add(Duration::days(ndays as i64)) + let date = try!(newyear.checked_add_signed(OldDuration::days(ndays as i64)) .ok_or(OUT_OF_RANGE)); if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error @@ -526,7 +526,7 @@ impl Parsed { // it's okay, just do not try to overwrite the existing field. 59 => {} // `datetime` is known to be off by one second. - 0 => { datetime = datetime - Duration::seconds(1); } + 0 => { datetime = datetime - OldDuration::seconds(1); } // otherwise it is impossible. _ => return Err(IMPOSSIBLE) } @@ -957,9 +957,12 @@ mod tests { ymdhmsn(2014,12,31, 4,26,40,12_345_678)); // more timestamps - let max_days_from_year_1970 = date::MAX - NaiveDate::from_ymd(1970,1,1); - let year_0_from_year_1970 = NaiveDate::from_ymd(0,1,1) - NaiveDate::from_ymd(1970,1,1); - let min_days_from_year_1970 = date::MIN - NaiveDate::from_ymd(1970,1,1); + let max_days_from_year_1970 = + date::MAX.signed_duration_since(NaiveDate::from_ymd(1970,1,1)); + let year_0_from_year_1970 = + NaiveDate::from_ymd(0,1,1).signed_duration_since(NaiveDate::from_ymd(1970,1,1)); + let min_days_from_year_1970 = + date::MIN.signed_duration_since(NaiveDate::from_ymd(1970,1,1)); assert_eq!(parse!(timestamp: min_days_from_year_1970.num_seconds()), ymdhms(date::MIN.year(),1,1, 0,0,0)); assert_eq!(parse!(timestamp: year_0_from_year_1970.num_seconds()), diff --git a/src/lib.rs b/src/lib.rs index 236d0a1..6badb30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,17 +44,34 @@ //! extern crate chrono; //! ``` //! +//! Avoid using `use chrono::*;` as Chrono exports several modules other than types. +//! If you prefer the glob imports, use the following instead: +//! +//! ```rust +//! use chrono::prelude::*; +//! ``` +//! //! ## Overview //! //! ### Duration //! -//! [**`Duration`**](./struct.Duration.html) represents the magnitude of a time -//! span. Note that this is an "accurate" duration represented as seconds and +//! Chrono currently uses +//! the [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type +//! from the `time` crate to represent the magnitude of a time span. +//! Since this has the same name to the newer, standard type for duration, +//! the reference will refer this type as `OldDuration`. +//! Note that this is an "accurate" duration represented as seconds and //! nanoseconds and does not represent "nominal" components such as days or -//! months. `Duration` used to be provided by Chrono. It has been moved to the -//! `time` crate as the -//! [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) -//! type, but is still re-exported from Chrono. +//! months. +//! +//! Chrono does not yet natively support +//! the standard [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) type, +//! but it will be supported in the future. +//! Meanwhile you can convert between two types with +//! [`Duration::from_std`](https://doc.rust-lang.org/time/time/struct.Duration.html#method.from_std) +//! and +//! [`Duration::to_std`](https://doc.rust-lang.org/time/time/struct.Duration.html#method.to_std) +//! methods. //! //! ### Date and Time //! @@ -94,7 +111,7 @@ //! ([`Local::now()`](./offset/local/struct.Local.html#method.now)). //! //! ~~~~ {.rust} -//! use chrono::*; +//! use chrono::prelude::*; //! //! 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` @@ -106,7 +123,8 @@ //! but in turn we get a rich combination of initialization methods. //! //! ~~~~ {.rust} -//! use chrono::*; +//! use chrono::prelude::*; +//! use chrono::offset::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") @@ -139,7 +157,9 @@ //! The following illustrates most supported operations to the date and time: //! //! ~~~~ {.rust} -//! use chrono::*; +//! # extern crate chrono; extern crate time; fn main() { +//! use chrono::prelude::*; +//! use time::Duration; //! //! # /* we intentionally fake the datetime... //! // assume this returned `2014-11-28T21:45:59.324310806+09:00`: @@ -167,12 +187,15 @@ //! 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)); +//! let dt1 = UTC.ymd(2014, 11, 14).and_hms(8, 9, 10); +//! let dt2 = UTC.ymd(2014, 11, 14).and_hms(10, 9, 8); +//! assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2)); +//! assert_eq!(dt2.signed_duration_since(dt1), 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`](./datetime/struct.DateTime.html#method.format) method, @@ -186,7 +209,7 @@ //! for well-known formats. //! //! ~~~~ {.rust} -//! use chrono::*; +//! use chrono::prelude::*; //! //! 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"); @@ -227,7 +250,7 @@ //! [`format`](./format/index.html) module. //! //! ~~~~ {.rust} -//! use chrono::*; +//! use chrono::prelude::*; //! //! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); //! let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600)); @@ -263,7 +286,8 @@ //! Most operations available to `DateTime` are also available to `Date` whenever appropriate. //! //! ~~~~ {.rust} -//! use chrono::*; +//! use chrono::prelude::*; +//! use chrono::offset::LocalResult; //! //! # // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;) //! assert_eq!(UTC::today(), UTC::now().date()); @@ -326,14 +350,16 @@ #![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507 #![deny(missing_docs)] -extern crate time as stdtime; +extern crate time as oldtime; extern crate num; #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; #[cfg(feature = "serde")] extern crate serde; -pub use duration::Duration; +// this reexport is to aid the transition and should not be in the prelude! +pub use oldtime::Duration; + pub use offset::{TimeZone, Offset, LocalResult}; pub use offset::utc::UTC; pub use offset::fixed::FixedOffset; @@ -345,24 +371,26 @@ pub use date::Date; pub use datetime::DateTime; pub use format::{ParseError, ParseResult}; +/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). +pub mod prelude { + pub use {Datelike, Timelike, Weekday}; + pub use offset::{TimeZone, Offset}; + pub use offset::utc::UTC; + pub use offset::fixed::FixedOffset; + pub use offset::local::Local; + pub use naive::date::NaiveDate; + pub use naive::time::NaiveTime; + pub use naive::datetime::NaiveDateTime; + pub use date::Date; + pub use datetime::DateTime; +} + // useful throughout the codebase macro_rules! try_opt { ($e:expr) => (match $e { Some(v) => v, None => return None }) } mod div; -pub mod duration { - //! ISO 8601 accurate duration. - //! - //! Note that this is an "accurate" (i.e. "exact") duration represented as - //! seconds and nanoseconds. It does not include the "nominal" components - //! (years, months, weeks, and days) in the ISO 8601 duration format - //! because arithmetic with nominal components is not defined in ISO 8601. - //! - //! This used to be a part of Chrono, - //! but has been subsequently merged into Rust's standard library. - pub use stdtime::Duration; -} pub mod offset; pub mod naive { //! Date and time types which do not concern about the timezones. diff --git a/src/naive/date.rs b/src/naive/date.rs index 1b3b96b..a860ef7 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -49,10 +49,10 @@ use std::{str, fmt, hash}; use std::ops::{Add, Sub}; use num::traits::ToPrimitive; +use oldtime::Duration as OldDuration; use {Weekday, Datelike}; use div::div_mod_floor; -use duration::Duration; use naive::time::NaiveTime; use naive::datetime::NaiveDateTime; use format::{Item, Numeric, Pad}; @@ -114,7 +114,7 @@ fn test_date_bounds() { // let's also check that the entire range do not exceed 2^44 seconds // (sometimes used for bounding `Duration` against overflow) - let maxsecs = (MAX - MIN).num_seconds(); + let maxsecs = MAX.signed_duration_since(MIN).num_seconds(); let maxsecs = maxsecs + 86401; // also take care of DateTime assert!(maxsecs < (1 << MAX_BITS), "The entire `NaiveDate` range somehow exceeds 2^{} seconds", MAX_BITS); @@ -798,17 +798,22 @@ impl NaiveDate { /// # Example /// /// ~~~~ - /// use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveDate; /// use chrono::naive::date::MAX; + /// use time::Duration; /// /// let d = NaiveDate::from_ymd(2015, 9, 5); - /// assert_eq!(d.checked_add(Duration::days(40)), Some(NaiveDate::from_ymd(2015, 10, 15))); - /// assert_eq!(d.checked_add(Duration::days(-40)), Some(NaiveDate::from_ymd(2015, 7, 27))); - /// assert_eq!(d.checked_add(Duration::days(1_000_000_000)), None); - /// assert_eq!(d.checked_add(Duration::days(-1_000_000_000)), None); - /// assert_eq!(MAX.checked_add(Duration::days(1)), None); + /// assert_eq!(d.checked_add_signed(Duration::days(40)), + /// Some(NaiveDate::from_ymd(2015, 10, 15))); + /// assert_eq!(d.checked_add_signed(Duration::days(-40)), + /// Some(NaiveDate::from_ymd(2015, 7, 27))); + /// assert_eq!(d.checked_add_signed(Duration::days(1_000_000_000)), None); + /// assert_eq!(d.checked_add_signed(Duration::days(-1_000_000_000)), None); + /// assert_eq!(MAX.checked_add_signed(Duration::days(1)), None); + /// # } /// ~~~~ - pub fn checked_add(self, rhs: Duration) -> Option { + pub fn checked_add_signed(self, rhs: OldDuration) -> Option { let year = self.year(); let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); @@ -829,17 +834,22 @@ impl NaiveDate { /// # Example /// /// ~~~~ - /// use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveDate; /// use chrono::naive::date::MIN; + /// use time::Duration; /// /// let d = NaiveDate::from_ymd(2015, 9, 5); - /// assert_eq!(d.checked_sub(Duration::days(40)), Some(NaiveDate::from_ymd(2015, 7, 27))); - /// assert_eq!(d.checked_sub(Duration::days(-40)), Some(NaiveDate::from_ymd(2015, 10, 15))); - /// assert_eq!(d.checked_sub(Duration::days(1_000_000_000)), None); - /// assert_eq!(d.checked_sub(Duration::days(-1_000_000_000)), None); - /// assert_eq!(MIN.checked_sub(Duration::days(1)), None); + /// assert_eq!(d.checked_sub_signed(Duration::days(40)), + /// Some(NaiveDate::from_ymd(2015, 7, 27))); + /// assert_eq!(d.checked_sub_signed(Duration::days(-40)), + /// Some(NaiveDate::from_ymd(2015, 10, 15))); + /// assert_eq!(d.checked_sub_signed(Duration::days(1_000_000_000)), None); + /// assert_eq!(d.checked_sub_signed(Duration::days(-1_000_000_000)), None); + /// assert_eq!(MIN.checked_sub_signed(Duration::days(1)), None); + /// # } /// ~~~~ - pub fn checked_sub(self, rhs: Duration) -> Option { + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option { let year = self.year(); let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); @@ -853,6 +863,42 @@ impl NaiveDate { Of::new(ordinal, flags)) } + /// Subtracts another `NaiveDate` from the current date. + /// Returns a `Duration` of integral numbers. + /// + /// This does not overflow or underflow at all, + /// as all possible output fits in the range of `Duration`. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveDate; + /// use time::Duration; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// let since = NaiveDate::signed_duration_since; + /// + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2014, 1, 1)), Duration::zero()); + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2013, 12, 31)), Duration::days(1)); + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2014, 1, 2)), Duration::days(-1)); + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2013, 9, 23)), Duration::days(100)); + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2013, 1, 1)), Duration::days(365)); + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2010, 1, 1)), Duration::days(365*4 + 1)); + /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(1614, 1, 1)), Duration::days(365*400 + 97)); + /// # } + /// ~~~~ + pub fn signed_duration_since(self, rhs: NaiveDate) -> OldDuration { + let year1 = self.year(); + let year2 = rhs.year(); + let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400); + let (year2_div_400, year2_mod_400) = div_mod_floor(year2, 400); + let cycle1 = internals::yo_to_cycle(year1_mod_400 as u32, self.of().ordinal()) as i64; + let cycle2 = internals::yo_to_cycle(year2_mod_400 as u32, rhs.of().ordinal()) as i64; + OldDuration::days((year1_div_400 as i64 - year2_div_400 as i64) * 146097 + + (cycle1 - cycle2)) + } + /// Formats the date with the specified formatting items. /// Otherwise it is same to the ordinary `format` method. /// @@ -1290,12 +1336,14 @@ impl hash::Hash for NaiveDate { /// rounding to the closest integral number of days towards `Duration::zero()`. /// /// Panics on underflow or overflow. -/// Use [`NaiveDate::checked_add`](#method.checked_add) to detect that. +/// Use [`NaiveDate::checked_add_signed`](#method.checked_add_signed) to detect that. /// /// # Example /// /// ~~~~ -/// use chrono::{NaiveDate, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// use chrono::NaiveDate; +/// use time::Duration; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1307,47 +1355,14 @@ impl hash::Hash for NaiveDate { /// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(364), from_ymd(2014, 12, 31)); /// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(365*4 + 1), from_ymd(2018, 1, 1)); /// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(365*400 + 97), from_ymd(2414, 1, 1)); +/// # } /// ~~~~ -impl Add for NaiveDate { +impl Add for NaiveDate { type Output = NaiveDate; #[inline] - fn add(self, rhs: Duration) -> NaiveDate { - self.checked_add(rhs).expect("`NaiveDate + Duration` overflowed") - } -} - -/// A subtraction of `NaiveDate` from `NaiveDate` yields a `Duration` of integral numbers. -/// -/// This does not overflow or underflow at all, -/// as all possible output fits in the range of `Duration`. -/// -/// # Example -/// -/// ~~~~ -/// use chrono::{NaiveDate, Duration}; -/// -/// let from_ymd = NaiveDate::from_ymd; -/// -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2014, 1, 1), Duration::zero()); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 12, 31), Duration::days(1)); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2014, 1, 2), Duration::days(-1)); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 9, 23), Duration::days(100)); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 1, 1), Duration::days(365)); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2010, 1, 1), Duration::days(365*4 + 1)); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(1614, 1, 1), Duration::days(365*400 + 97)); -/// ~~~~ -impl Sub for NaiveDate { - type Output = Duration; - - fn sub(self, rhs: NaiveDate) -> Duration { - let year1 = self.year(); - let year2 = rhs.year(); - let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400); - let (year2_div_400, year2_mod_400) = div_mod_floor(year2, 400); - let cycle1 = internals::yo_to_cycle(year1_mod_400 as u32, self.of().ordinal()) as i64; - let cycle2 = internals::yo_to_cycle(year2_mod_400 as u32, rhs.of().ordinal()) as i64; - Duration::days((year1_div_400 as i64 - year2_div_400 as i64) * 146097 + (cycle1 - cycle2)) + fn add(self, rhs: OldDuration) -> NaiveDate { + self.checked_add_signed(rhs).expect("`NaiveDate + Duration` overflowed") } } @@ -1356,12 +1371,14 @@ impl Sub for NaiveDate { /// It is same to the addition with a negated `Duration`. /// /// Panics on underflow or overflow. -/// Use [`NaiveDate::checked_sub`](#method.checked_sub) to detect that. +/// Use [`NaiveDate::checked_sub_signed`](#method.checked_sub_signed) to detect that. /// /// # Example /// /// ~~~~ -/// use chrono::{NaiveDate, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// use chrono::NaiveDate; +/// use time::Duration; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1373,13 +1390,14 @@ impl Sub for NaiveDate { /// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(364), from_ymd(2013, 1, 2)); /// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(365*4 + 1), from_ymd(2010, 1, 1)); /// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(365*400 + 97), from_ymd(1614, 1, 1)); +/// # } /// ~~~~ -impl Sub for NaiveDate { +impl Sub for NaiveDate { type Output = NaiveDate; #[inline] - fn sub(self, rhs: Duration) -> NaiveDate { - self.checked_sub(rhs).expect("`NaiveDate - Duration` overflowed") + fn sub(self, rhs: OldDuration) -> NaiveDate { + self.checked_sub_signed(rhs).expect("`NaiveDate - Duration` overflowed") } } @@ -1674,8 +1692,8 @@ mod tests { use super::{MIN, MIN_YEAR, MIN_DAYS_FROM_YEAR_0}; use super::{MAX, MAX_YEAR, MAX_DAYS_FROM_YEAR_0}; use {Datelike, Weekday}; - use duration::Duration; use std::{i32, u32}; + use oldtime::Duration; #[test] fn test_date_from_ymd() { @@ -1938,8 +1956,8 @@ mod tests { fn check((y1,m1,d1): (i32, u32, u32), rhs: Duration, ymd: Option<(i32, u32, u32)>) { let lhs = NaiveDate::from_ymd(y1, m1, d1); let sum = ymd.map(|(y,m,d)| NaiveDate::from_ymd(y, m, d)); - assert_eq!(lhs.checked_add(rhs), sum); - assert_eq!(lhs.checked_sub(-rhs), sum); + assert_eq!(lhs.checked_add_signed(rhs), sum); + assert_eq!(lhs.checked_sub_signed(-rhs), sum); } check((2014, 1, 1), Duration::zero(), Some((2014, 1, 1))); @@ -1968,8 +1986,8 @@ mod tests { fn check((y1,m1,d1): (i32, u32, u32), (y2,m2,d2): (i32, u32, u32), diff: Duration) { let lhs = NaiveDate::from_ymd(y1, m1, d1); let rhs = NaiveDate::from_ymd(y2, m2, d2); - assert_eq!(lhs - rhs, diff); - assert_eq!(rhs - lhs, -diff); + assert_eq!(lhs.signed_duration_since(rhs), diff); + assert_eq!(rhs.signed_duration_since(lhs), -diff); } check((2014, 1, 1), (2014, 1, 1), Duration::zero()); diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 296f5d8..65f7e91 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -6,10 +6,10 @@ use std::{str, fmt, hash}; use std::ops::{Add, Sub}; use num::traits::ToPrimitive; +use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; use div::div_mod_floor; -use duration::Duration; use naive::time::NaiveTime; use naive::date::NaiveDate; use format::{Item, Numeric, Pad, Fixed}; @@ -144,14 +144,16 @@ impl NaiveDateTime { } } - /// *Deprecated:* Same to [`NaiveDateTime::from_timestamp`](#method.from_timestamp). + /// Same to [`NaiveDateTime::from_timestamp`](#method.from_timestamp). #[inline] + #[deprecated(since = "0.2.0", note = "Renamed to `from_timestamp`")] pub fn from_num_seconds_from_unix_epoch(secs: i64, nsecs: u32) -> NaiveDateTime { NaiveDateTime::from_timestamp(secs, nsecs) } - /// *Deprecated:* Same to [`NaiveDateTime::from_timestamp_opt`](#method.from_timestamp_opt). + /// Same to [`NaiveDateTime::from_timestamp_opt`](#method.from_timestamp_opt). #[inline] + #[deprecated(since = "0.2.0", note = "Renamed to `from_timestamp_opt`")] pub fn from_num_seconds_from_unix_epoch_opt(secs: i64, nsecs: u32) -> Option { NaiveDateTime::from_timestamp_opt(secs, nsecs) } @@ -339,8 +341,9 @@ impl NaiveDateTime { self.time.nanosecond() } - /// *Deprecated:* Same to [`NaiveDateTime::timestamp`](#method.timestamp). + /// Same to [`NaiveDateTime::timestamp`](#method.timestamp). #[inline] + #[deprecated(since = "0.2.0", note = "Renamed to `timestamp`")] pub fn num_seconds_from_unix_epoch(&self) -> i64 { self.timestamp() } @@ -357,58 +360,77 @@ impl NaiveDateTime { /// # Example /// /// ~~~~ - /// use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveDate; + /// use time::Duration; /// /// let from_ymd = NaiveDate::from_ymd; /// /// let d = from_ymd(2016, 7, 8); /// let hms = |h, m, s| d.and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_add(Duration::zero()), Some(hms(3, 5, 7))); - /// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(1)), Some(hms(3, 5, 8))); - /// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(-1)), Some(hms(3, 5, 6))); - /// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(3600 + 60)), Some(hms(4, 6, 7))); - /// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(86400)), + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::zero()), + /// Some(hms(3, 5, 7))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(1)), + /// Some(hms(3, 5, 8))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(-1)), + /// Some(hms(3, 5, 6))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(3600 + 60)), + /// Some(hms(4, 6, 7))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(86400)), /// Some(from_ymd(2016, 7, 9).and_hms(3, 5, 7))); /// /// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); - /// assert_eq!(hmsm(3, 5, 7, 980).checked_add(Duration::milliseconds(450)), + /// assert_eq!(hmsm(3, 5, 7, 980).checked_add_signed(Duration::milliseconds(450)), /// Some(hmsm(3, 5, 8, 430))); + /// # } /// ~~~~ /// /// Overflow returns `None`. /// /// ~~~~ - /// # use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// # use chrono::NaiveDate; + /// # use time::Duration; /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_add(Duration::days(1_000_000_000)), None); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::days(1_000_000_000)), None); + /// # } /// ~~~~ /// /// Leap seconds are handled, /// but the addition assumes that it is the only leap second happened. /// /// ~~~~ - /// # use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// # use chrono::NaiveDate; + /// # use time::Duration; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); - /// assert_eq!(leap.checked_add(Duration::zero()), Some(hmsm(3, 5, 59, 1_300))); - /// assert_eq!(leap.checked_add(Duration::milliseconds(-500)), Some(hmsm(3, 5, 59, 800))); - /// assert_eq!(leap.checked_add(Duration::milliseconds(500)), Some(hmsm(3, 5, 59, 1_800))); - /// assert_eq!(leap.checked_add(Duration::milliseconds(800)), Some(hmsm(3, 6, 0, 100))); - /// assert_eq!(leap.checked_add(Duration::seconds(10)), Some(hmsm(3, 6, 9, 300))); - /// assert_eq!(leap.checked_add(Duration::seconds(-10)), Some(hmsm(3, 5, 50, 300))); - /// assert_eq!(leap.checked_add(Duration::days(1)), + /// assert_eq!(leap.checked_add_signed(Duration::zero()), + /// Some(hmsm(3, 5, 59, 1_300))); + /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(-500)), + /// Some(hmsm(3, 5, 59, 800))); + /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(500)), + /// Some(hmsm(3, 5, 59, 1_800))); + /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(800)), + /// Some(hmsm(3, 6, 0, 100))); + /// assert_eq!(leap.checked_add_signed(Duration::seconds(10)), + /// Some(hmsm(3, 6, 9, 300))); + /// assert_eq!(leap.checked_add_signed(Duration::seconds(-10)), + /// Some(hmsm(3, 5, 50, 300))); + /// assert_eq!(leap.checked_add_signed(Duration::days(1)), /// Some(from_ymd(2016, 7, 9).and_hms_milli(3, 5, 59, 300))); + /// # } /// ~~~~ - pub fn checked_add(self, rhs: Duration) -> Option { - let (time, rhs) = self.time.overflowing_add(rhs); + pub fn checked_add_signed(self, rhs: OldDuration) -> Option { + let (time, rhs) = self.time.overflowing_add_signed(rhs); - // early checking to avoid overflow in Duration::seconds + // early checking to avoid overflow in OldDuration::seconds if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) { return None; } - let date = try_opt!(self.date.checked_add(Duration::seconds(rhs))); + let date = try_opt!(self.date.checked_add_signed(OldDuration::seconds(rhs))); Some(NaiveDateTime { date: date, time: time }) } @@ -424,59 +446,142 @@ impl NaiveDateTime { /// # Example /// /// ~~~~ - /// use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveDate; + /// use time::Duration; /// /// let from_ymd = NaiveDate::from_ymd; /// /// let d = from_ymd(2016, 7, 8); /// let hms = |h, m, s| d.and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_sub(Duration::zero()), Some(hms(3, 5, 7))); - /// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(1)), Some(hms(3, 5, 6))); - /// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(-1)), Some(hms(3, 5, 8))); - /// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(3600 + 60)), Some(hms(2, 4, 7))); - /// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(86400)), + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::zero()), + /// Some(hms(3, 5, 7))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(1)), + /// Some(hms(3, 5, 6))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(-1)), + /// Some(hms(3, 5, 8))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(3600 + 60)), + /// Some(hms(2, 4, 7))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(86400)), /// Some(from_ymd(2016, 7, 7).and_hms(3, 5, 7))); /// /// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); - /// assert_eq!(hmsm(3, 5, 7, 450).checked_sub(Duration::milliseconds(670)), + /// assert_eq!(hmsm(3, 5, 7, 450).checked_sub_signed(Duration::milliseconds(670)), /// Some(hmsm(3, 5, 6, 780))); + /// # } /// ~~~~ /// /// Overflow returns `None`. /// /// ~~~~ - /// # use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// # use chrono::NaiveDate; + /// # use time::Duration; /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_sub(Duration::days(1_000_000_000)), None); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::days(1_000_000_000)), None); + /// # } /// ~~~~ /// /// Leap seconds are handled, /// but the subtraction assumes that it is the only leap second happened. /// /// ~~~~ - /// # use chrono::{NaiveDate, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// # use chrono::NaiveDate; + /// # use time::Duration; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); - /// assert_eq!(leap.checked_sub(Duration::zero()), Some(hmsm(3, 5, 59, 1_300))); - /// assert_eq!(leap.checked_sub(Duration::milliseconds(200)), Some(hmsm(3, 5, 59, 1_100))); - /// assert_eq!(leap.checked_sub(Duration::milliseconds(500)), Some(hmsm(3, 5, 59, 800))); - /// assert_eq!(leap.checked_sub(Duration::seconds(60)), Some(hmsm(3, 5, 0, 300))); - /// assert_eq!(leap.checked_sub(Duration::days(1)), + /// assert_eq!(leap.checked_sub_signed(Duration::zero()), + /// Some(hmsm(3, 5, 59, 1_300))); + /// assert_eq!(leap.checked_sub_signed(Duration::milliseconds(200)), + /// Some(hmsm(3, 5, 59, 1_100))); + /// assert_eq!(leap.checked_sub_signed(Duration::milliseconds(500)), + /// Some(hmsm(3, 5, 59, 800))); + /// assert_eq!(leap.checked_sub_signed(Duration::seconds(60)), + /// Some(hmsm(3, 5, 0, 300))); + /// assert_eq!(leap.checked_sub_signed(Duration::days(1)), /// Some(from_ymd(2016, 7, 7).and_hms_milli(3, 6, 0, 300))); + /// # } /// ~~~~ - pub fn checked_sub(self, rhs: Duration) -> Option { - let (time, rhs) = self.time.overflowing_sub(rhs); + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option { + let (time, rhs) = self.time.overflowing_sub_signed(rhs); - // early checking to avoid overflow in Duration::seconds + // early checking to avoid overflow in OldDuration::seconds if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) { return None; } - let date = try_opt!(self.date.checked_sub(Duration::seconds(rhs))); + let date = try_opt!(self.date.checked_sub_signed(OldDuration::seconds(rhs))); Some(NaiveDateTime { date: date, time: time }) } + /// Subtracts another `NaiveDateTime` from the current date and time. + /// This does not overflow or underflow at all. + /// + /// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), + /// the subtraction assumes that **there is no leap second ever**, + /// except when any of the `NaiveDateTime`s themselves represents a leap second + /// in which case the assumption becomes that + /// **there are exactly one (or two) leap second(s) ever**. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveDate; + /// use time::Duration; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// + /// let d = from_ymd(2016, 7, 8); + /// assert_eq!(d.and_hms(3, 5, 7).signed_duration_since(d.and_hms(2, 4, 6)), + /// Duration::seconds(3600 + 60 + 1)); + /// + /// // July 8 is 190th day in the year 2016 + /// let d0 = from_ymd(2016, 1, 1); + /// assert_eq!(d.and_hms_milli(0, 7, 6, 500).signed_duration_since(d0.and_hms(0, 0, 0)), + /// Duration::seconds(189 * 86400 + 7 * 60 + 6) + Duration::milliseconds(500)); + /// # } + /// ~~~~ + /// + /// Leap seconds are handled, but the subtraction assumes that + /// there were no other leap seconds happened. + /// + /// ~~~~ + /// # extern crate chrono; extern crate time; fn main() { + /// # use chrono::NaiveDate; + /// # use time::Duration; + /// # let from_ymd = NaiveDate::from_ymd; + /// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); + /// assert_eq!(leap.signed_duration_since(from_ymd(2015, 6, 30).and_hms(23, 0, 0)), + /// Duration::seconds(3600) + Duration::milliseconds(500)); + /// assert_eq!(from_ymd(2015, 7, 1).and_hms(1, 0, 0).signed_duration_since(leap), + /// Duration::seconds(3600) - Duration::milliseconds(500)); + /// # } + /// ~~~~ + pub fn signed_duration_since(self, rhs: NaiveDateTime) -> OldDuration { + self.date.signed_duration_since(rhs.date) + self.time.signed_duration_since(rhs.time) + } + + /// Same to [`NaiveDateTime::checked_add_signed`](#method.checked_add_signed). + #[inline] + #[deprecated(since = "0.2.26", + note = "Renamed to `checked_add_signed`, \ + will be replaced with a version with `std::time::Duration`")] + pub fn checked_add(self, rhs: OldDuration) -> Option { + self.checked_add_signed(rhs) + } + + /// Same to [`NaiveDateTime::checked_sub_signed`](#method.checked_sub_signed). + #[inline] + #[deprecated(since = "0.2.26", + note = "Renamed to `checked_sub_signed`, \ + will be replaced with a version with `std::time::Duration`")] + pub fn checked_sub(self, rhs: OldDuration) -> Option { + self.checked_sub_signed(rhs) + } + /// Formats the combined date and time with the specified formatting items. /// Otherwise it is same to the ordinary [`format`](#method.format) method. /// @@ -1053,12 +1158,14 @@ impl hash::Hash for NaiveDateTime { /// in which case the assumption becomes that **there is exactly a single leap second ever**. /// /// Panics on underflow or overflow. -/// Use [`NaiveDateTime::checked_add`](#method.checked_add) to detect that. +/// Use [`NaiveDateTime::checked_add_signed`](#method.checked_add_signed) to detect that. /// /// # Example /// /// ~~~~ -/// use chrono::{NaiveDate, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// use chrono::NaiveDate; +/// use time::Duration; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1075,13 +1182,16 @@ impl hash::Hash for NaiveDateTime { /// /// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); /// assert_eq!(hmsm(3, 5, 7, 980) + Duration::milliseconds(450), hmsm(3, 5, 8, 430)); +/// # } /// ~~~~ /// /// Leap seconds are handled, /// but the addition assumes that it is the only leap second happened. /// /// ~~~~ -/// # use chrono::{NaiveDate, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// # use chrono::NaiveDate; +/// # use time::Duration; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); @@ -1093,59 +1203,26 @@ impl hash::Hash for NaiveDateTime { /// assert_eq!(leap + Duration::seconds(-10), hmsm(3, 5, 50, 300)); /// assert_eq!(leap + Duration::days(1), /// from_ymd(2016, 7, 9).and_hms_milli(3, 5, 59, 300)); +/// # } /// ~~~~ -impl Add for NaiveDateTime { +impl Add for NaiveDateTime { type Output = NaiveDateTime; #[inline] - fn add(self, rhs: Duration) -> NaiveDateTime { - self.checked_add(rhs).expect("`NaiveDateTime + Duration` overflowed") + fn add(self, rhs: OldDuration) -> NaiveDateTime { + self.checked_add_signed(rhs).expect("`NaiveDateTime + Duration` overflowed") } } -/// A subtraction of `NaiveDateTime` from `NaiveDateTime` yields a `Duration`. -/// This does not overflow or underflow at all. -/// -/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), -/// the subtraction assumes that **there is no leap second ever**, -/// except when any of the `NaiveDateTime`s themselves represents a leap second -/// in which case the assumption becomes that -/// **there are exactly one (or two) leap second(s) ever**. -/// -/// # Example -/// -/// ~~~~ -/// use chrono::{NaiveDate, Duration}; -/// -/// let from_ymd = NaiveDate::from_ymd; -/// -/// let d = from_ymd(2016, 7, 8); -/// assert_eq!(d.and_hms(3, 5, 7) - d.and_hms(2, 4, 6), -/// Duration::seconds(3600 + 60 + 1)); -/// -/// // July 8 is 190th day in the year 2016 -/// let d0 = from_ymd(2016, 1, 1); -/// assert_eq!(d.and_hms_milli(0, 7, 6, 500) - d0.and_hms(0, 0, 0), -/// Duration::seconds(189 * 86400 + 7 * 60 + 6) + Duration::milliseconds(500)); -/// ~~~~ -/// -/// Leap seconds are handled, but the subtraction assumes that -/// there were no other leap seconds happened. -/// -/// ~~~~ -/// # use chrono::{NaiveDate, Duration}; -/// # let from_ymd = NaiveDate::from_ymd; -/// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); -/// assert_eq!(leap - from_ymd(2015, 6, 30).and_hms(23, 0, 0), -/// Duration::seconds(3600) + Duration::milliseconds(500)); -/// assert_eq!(from_ymd(2015, 7, 1).and_hms(1, 0, 0) - leap, -/// Duration::seconds(3600) - Duration::milliseconds(500)); -/// ~~~~ +/// Use [`NaiveDateTime::signed_duration_since`](#method.signed_duration_since) instead. +// XXX this does not really work yet +#[deprecated(since = "0.2.26", note = "Use `signed_duration_since` method instead")] impl Sub for NaiveDateTime { - type Output = Duration; + type Output = OldDuration; - fn sub(self, rhs: NaiveDateTime) -> Duration { - (self.date - rhs.date) + (self.time - rhs.time) + #[inline] + fn sub(self, rhs: NaiveDateTime) -> OldDuration { + self.signed_duration_since(rhs) } } @@ -1158,12 +1235,14 @@ impl Sub for NaiveDateTime { /// in which case the assumption becomes that **there is exactly a single leap second ever**. /// /// Panics on underflow or overflow. -/// Use [`NaiveDateTime::checked_sub`](#method.checked_sub) to detect that. +/// Use [`NaiveDateTime::checked_sub_signed`](#method.checked_sub_signed) to detect that. /// /// # Example /// /// ~~~~ -/// use chrono::{NaiveDate, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// use chrono::NaiveDate; +/// use time::Duration; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1180,13 +1259,16 @@ impl Sub for NaiveDateTime { /// /// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); /// assert_eq!(hmsm(3, 5, 7, 450) - Duration::milliseconds(670), hmsm(3, 5, 6, 780)); +/// # } /// ~~~~ /// /// Leap seconds are handled, /// but the subtraction assumes that it is the only leap second happened. /// /// ~~~~ -/// # use chrono::{NaiveDate, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// # use chrono::NaiveDate; +/// # use time::Duration; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); @@ -1196,13 +1278,14 @@ impl Sub for NaiveDateTime { /// assert_eq!(leap - Duration::seconds(60), hmsm(3, 5, 0, 300)); /// assert_eq!(leap - Duration::days(1), /// from_ymd(2016, 7, 7).and_hms_milli(3, 6, 0, 300)); +/// # } /// ~~~~ -impl Sub for NaiveDateTime { +impl Sub for NaiveDateTime { type Output = NaiveDateTime; #[inline] - fn sub(self, rhs: Duration) -> NaiveDateTime { - self.checked_sub(rhs).expect("`NaiveDateTime - Duration` overflowed") + fn sub(self, rhs: OldDuration) -> NaiveDateTime { + self.checked_sub_signed(rhs).expect("`NaiveDateTime - Duration` overflowed") } } @@ -1557,10 +1640,10 @@ mod serde { mod tests { use super::NaiveDateTime; use Datelike; - use duration::Duration; use naive::date as naive_date; use naive::date::NaiveDate; use std::i64; + use oldtime::Duration; #[test] fn test_datetime_from_timestamp() { @@ -1581,8 +1664,8 @@ mod tests { result: Option<(i32,u32,u32,u32,u32,u32)>) { let lhs = NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); let sum = result.map(|(y,m,d,h,n,s)| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s)); - assert_eq!(lhs.checked_add(rhs), sum); - assert_eq!(lhs.checked_sub(-rhs), sum); + assert_eq!(lhs.checked_add_signed(rhs), sum); + assert_eq!(lhs.checked_sub_signed(-rhs), sum); }; check((2014,5,6, 7,8,9), Duration::seconds(3600 + 60 + 1), Some((2014,5,6, 8,9,10))); @@ -1595,14 +1678,16 @@ mod tests { // overflow check // assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`. // (they are private constants, but the equivalence is tested in that module.) - let max_days_from_year_0 = naive_date::MAX - NaiveDate::from_ymd(0,1,1); + let max_days_from_year_0 = + naive_date::MAX.signed_duration_since(NaiveDate::from_ymd(0,1,1)); check((0,1,1, 0,0,0), max_days_from_year_0, Some((naive_date::MAX.year(),12,31, 0,0,0))); check((0,1,1, 0,0,0), max_days_from_year_0 + Duration::seconds(86399), Some((naive_date::MAX.year(),12,31, 23,59,59))); check((0,1,1, 0,0,0), max_days_from_year_0 + Duration::seconds(86400), None); check((0,1,1, 0,0,0), Duration::max_value(), None); - let min_days_from_year_0 = naive_date::MIN - NaiveDate::from_ymd(0,1,1); + let min_days_from_year_0 = + naive_date::MIN.signed_duration_since(NaiveDate::from_ymd(0,1,1)); check((0,1,1, 0,0,0), min_days_from_year_0, Some((naive_date::MIN.year(),1,1, 0,0,0))); check((0,1,1, 0,0,0), min_days_from_year_0 - Duration::seconds(1), None); check((0,1,1, 0,0,0), Duration::min_value(), None); @@ -1611,14 +1696,16 @@ mod tests { #[test] fn test_datetime_sub() { let ymdhms = |y,m,d,h,n,s| NaiveDate::from_ymd(y,m,d).and_hms(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), + let since = NaiveDateTime::signed_duration_since; + assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 9)), + Duration::zero()); + assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 10), ymdhms(2014, 5, 6, 7, 8, 9)), Duration::seconds(1)); - assert_eq!(ymdhms(2014, 5, 6, 7, 8, 9) - ymdhms(2014, 5, 6, 7, 8, 10), + assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), Duration::seconds(-1)); - assert_eq!(ymdhms(2014, 5, 7, 7, 8, 9) - ymdhms(2014, 5, 6, 7, 8, 10), + assert_eq!(since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), Duration::seconds(86399)); - assert_eq!(ymdhms(2001, 9, 9, 1, 46, 39) - ymdhms(1970, 1, 1, 0, 0, 0), + assert_eq!(since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)), Duration::seconds(999_999_999)); } @@ -1704,6 +1791,6 @@ mod tests { let base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); let t = -946684799990000; let time = base + Duration::microseconds(t); - assert_eq!(t, (time - base).num_microseconds().unwrap()); + assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap()); } } diff --git a/src/naive/time.rs b/src/naive/time.rs index 3899ec1..29a0a4b 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -158,10 +158,10 @@ use std::{str, fmt, hash}; use std::ops::{Add, Sub}; +use oldtime::Duration as OldDuration; use Timelike; use div::div_mod_floor; -use duration::Duration; use format::{Item, Numeric, Pad, Fixed}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; @@ -522,18 +522,21 @@ impl NaiveTime { /// # Example /// /// ~~~~ - /// use chrono::{NaiveTime, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveTime; + /// use time::Duration; /// /// let from_hms = NaiveTime::from_hms; /// - /// assert_eq!(from_hms(3, 4, 5).overflowing_add(Duration::hours(11)), + /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(11)), /// (from_hms(14, 4, 5), 0)); - /// assert_eq!(from_hms(3, 4, 5).overflowing_add(Duration::hours(23)), + /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(23)), /// (from_hms(2, 4, 5), 86400)); - /// assert_eq!(from_hms(3, 4, 5).overflowing_add(Duration::hours(-7)), + /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(-7)), /// (from_hms(20, 4, 5), -86400)); + /// # } /// ~~~~ - pub fn overflowing_add(&self, mut rhs: Duration) -> (NaiveTime, i64) { + pub fn overflowing_add_signed(&self, mut rhs: OldDuration) -> (NaiveTime, i64) { let mut secs = self.secs; let mut frac = self.frac; @@ -542,12 +545,12 @@ impl NaiveTime { // otherwise the addition immediately finishes. if frac >= 1_000_000_000 { let rfrac = 2_000_000_000 - frac; - if rhs >= Duration::nanoseconds(rfrac as i64) { - rhs = rhs - Duration::nanoseconds(rfrac as i64); + if rhs >= OldDuration::nanoseconds(rfrac as i64) { + rhs = rhs - OldDuration::nanoseconds(rfrac as i64); secs += 1; frac = 0; - } else if rhs < Duration::nanoseconds(-(frac as i64)) { - rhs = rhs + Duration::nanoseconds(frac as i64); + } else if rhs < OldDuration::nanoseconds(-(frac as i64)) { + rhs = rhs + OldDuration::nanoseconds(frac as i64); frac = 0; } else { frac = (frac as i64 + rhs.num_nanoseconds().unwrap()) as u32; @@ -559,8 +562,8 @@ impl NaiveTime { debug_assert!(frac < 1_000_000_000); let rhssecs = rhs.num_seconds(); - let rhsfrac = (rhs - Duration::seconds(rhssecs)).num_nanoseconds().unwrap(); - debug_assert!(Duration::seconds(rhssecs) + Duration::nanoseconds(rhsfrac) == rhs); + let rhsfrac = (rhs - OldDuration::seconds(rhssecs)).num_nanoseconds().unwrap(); + debug_assert!(OldDuration::seconds(rhssecs) + OldDuration::nanoseconds(rhsfrac) == rhs); let rhssecsinday = rhssecs % 86400; let mut morerhssecs = rhssecs - rhssecsinday; let rhssecs = rhssecsinday as i32; @@ -604,23 +607,130 @@ impl NaiveTime { /// # Example /// /// ~~~~ - /// use chrono::{NaiveTime, Duration}; + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveTime; + /// use time::Duration; /// /// let from_hms = NaiveTime::from_hms; /// - /// assert_eq!(from_hms(3, 4, 5).overflowing_sub(Duration::hours(2)), + /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(2)), /// (from_hms(1, 4, 5), 0)); - /// assert_eq!(from_hms(3, 4, 5).overflowing_sub(Duration::hours(17)), + /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(17)), /// (from_hms(10, 4, 5), 86400)); - /// assert_eq!(from_hms(3, 4, 5).overflowing_sub(Duration::hours(-22)), + /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(-22)), /// (from_hms(1, 4, 5), -86400)); + /// # } /// ~~~~ #[inline] - pub fn overflowing_sub(&self, rhs: Duration) -> (NaiveTime, i64) { - let (time, rhs) = self.overflowing_add(-rhs); + pub fn overflowing_sub_signed(&self, rhs: OldDuration) -> (NaiveTime, i64) { + let (time, rhs) = self.overflowing_add_signed(-rhs); (time, -rhs) // safe to negate, rhs is within +/- (2^63 / 1000) } + /// Subtracts another `NaiveTime` from the current time. + /// Returns a `Duration` within +/- 1 day. + /// This does not overflow or underflow at all. + /// + /// As a part of Chrono's [leap second handling](./index.html#leap-second-handling), + /// the subtraction assumes that **there is no leap second ever**, + /// except when any of the `NaiveTime`s themselves represents a leap second + /// in which case the assumption becomes that + /// **there are exactly one (or two) leap second(s) ever**. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; extern crate time; fn main() { + /// use chrono::NaiveTime; + /// use time::Duration; + /// + /// let from_hmsm = NaiveTime::from_hms_milli; + /// let since = NaiveTime::signed_duration_since; + /// + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 7, 900)), + /// Duration::zero()); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 7, 875)), + /// Duration::milliseconds(25)); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 6, 925)), + /// Duration::milliseconds(975)); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 0, 900)), + /// Duration::seconds(7)); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 0, 7, 900)), + /// Duration::seconds(5 * 60)); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(0, 5, 7, 900)), + /// Duration::seconds(3 * 3600)); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(4, 5, 7, 900)), + /// Duration::seconds(-3600)); + /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(2, 4, 6, 800)), + /// Duration::seconds(3600 + 60 + 1) + Duration::milliseconds(100)); + /// # } + /// ~~~~ + /// + /// Leap seconds are handled, but the subtraction assumes that + /// there were no other leap seconds happened. + /// + /// ~~~~ + /// # extern crate chrono; extern crate time; fn main() { + /// # use chrono::NaiveTime; + /// # use time::Duration; + /// # let from_hmsm = NaiveTime::from_hms_milli; + /// # let since = NaiveTime::signed_duration_since; + /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 59, 0)), + /// Duration::seconds(1)); + /// assert_eq!(since(from_hmsm(3, 0, 59, 1_500), from_hmsm(3, 0, 59, 0)), + /// Duration::milliseconds(1500)); + /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 0, 0)), + /// Duration::seconds(60)); + /// assert_eq!(since(from_hmsm(3, 0, 0, 0), from_hmsm(2, 59, 59, 1_000)), + /// Duration::seconds(1)); + /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(2, 59, 59, 1_000)), + /// Duration::seconds(61)); + /// # } + /// ~~~~ + pub fn signed_duration_since(self, rhs: NaiveTime) -> OldDuration { + // | | :leap| | | | | | | :leap| | + // | | : | | | | | | | : | | + // ----+----+-----*---+----+----+----+----+----+----+-------*-+----+---- + // | `rhs` | | `self` + // |======================================>| | + // | | `self.secs - rhs.secs` |`self.frac` + // |====>| | |======>| + // `rhs.frac`|========================================>| + // | | | `self - rhs` | | + + use std::cmp::Ordering; + + let secs = self.secs as i64 - rhs.secs as i64; + let frac = self.frac as i64 - rhs.frac as i64; + + // `secs` may contain a leap second yet to be counted + let adjust = match self.secs.cmp(&rhs.secs) { + Ordering::Greater => if rhs.frac >= 1_000_000_000 { 1 } else { 0 }, + Ordering::Equal => 0, + Ordering::Less => if self.frac >= 1_000_000_000 { -1 } else { 0 }, + }; + + OldDuration::seconds(secs + adjust) + OldDuration::nanoseconds(frac) + } + + /// Same to [`NaiveTime::overflowing_add_signed`](#method.overflowing_add_signed). + #[inline] + #[deprecated(since = "0.2.26", + note = "Renamed to `overflowing_add_signed`, \ + will be replaced with a version with `std::time::Duration`")] + pub fn overflowing_add(self, rhs: OldDuration) -> (NaiveTime, i64) { + self.overflowing_add_signed(rhs) + } + + /// Same to [`NaiveTime::overflowing_sub_signed`](#method.overflowing_sub_signed). + #[inline] + #[deprecated(since = "0.2.26", + note = "Renamed to `overflowing_sub_signed`, \ + will be replaced with a version with `std::time::Duration`")] + pub fn overflowing_sub(self, rhs: OldDuration) -> (NaiveTime, i64) { + self.overflowing_sub_signed(rhs) + } + /// Formats the time with the specified formatting items. /// Otherwise it is same to the ordinary [`format`](#method.format) method. /// @@ -924,7 +1034,9 @@ impl hash::Hash for NaiveTime { /// # Example /// /// ~~~~ -/// use chrono::{NaiveTime, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// use chrono::NaiveTime; +/// use time::Duration; /// /// let from_hmsm = NaiveTime::from_hms_milli; /// @@ -936,22 +1048,28 @@ impl hash::Hash for NaiveTime { /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::milliseconds(80), from_hmsm(3, 5, 7, 80)); /// assert_eq!(from_hmsm(3, 5, 7, 950) + Duration::milliseconds(280), from_hmsm(3, 5, 8, 230)); /// assert_eq!(from_hmsm(3, 5, 7, 950) + Duration::milliseconds(-980), from_hmsm(3, 5, 6, 970)); +/// # } /// ~~~~ /// /// The addition wraps around. /// /// ~~~~ -/// # use chrono::{NaiveTime, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// # use chrono::NaiveTime; +/// # use time::Duration; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(22*60*60), from_hmsm(1, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(-8*60*60), from_hmsm(19, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::days(800), from_hmsm(3, 5, 7, 0)); +/// # } /// ~~~~ /// /// Leap seconds are handled, but the addition assumes that it is the only leap second happened. /// /// ~~~~ -/// # use chrono::{NaiveTime, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// # use chrono::NaiveTime; +/// # use time::Duration; /// # let from_hmsm = NaiveTime::from_hms_milli; /// let leap = from_hmsm(3, 5, 59, 1_300); /// assert_eq!(leap + Duration::zero(), from_hmsm(3, 5, 59, 1_300)); @@ -961,82 +1079,26 @@ impl hash::Hash for NaiveTime { /// assert_eq!(leap + Duration::seconds(10), from_hmsm(3, 6, 9, 300)); /// assert_eq!(leap + Duration::seconds(-10), from_hmsm(3, 5, 50, 300)); /// assert_eq!(leap + Duration::days(1), from_hmsm(3, 5, 59, 300)); +/// # } /// ~~~~ -impl Add for NaiveTime { +impl Add for NaiveTime { type Output = NaiveTime; #[inline] - fn add(self, rhs: Duration) -> NaiveTime { - self.overflowing_add(rhs).0 + fn add(self, rhs: OldDuration) -> NaiveTime { + self.overflowing_add_signed(rhs).0 } } -/// A subtraction of `NaiveTime` from `NaiveTime` yields a `Duration` within +/- 1 day. -/// This does not overflow or underflow at all. -/// -/// As a part of Chrono's [leap second handling](./index.html#leap-second-handling), -/// the subtraction assumes that **there is no leap second ever**, -/// except when any of the `NaiveTime`s themselves represents a leap second -/// in which case the assumption becomes that -/// **there are exactly one (or two) leap second(s) ever**. -/// -/// # Example -/// -/// ~~~~ -/// use chrono::{NaiveTime, Duration}; -/// -/// let from_hmsm = NaiveTime::from_hms_milli; -/// -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 7, 900), Duration::zero()); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 7, 875), Duration::milliseconds(25)); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 6, 925), Duration::milliseconds(975)); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 0, 900), Duration::seconds(7)); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 0, 7, 900), Duration::seconds(5 * 60)); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(0, 5, 7, 900), Duration::seconds(3 * 3600)); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(4, 5, 7, 900), Duration::seconds(-3600)); -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(2, 4, 6, 800), -/// Duration::seconds(3600 + 60 + 1) + Duration::milliseconds(100)); -/// ~~~~ -/// -/// Leap seconds are handled, but the subtraction assumes that -/// there were no other leap seconds happened. -/// -/// ~~~~ -/// # use chrono::{NaiveTime, Duration}; -/// # let from_hmsm = NaiveTime::from_hms_milli; -/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 59, 0), Duration::seconds(1)); -/// assert_eq!(from_hmsm(3, 0, 59, 1_500) - from_hmsm(3, 0, 59, 0), Duration::milliseconds(1500)); -/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 0, 0), Duration::seconds(60)); -/// assert_eq!(from_hmsm(3, 0, 0, 0) - from_hmsm(2, 59, 59, 1_000), Duration::seconds(1)); -/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(2, 59, 59, 1_000), Duration::seconds(61)); -/// ~~~~ +/// Use [`NaiveTime::signed_duration_since`](#method.signed_duration_since) instead. +// XXX this does not really work yet +#[deprecated(since = "0.2.26", note = "Use `signed_duration_since` method instead")] impl Sub for NaiveTime { - type Output = Duration; + type Output = OldDuration; - fn sub(self, rhs: NaiveTime) -> Duration { - // | | :leap| | | | | | | :leap| | - // | | : | | | | | | | : | | - // ----+----+-----*---+----+----+----+----+----+----+-------*-+----+---- - // | `rhs` | | `self` - // |======================================>| | - // | | `self.secs - rhs.secs` |`self.frac` - // |====>| | |======>| - // `rhs.frac`|========================================>| - // | | | `self - rhs` | | - - use std::cmp::Ordering; - - let secs = self.secs as i64 - rhs.secs as i64; - let frac = self.frac as i64 - rhs.frac as i64; - - // `secs` may contain a leap second yet to be counted - let adjust = match self.secs.cmp(&rhs.secs) { - Ordering::Greater => if rhs.frac >= 1_000_000_000 { 1 } else { 0 }, - Ordering::Equal => 0, - Ordering::Less => if self.frac >= 1_000_000_000 { -1 } else { 0 }, - }; - - Duration::seconds(secs + adjust) + Duration::nanoseconds(frac) + #[inline] + fn sub(self, rhs: NaiveTime) -> OldDuration { + self.signed_duration_since(rhs) } } @@ -1052,7 +1114,9 @@ impl Sub for NaiveTime { /// # Example /// /// ~~~~ -/// use chrono::{NaiveTime, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// use chrono::NaiveTime; +/// use time::Duration; /// /// let from_hmsm = NaiveTime::from_hms_milli; /// @@ -1062,21 +1126,27 @@ impl Sub for NaiveTime { /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(2*60*60 + 6*60), from_hmsm(0, 59, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::milliseconds(80), from_hmsm(3, 5, 6, 920)); /// assert_eq!(from_hmsm(3, 5, 7, 950) - Duration::milliseconds(280), from_hmsm(3, 5, 7, 670)); +/// # } /// ~~~~ /// /// The subtraction wraps around. /// /// ~~~~ -/// # use chrono::{NaiveTime, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// # use chrono::NaiveTime; +/// # use time::Duration; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(8*60*60), from_hmsm(19, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::days(800), from_hmsm(3, 5, 7, 0)); +/// # } /// ~~~~ /// /// Leap seconds are handled, but the subtraction assumes that it is the only leap second happened. /// /// ~~~~ -/// # use chrono::{NaiveTime, Duration}; +/// # extern crate chrono; extern crate time; fn main() { +/// # use chrono::NaiveTime; +/// # use time::Duration; /// # let from_hmsm = NaiveTime::from_hms_milli; /// let leap = from_hmsm(3, 5, 59, 1_300); /// assert_eq!(leap - Duration::zero(), from_hmsm(3, 5, 59, 1_300)); @@ -1084,13 +1154,14 @@ impl Sub for NaiveTime { /// assert_eq!(leap - Duration::milliseconds(500), from_hmsm(3, 5, 59, 800)); /// assert_eq!(leap - Duration::seconds(60), from_hmsm(3, 5, 0, 300)); /// assert_eq!(leap - Duration::days(1), from_hmsm(3, 6, 0, 300)); +/// # } /// ~~~~ -impl Sub for NaiveTime { +impl Sub for NaiveTime { type Output = NaiveTime; #[inline] - fn sub(self, rhs: Duration) -> NaiveTime { - self.overflowing_sub(rhs).0 + fn sub(self, rhs: OldDuration) -> NaiveTime { + self.overflowing_sub_signed(rhs).0 } } @@ -1434,8 +1505,8 @@ mod serde { mod tests { use super::NaiveTime; use Timelike; - use duration::Duration; use std::u32; + use oldtime::Duration; #[test] fn test_time_from_hms_milli() { @@ -1526,17 +1597,17 @@ mod tests { fn test_time_overflowing_add() { let hmsm = NaiveTime::from_hms_milli; - assert_eq!(hmsm(3, 4, 5, 678).overflowing_add(Duration::hours(11)), + assert_eq!(hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(11)), (hmsm(14, 4, 5, 678), 0)); - assert_eq!(hmsm(3, 4, 5, 678).overflowing_add(Duration::hours(23)), + assert_eq!(hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(23)), (hmsm(2, 4, 5, 678), 86400)); - assert_eq!(hmsm(3, 4, 5, 678).overflowing_add(Duration::hours(-7)), + assert_eq!(hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(-7)), (hmsm(20, 4, 5, 678), -86400)); - // overflowing_add with leap seconds may be counter-intuitive - assert_eq!(hmsm(3, 4, 5, 1_678).overflowing_add(Duration::days(1)), + // overflowing_add_signed with leap seconds may be counter-intuitive + assert_eq!(hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(1)), (hmsm(3, 4, 5, 678), 86400)); - assert_eq!(hmsm(3, 4, 5, 1_678).overflowing_add(Duration::days(-1)), + assert_eq!(hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(-1)), (hmsm(3, 4, 6, 678), -86400)); } @@ -1545,8 +1616,8 @@ mod tests { macro_rules! check { ($lhs:expr, $rhs:expr, $diff:expr) => ({ // `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration` - assert_eq!($lhs - $rhs, $diff); - assert_eq!($rhs - $lhs, -$diff); + assert_eq!($lhs.signed_duration_since($rhs), $diff); + assert_eq!($rhs.signed_duration_since($lhs), -$diff); }) } diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index c56cebe..8c545d2 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -1,14 +1,12 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -/*! - * The time zone which has a fixed offset from UTC. - */ +//! The time zone which has a fixed offset from UTC. use std::fmt; +use oldtime::Duration as OldDuration; use div::div_mod_floor; -use duration::Duration; use naive::date::NaiveDate; use naive::datetime::NaiveDateTime; use super::{TimeZone, Offset, LocalResult}; @@ -120,7 +118,7 @@ impl TimeZone for FixedOffset { } impl Offset for FixedOffset { - fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) } + fn local_minus_utc(&self) -> OldDuration { OldDuration::seconds(self.local_minus_utc as i64) } } impl fmt::Debug for FixedOffset { diff --git a/src/offset/local.rs b/src/offset/local.rs index 2b5cda7..d37c15f 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -1,14 +1,12 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -/*! - * The local (system) time zone. - */ +//! The local (system) time zone. -use stdtime; +use oldtime; +use oldtime::Duration as OldDuration; use {Datelike, Timelike}; -use duration::Duration; use naive::date::NaiveDate; use naive::time::NaiveTime; use naive::datetime::NaiveDateTime; @@ -19,20 +17,20 @@ use super::fixed::FixedOffset; /// Converts a `time::Tm` struct into the timezone-aware `DateTime`. /// This assumes that `time` is working correctly, i.e. any error is fatal. -fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime { +fn tm_to_datetime(mut tm: oldtime::Tm) -> DateTime { if tm.tm_sec >= 60 { tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000; tm.tm_sec = 59; } #[cfg(not(windows))] - fn tm_to_naive_date(tm: &stdtime::Tm) -> NaiveDate { + fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate { // from_yo is more efficient than from_ymd (since it's the internal representation). NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1) } #[cfg(windows)] - fn tm_to_naive_date(tm: &stdtime::Tm) -> NaiveDate { + fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate { // ...but tm_yday is broken in Windows (issue #85) NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32) } @@ -41,17 +39,17 @@ fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime { let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32, tm.tm_sec as u32, tm.tm_nsec as u32); let offset = FixedOffset::east(tm.tm_utcoff); - DateTime::from_utc(date.and_time(time) + Duration::seconds(-tm.tm_utcoff as i64), offset) + DateTime::from_utc(date.and_time(time) + OldDuration::seconds(-tm.tm_utcoff as i64), offset) } /// Converts a local `NaiveDateTime` to the `time::Timespec`. -fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> stdtime::Timespec { +fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { // well, this exploits an undocumented `Tm::to_timespec` behavior // to get the exact function we want (either `timegm` or `mktime`). // the number 1 is arbitrary but should be non-zero to trigger `mktime`. let tm_utcoff = if local {1} else {0}; - let tm = stdtime::Tm { + let tm = oldtime::Tm { tm_sec: d.second() as i32, tm_min: d.minute() as i32, tm_hour: d.hour() as i32, @@ -93,7 +91,7 @@ impl Local { /// Returns a `DateTime` which corresponds to the current date. pub fn now() -> DateTime { - tm_to_datetime(stdtime::now()) + tm_to_datetime(oldtime::now()) } } @@ -127,7 +125,7 @@ impl TimeZone for Local { } fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult> { let timespec = datetime_to_timespec(local, true); - LocalResult::Single(tm_to_datetime(stdtime::at(timespec))) + LocalResult::Single(tm_to_datetime(oldtime::at(timespec))) } fn from_utc_date(&self, utc: &NaiveDate) -> Date { @@ -136,7 +134,7 @@ impl TimeZone for Local { } fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime { let timespec = datetime_to_timespec(utc, false); - tm_to_datetime(stdtime::at(timespec)) + tm_to_datetime(oldtime::at(timespec)) } } diff --git a/src/offset/mod.rs b/src/offset/mod.rs index 4ae3d71..cf27da9 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -22,10 +22,10 @@ use std::fmt; use std::ops::Add; +use oldtime::Duration as OldDuration; use Weekday; use Timelike; -use duration::Duration; use naive::date::NaiveDate; use naive::time::NaiveTime; use naive::datetime::NaiveDateTime; @@ -36,8 +36,10 @@ use format::{parse, Parsed, ParseResult, StrftimeItems}; /// Same to `*lhs + *rhs`, but keeps the leap second information. /// `rhs` should *not* have a fractional second. // TODO this should be replaced by the addition with FixedOffset in 0.3! -pub fn add_with_leapsecond>(lhs: &T, rhs: &Duration) -> T { - debug_assert!(*rhs == Duration::seconds(rhs.num_seconds())); +pub fn add_with_leapsecond(lhs: &T, rhs: &OldDuration) -> T + where T: Timelike + Add +{ + debug_assert!(*rhs == OldDuration::seconds(rhs.num_seconds())); // extract and temporarily remove the fractional part and later recover it let nanos = lhs.nanosecond(); @@ -174,7 +176,7 @@ impl LocalResult { /// The offset from the local time to UTC. pub trait Offset: Sized + Clone + fmt::Debug { /// Returns the offset from UTC to the local time stored. - fn local_minus_utc(&self) -> Duration; + fn local_minus_utc(&self) -> OldDuration; } /// The time zone. diff --git a/src/offset/utc.rs b/src/offset/utc.rs index 55c7079..b1a97cc 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -6,9 +6,9 @@ */ use std::fmt; -use stdtime; +use oldtime; +use oldtime::Duration as OldDuration; -use duration::Duration; use naive::date::NaiveDate; use naive::datetime::NaiveDateTime; use date::Date; @@ -42,7 +42,7 @@ impl UTC { /// Returns a `DateTime` which corresponds to the current date. pub fn now() -> DateTime { - let spec = stdtime::get_time(); + let spec = oldtime::get_time(); let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32); DateTime::from_utc(naive, UTC) } @@ -65,7 +65,7 @@ impl TimeZone for UTC { } impl Offset for UTC { - fn local_minus_utc(&self) -> Duration { Duration::zero() } + fn local_minus_utc(&self) -> OldDuration { OldDuration::zero() } } impl fmt::Debug for UTC { From c118a3985fd47dd86dea150d3cee593f1b36d0eb Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 03:08:01 +0900 Subject: [PATCH 15/53] Serialization cleanup for 0.3. - Rustc-serialize now uses the same serialization format as Serde. This also means that the older format (naturally derived from the internals) is no longer supported. - Serialization support only existed for rustc-serialize has been (temporarily) removed. This affects `Date` and all individual time zone types. This does *not* affect `DateTime` as it has individual support per `Tz`. Please note that this is considered a temporary solution to avoid stabilizing diverging implementations. Their implementations will likely be reintroduced later. --- src/date.rs | 52 --------- src/datetime.rs | 143 +++++++++++++++---------- src/naive/date.rs | 178 ++++++++++--------------------- src/naive/datetime.rs | 242 ++++++++++++++++-------------------------- src/naive/time.rs | 217 +++++++++++++------------------------ src/offset/fixed.rs | 86 --------------- src/offset/local.rs | 1 - src/offset/utc.rs | 1 - 8 files changed, 311 insertions(+), 609 deletions(-) diff --git a/src/date.rs b/src/date.rs index 7b8e5f1..16374c4 100644 --- a/src/date.rs +++ b/src/date.rs @@ -398,58 +398,6 @@ impl fmt::Display for Date where Tz::Offset: fmt::Display { } } -#[cfg(feature = "rustc-serialize")] -mod rustc_serialize { - use super::Date; - use offset::TimeZone; - use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - - // TODO the current serialization format is NEVER intentionally defined. - // in the future it is likely to be redefined to more sane and reasonable format. - - impl Encodable for Date where Tz::Offset: Encodable { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Date", 2, |s| { - try!(s.emit_struct_field("date", 0, |s| self.date.encode(s))); - try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s))); - Ok(()) - }) - } - } - - impl Decodable for Date where Tz::Offset: Decodable { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_struct("Date", 2, |d| { - let date = try!(d.read_struct_field("date", 0, Decodable::decode)); - let offset = try!(d.read_struct_field("offset", 1, Decodable::decode)); - Ok(Date::from_utc(date, offset)) - }) - } - } - - #[test] - fn test_encodable() { - use offset::utc::UTC; - use rustc_serialize::json::encode; - - assert_eq!(encode(&UTC.ymd(2014, 7, 24)).ok(), - Some(r#"{"date":{"ymdf":16501977},"offset":{}}"#.into())); - } - - #[test] - fn test_decodable() { - use offset::utc::UTC; - use rustc_serialize::json; - - let decode = |s: &str| json::decode::>(s); - - assert_eq!(decode(r#"{"date":{"ymdf":16501977},"offset":{}}"#).ok(), - Some(UTC.ymd(2014, 7, 24))); - - assert!(decode(r#"{"date":{"ymdf":0},"offset":{}}"#).is_err()); - } -} - #[cfg(test)] mod tests { use std::fmt; diff --git a/src/datetime.rs b/src/datetime.rs index ff0e58b..62bb55c 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -405,65 +405,108 @@ impl str::FromStr for DateTime { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string_utc: FUTC, to_string_fixed: FFixed) + where FUTC: Fn(&DateTime) -> Result, + FFixed: Fn(&DateTime) -> Result, + E: ::std::fmt::Debug +{ + assert_eq!(to_string_utc(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06Z""#.into())); + + assert_eq!(to_string_fixed(&FixedOffset::east(3660).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06+01:01""#.into())); + assert_eq!(to_string_fixed(&FixedOffset::east(3650).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06+01:00:50""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(utc_from_str: FUTC, + fixed_from_str: FFixed, + local_from_str: FLocal) + where FUTC: Fn(&str) -> Result, E>, + FFixed: Fn(&str) -> Result, E>, + FLocal: Fn(&str) -> Result, E>, + E: ::std::fmt::Debug +{ + // should check against the offset as well (the normal DateTime comparison will ignore them) + fn norm(dt: &Option>) -> Option<(&DateTime, &Tz::Offset)> { + dt.as_ref().map(|dt| (dt, dt.offset())) + } + + assert_eq!(norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()), + norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + assert_eq!(norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), + norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + + assert_eq!(norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), + norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6)))); + assert_eq!(norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), + norm(&Some(FixedOffset::east(60*60 + 23*60).ymd(2014, 7, 24).and_hms(13, 57, 6)))); + + // we don't know the exact local offset but we can check that + // the conversion didn't change the instant itself + assert_eq!(local_from_str(r#""2014-07-24T12:34:06Z""#).unwrap(), + UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#).unwrap(), + UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + + assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); + assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::DateTime; use offset::TimeZone; + use offset::utc::UTC; + use offset::local::Local; + use offset::fixed::FixedOffset; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // in the future it is likely to be redefined to more sane and reasonable format. - - impl Encodable for DateTime where Tz::Offset: Encodable { + impl Encodable for DateTime { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("DateTime", 2, |s| { - try!(s.emit_struct_field("datetime", 0, |s| self.datetime.encode(s))); - try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } - impl Decodable for DateTime where Tz::Offset: Decodable { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_struct("DateTime", 2, |d| { - let datetime = try!(d.read_struct_field("datetime", 0, Decodable::decode)); - let offset = try!(d.read_struct_field("offset", 1, Decodable::decode)); - Ok(DateTime::from_utc(datetime, offset)) - }) + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + match d.read_str()?.parse::>() { + Ok(dt) => Ok(dt), + Err(_) => Err(d.error("invalid date and time")), + } } } + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + match d.read_str()?.parse::>() { + Ok(dt) => Ok(dt.with_timezone(&UTC)), + Err(_) => Err(d.error("invalid date and time")), + } + } + } + + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + match d.read_str()?.parse::>() { + Ok(dt) => Ok(dt.with_timezone(&Local)), + Err(_) => Err(d.error("invalid date and time")), + } + } + } + + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use offset::utc::UTC; - use rustc_serialize::json::encode; - - assert_eq!( - encode(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(concat!(r#"{"datetime":{"date":{"ymdf":16501977},"#, - r#""time":{"secs":45246,"frac":0}},"#, - r#""offset":{}}"#).into())); + super::test_encodable_json(json::encode, json::encode); } #[test] fn test_decodable() { - use offset::utc::UTC; - use rustc_serialize::json; - - let decode = |s: &str| json::decode::>(s); - - assert_eq!( - decode(r#"{"datetime":{"date":{"ymdf":16501977}, - "time":{"secs":45246,"frac":0}}, - "offset":{}}"#).ok(), - Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6))); - - assert_eq!( - decode(r#"{"datetime":{"date":{"ymdf":0}, - "time":{"secs":0,"frac":0}}, - "offset":{}}"#).ok(), - None); + super::test_decodable_json(json::decode, json::decode, json::decode); } } @@ -475,14 +518,11 @@ mod serde { use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; - use std::fmt::Display; use serde::{ser, de}; // TODO not very optimized for space (binary formats would want something better) - impl ser::Serialize for DateTime - where Tz::Offset: Display - { + impl ser::Serialize for DateTime { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { @@ -537,22 +577,13 @@ mod serde { #[test] fn test_serde_serialize() { - use self::serde_json::to_string; - - assert_eq!(to_string(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06Z""#.into())); + super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - use self::serde_json; - - let from_str = |s: &str| serde_json::from_str::>(s); - - assert_eq!(from_str(r#""2014-07-24T12:34:06Z""#).ok(), - Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6))); - - assert!(from_str(r#""2014-07-32T12:34:06Z""#).is_err()); + super::test_decodable_json(self::serde_json::from_str, self::serde_json::from_str, + self::serde_json::from_str); } #[test] diff --git a/src/naive/date.rs b/src/naive/date.rs index a860ef7..f8fff8f 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -136,26 +136,6 @@ impl NaiveDate { NaiveDate::from_of(year, mdf.to_of()) } - /// Makes a new `NaiveDate` from the serialized representation. - /// Used for serialization formats. - #[cfg(feature = "rustc-serialize")] - fn from_serialized(ymdf: i32) -> Option { - // check if the year flag is correct - if (ymdf & 0b1111) as u8 != YearFlags::from_year(ymdf >> 13).0 { return None; } - - // check if the ordinal is in the range - let date = NaiveDate { ymdf: ymdf }; - if !date.of().valid() { return None; } - - Some(date) - } - - /// Returns a serialized representation of this `NaiveDate`. - #[cfg(feature = "rustc-serialize")] - fn to_serialized(&self) -> i32 { - self.ymdf - } - /// Makes a new `NaiveDate` from the [calendar date](./index.html#calendar-date) /// (year, month and day). /// @@ -1497,83 +1477,84 @@ impl str::FromStr for NaiveDate { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string: F) + where F: Fn(&NaiveDate) -> Result, E: ::std::fmt::Debug +{ + assert_eq!(to_string(&NaiveDate::from_ymd(2014, 7, 24)).ok(), + Some(r#""2014-07-24""#.into())); + assert_eq!(to_string(&NaiveDate::from_ymd(0, 1, 1)).ok(), + Some(r#""0000-01-01""#.into())); + assert_eq!(to_string(&NaiveDate::from_ymd(-1, 12, 31)).ok(), + Some(r#""-0001-12-31""#.into())); + assert_eq!(to_string(&MIN).ok(), + Some(r#""-262144-01-01""#.into())); + assert_eq!(to_string(&MAX).ok(), + Some(r#""+262143-12-31""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + use std::{i32, i64}; + + assert_eq!(from_str(r#""2016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); + assert_eq!(from_str(r#""2016-7-8""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); + assert_eq!(from_str(r#""+002016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); + assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); + assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); + assert_eq!(from_str(r#""-0001-12-31""#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); + assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(MIN)); + assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(MAX)); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""20001231""#).is_err()); + assert!(from_str(r#""2000-00-00""#).is_err()); + assert!(from_str(r#""2000-02-30""#).is_err()); + assert!(from_str(r#""2001-02-29""#).is_err()); + assert!(from_str(r#""2002-002-28""#).is_err()); + assert!(from_str(r#""yyyy-mm-dd""#).is_err()); + assert!(from_str(r#"0"#).is_err()); + assert!(from_str(r#"20.01"#).is_err()); + assert!(from_str(&i32::MIN.to_string()).is_err()); + assert!(from_str(&i32::MAX.to_string()).is_err()); + assert!(from_str(&i64::MIN.to_string()).is_err()); + assert!(from_str(&i64::MAX.to_string()).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"ymdf":20}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::NaiveDate; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // this basically follows the automatically generated implementation for those traits, - // plus manual verification steps for avoiding security problem. - // in the future it is likely to be redefined to more sane and reasonable format. - impl Encodable for NaiveDate { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let ymdf = self.to_serialized(); - s.emit_struct("NaiveDate", 1, |s| { - try!(s.emit_struct_field("ymdf", 0, |s| ymdf.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } impl Decodable for NaiveDate { fn decode(d: &mut D) -> Result { - d.read_struct("NaiveDate", 1, |d| { - let ymdf = try!(d.read_struct_field("ymdf", 0, Decodable::decode)); - NaiveDate::from_serialized(ymdf).ok_or_else(|| d.error("invalid date")) - }) + d.read_str()?.parse().map_err(|_| d.error("invalid date")) } } + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use rustc_serialize::json::encode; - - assert_eq!(encode(&NaiveDate::from_ymd(2016, 7, 8)).ok(), - Some(r#"{"ymdf":16518115}"#.into())); - assert_eq!(encode(&NaiveDate::from_ymd(0, 1, 1)).ok(), - Some(r#"{"ymdf":20}"#.into())); - assert_eq!(encode(&NaiveDate::from_ymd(-1, 12, 31)).ok(), - Some(r#"{"ymdf":-2341}"#.into())); - assert_eq!(encode(&super::MIN).ok(), - Some(r#"{"ymdf":-2147483625}"#.into())); - assert_eq!(encode(&super::MAX).ok(), - Some(r#"{"ymdf":2147481311}"#.into())); + super::test_encodable_json(json::encode); } #[test] fn test_decodable() { - use rustc_serialize::json; - use std::{i32, i64}; - - let decode = |s: &str| json::decode::(s); - - assert_eq!(decode(r#"{"ymdf":16518115}"#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(decode(r#"{"ymdf":20}"#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(decode(r#"{"ymdf":-2341}"#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); - assert_eq!(decode(r#"{"ymdf":-2147483625}"#).ok(), Some(super::MIN)); - assert_eq!(decode(r#"{"ymdf":2147481311}"#).ok(), Some(super::MAX)); - - // some extreme values and zero are always invalid - assert!(decode(r#"{"ymdf":0}"#).is_err()); - assert!(decode(r#"{"ymdf":1}"#).is_err()); - assert!(decode(r#"{"ymdf":-1}"#).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i32::MIN)).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i32::MAX)).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i64::MIN)).is_err()); - assert!(decode(&format!(r#"{{"ymdf":{}}}"#, i64::MAX)).is_err()); - - // bad formats - assert!(decode(r#"{"ymdf":20.01}"#).is_err()); - assert!(decode(r#"{"ymdf":"string"}"#).is_err()); - assert!(decode(r#"{"ymdf":null}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"{"date":20}"#).is_err()); - assert!(decode(r#"20"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#""2016-07-08""#).is_err()); // :( - assert!(decode(r#"null"#).is_err()); + super::test_decodable_json(json::decode); } } @@ -1623,53 +1604,12 @@ mod serde { #[test] fn test_serde_serialize() { - use self::serde_json::to_string; - - assert_eq!(to_string(&NaiveDate::from_ymd(2014, 7, 24)).ok(), - Some(r#""2014-07-24""#.into())); - assert_eq!(to_string(&NaiveDate::from_ymd(0, 1, 1)).ok(), - Some(r#""0000-01-01""#.into())); - assert_eq!(to_string(&NaiveDate::from_ymd(-1, 12, 31)).ok(), - Some(r#""-0001-12-31""#.into())); - assert_eq!(to_string(&super::MIN).ok(), - Some(r#""-262144-01-01""#.into())); - assert_eq!(to_string(&super::MAX).ok(), - Some(r#""+262143-12-31""#.into())); + super::test_encodable_json(self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - use self::serde_json; - use std::{i32, i64}; - - let from_str = |s: &str| serde_json::from_str::(s); - - assert_eq!(from_str(r#""2016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""2016-7-8""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""+002016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_str(r#""-0001-12-31""#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); - assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(super::MIN)); - assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(super::MAX)); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""20001231""#).is_err()); - assert!(from_str(r#""2000-00-00""#).is_err()); - assert!(from_str(r#""2000-02-30""#).is_err()); - assert!(from_str(r#""2001-02-29""#).is_err()); - assert!(from_str(r#""2002-002-28""#).is_err()); - assert!(from_str(r#""yyyy-mm-dd""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"20.01"#).is_err()); - assert!(from_str(&i32::MIN.to_string()).is_err()); - assert!(from_str(&i32::MAX.to_string()).is_err()); - assert!(from_str(&i64::MIN.to_string()).is_err()); - assert!(from_str(&i64::MAX.to_string()).is_err()); - assert!(from_str(r#"{}"#).is_err()); - assert!(from_str(r#"{"ymdf":20}"#).is_err()); // :( - assert!(from_str(r#"null"#).is_err()); + super::test_decodable_json(self::serde_json::from_str); } #[test] diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 65f7e91..b60a565 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1394,105 +1394,116 @@ impl str::FromStr for NaiveDateTime { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string: F) + where F: Fn(&NaiveDateTime) -> Result, E: ::std::fmt::Debug +{ + use naive::date; + + assert_eq!( + to_string(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), + Some(r#""2016-07-08T09:10:48.090""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + Some(r#""2014-07-24T12:34:06""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), + Some(r#""0000-01-01T00:00:60""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), + Some(r#""-0001-12-31T23:59:59.000000007""#.into())); + assert_eq!( + to_string(&date::MIN.and_hms(0, 0, 0)).ok(), + Some(r#""-262144-01-01T00:00:00""#.into())); + assert_eq!( + to_string(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), + Some(r#""+262143-12-31T23:59:60.999999999""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + use naive::date; + + assert_eq!( + from_str(r#""2016-07-08T09:10:48.090""#).ok(), + Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); + assert_eq!( + from_str(r#""2016-7-8T9:10:48.09""#).ok(), + Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); + assert_eq!( + from_str(r#""2014-07-24T12:34:06""#).ok(), + Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6))); + assert_eq!( + from_str(r#""0000-01-01T00:00:60""#).ok(), + Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); + assert_eq!( + from_str(r#""0-1-1T0:0:60""#).ok(), + Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); + assert_eq!( + from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), + Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); + assert_eq!( + from_str(r#""-262144-01-01T00:00:00""#).ok(), + Some(date::MIN.and_hms(0, 0, 0))); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), + Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored + Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""2016-07-08""#).is_err()); + assert!(from_str(r#""09:10:48.090""#).is_err()); + assert!(from_str(r#""20160708T091048.090""#).is_err()); + assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); + assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); + assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); + assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); + assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); + assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); + assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); + assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); + assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); + assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); + assert!(from_str(r#"0"#).is_err()); + assert!(from_str(r#"20160708000000"#).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::NaiveDateTime; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // in the future it is likely to be redefined to more sane and reasonable format. - impl Encodable for NaiveDateTime { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("NaiveDateTime", 2, |s| { - try!(s.emit_struct_field("date", 0, |s| self.date.encode(s))); - try!(s.emit_struct_field("time", 1, |s| self.time.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } impl Decodable for NaiveDateTime { fn decode(d: &mut D) -> Result { - d.read_struct("NaiveDateTime", 2, |d| { - let date = try!(d.read_struct_field("date", 0, Decodable::decode)); - let time = try!(d.read_struct_field("time", 1, Decodable::decode)); - Ok(NaiveDateTime::new(date, time)) - }) + d.read_str()?.parse().map_err(|_| d.error("invalid date and time")) } } + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use naive::date::{self, NaiveDate}; - use rustc_serialize::json::encode; - - assert_eq!( - encode(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), - Some(r#"{"date":{"ymdf":16518115},"time":{"secs":33048,"frac":90000000}}"#.into())); - assert_eq!( - encode(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#"{"date":{"ymdf":16501977},"time":{"secs":45246,"frac":0}}"#.into())); - assert_eq!( - encode(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#"{"date":{"ymdf":20},"time":{"secs":59,"frac":1000000000}}"#.into())); - assert_eq!( - encode(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), - Some(r#"{"date":{"ymdf":-2341},"time":{"secs":86399,"frac":7}}"#.into())); - assert_eq!( - encode(&date::MIN.and_hms(0, 0, 0)).ok(), - Some(r#"{"date":{"ymdf":-2147483625},"time":{"secs":0,"frac":0}}"#.into())); - assert_eq!( - encode(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#"{"date":{"ymdf":2147481311},"time":{"secs":86399,"frac":1999999999}}"#.into())); + super::test_encodable_json(json::encode); } #[test] fn test_decodable() { - use naive::date::{self, NaiveDate}; - use rustc_serialize::json; - - let decode = |s: &str| json::decode::(s); - - assert_eq!( - decode(r#"{"date":{"ymdf":16518115},"time":{"secs":33048,"frac":90000000}}"#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); - assert_eq!( - decode(r#"{"time":{"frac":0,"secs":45246},"date":{"ymdf":16501977}}"#).ok(), - Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6))); - assert_eq!( - decode(r#"{"date": {"ymdf": 20}, - "time": {"secs": 59, - "frac": 1000000000}}"#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); - assert_eq!( - decode(r#"{"date":{"ymdf":-2341},"time":{"secs":86399,"frac":7}}"#).ok(), - Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); - assert_eq!( - decode(r#"{"date":{"ymdf":-2147483625},"time":{"secs":0,"frac":0}}"#).ok(), - Some(date::MIN.and_hms(0, 0, 0))); - assert_eq!( - decode(r#"{"date":{"ymdf":2147481311},"time":{"secs":86399,"frac":1999999999}}"#).ok(), - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(decode(r#"{"date":{},"time":{}}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":0},"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":20},"time":{"secs":86400,"frac":0}}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":-1}}"#).is_err()); - assert!(decode(r#"{"date":20,"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(decode(r#"{"date":"2016-08-04","time":"01:02:03.456"}"#).is_err()); - assert!(decode(r#"{"date":{"ymdf":20}}"#).is_err()); - assert!(decode(r#"{"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(decode(r#"{"ymdf":20}"#).is_err()); - assert!(decode(r#"{"secs":0,"frac":0}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"0"#).is_err()); - assert!(decode(r#"-1"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#""2016-08-04T12:34:56""#).is_err()); // :( - assert!(decode(r#""2016-08-04T12:34:56.789""#).is_err()); // :( - assert!(decode(r#"null"#).is_err()); + super::test_decodable_json(json::decode); } } @@ -1542,83 +1553,12 @@ mod serde { #[test] fn test_serde_serialize() { - use naive::date::{self, NaiveDate}; - use self::serde_json::to_string; - - assert_eq!( - to_string(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), - Some(r#""2016-07-08T09:10:48.090""#.into())); - assert_eq!( - to_string(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06""#.into())); - assert_eq!( - to_string(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#""0000-01-01T00:00:60""#.into())); - assert_eq!( - to_string(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), - Some(r#""-0001-12-31T23:59:59.000000007""#.into())); - assert_eq!( - to_string(&date::MIN.and_hms(0, 0, 0)).ok(), - Some(r#""-262144-01-01T00:00:00""#.into())); - assert_eq!( - to_string(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#""+262143-12-31T23:59:60.999999999""#.into())); + super::test_encodable_json(self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - use naive::date::{self, NaiveDate}; - - let from_str = |s: &str| serde_json::from_str::(s); - - assert_eq!( - from_str(r#""2016-07-08T09:10:48.090""#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); - assert_eq!( - from_str(r#""2016-7-8T9:10:48.09""#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90))); - assert_eq!( - from_str(r#""2014-07-24T12:34:06""#).ok(), - Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6))); - assert_eq!( - from_str(r#""0000-01-01T00:00:60""#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); - assert_eq!( - from_str(r#""0-1-1T0:0:60""#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000))); - assert_eq!( - from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), - Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); - assert_eq!( - from_str(r#""-262144-01-01T00:00:00""#).ok(), - Some(date::MIN.and_hms(0, 0, 0))); - assert_eq!( - from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - assert_eq!( - from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""2016-07-08""#).is_err()); - assert!(from_str(r#""09:10:48.090""#).is_err()); - assert!(from_str(r#""20160708T091048.090""#).is_err()); - assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); - assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); - assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); - assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); - assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); - assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); - assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"20160708000000"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); // :( - assert!(from_str(r#"null"#).is_err()); + super::test_decodable_json(self::serde_json::from_str); } #[test] diff --git a/src/naive/time.rs b/src/naive/time.rs index 29a0a4b..5171358 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -178,24 +178,6 @@ pub struct NaiveTime { } impl NaiveTime { - /// Makes a new `NaiveTime` from the serialized representation. - /// Used for serialization formats. - #[cfg(feature = "rustc-serialize")] - fn from_serialized(secs: u32, frac: u32) -> Option { - // check if the values are in the range - if secs >= 86400 { return None; } - if frac >= 2_000_000_000 { return None; } - - let time = NaiveTime { secs: secs, frac: frac }; - Some(time) - } - - /// Returns a serialized representation of this `NaiveDate`. - #[cfg(feature = "rustc-serialize")] - fn to_serialized(&self) -> (u32, u32) { - (self.secs, self.frac) - } - /// Makes a new `NaiveTime` from hour, minute and second. /// /// No [leap second](./index.html#leap-second-handling) is allowed here; @@ -1285,95 +1267,99 @@ impl str::FromStr for NaiveTime { } } +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json(to_string: F) + where F: Fn(&NaiveTime) -> Result, E: ::std::fmt::Debug +{ + assert_eq!(to_string(&NaiveTime::from_hms(0, 0, 0)).ok(), + Some(r#""00:00:00""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), + Some(r#""00:00:00.950""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), + Some(r#""00:00:60""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms(0, 1, 2)).ok(), + Some(r#""00:01:02""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_nano(3, 5, 7, 98765432)).ok(), + Some(r#""03:05:07.098765432""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms(7, 8, 9)).ok(), + Some(r#""07:08:09""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), + Some(r#""12:34:56.000789""#.into())); + assert_eq!(to_string(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), + Some(r#""23:59:60.999999999""#.into())); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + assert_eq!(from_str(r#""00:00:00""#).ok(), + Some(NaiveTime::from_hms(0, 0, 0))); + assert_eq!(from_str(r#""0:0:0""#).ok(), + Some(NaiveTime::from_hms(0, 0, 0))); + assert_eq!(from_str(r#""00:00:00.950""#).ok(), + Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); + assert_eq!(from_str(r#""0:0:0.95""#).ok(), + Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); + assert_eq!(from_str(r#""00:00:60""#).ok(), + Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); + assert_eq!(from_str(r#""00:01:02""#).ok(), + Some(NaiveTime::from_hms(0, 1, 2))); + assert_eq!(from_str(r#""03:05:07.098765432""#).ok(), + Some(NaiveTime::from_hms_nano(3, 5, 7, 98765432))); + assert_eq!(from_str(r#""07:08:09""#).ok(), + Some(NaiveTime::from_hms(7, 8, 9))); + assert_eq!(from_str(r#""12:34:56.000789""#).ok(), + Some(NaiveTime::from_hms_micro(12, 34, 56, 789))); + assert_eq!(from_str(r#""23:59:60.999999999""#).ok(), + Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); + assert_eq!(from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored + Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""000000""#).is_err()); + assert!(from_str(r#""00:00:61""#).is_err()); + assert!(from_str(r#""00:60:00""#).is_err()); + assert!(from_str(r#""24:00:00""#).is_err()); + assert!(from_str(r#""23:59:59,1""#).is_err()); + assert!(from_str(r#""012:34:56""#).is_err()); + assert!(from_str(r#""hh:mm:ss""#).is_err()); + assert!(from_str(r#"0"#).is_err()); + assert!(from_str(r#"86399"#).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::NaiveTime; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - // TODO the current serialization format is NEVER intentionally defined. - // this basically follows the automatically generated implementation for those traits, - // plus manual verification steps for avoiding security problem. - // in the future it is likely to be redefined to more sane and reasonable format. - impl Encodable for NaiveTime { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let (secs, frac) = self.to_serialized(); - s.emit_struct("NaiveTime", 2, |s| { - try!(s.emit_struct_field("secs", 0, |s| secs.encode(s))); - try!(s.emit_struct_field("frac", 1, |s| frac.encode(s))); - Ok(()) - }) + format!("{:?}", self).encode(s) } } impl Decodable for NaiveTime { fn decode(d: &mut D) -> Result { - d.read_struct("NaiveTime", 2, |d| { - let secs = try!(d.read_struct_field("secs", 0, Decodable::decode)); - let frac = try!(d.read_struct_field("frac", 1, Decodable::decode)); - NaiveTime::from_serialized(secs, frac).ok_or_else(|| d.error("invalid time")) - }) + d.read_str()?.parse().map_err(|_| d.error("invalid time")) } } + #[cfg(test)] use rustc_serialize::json; + #[test] fn test_encodable() { - use rustc_serialize::json::encode; - - assert_eq!(encode(&NaiveTime::from_hms(0, 0, 0)).ok(), - Some(r#"{"secs":0,"frac":0}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), - Some(r#"{"secs":0,"frac":950000000}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#"{"secs":59,"frac":1000000000}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms(0, 1, 2)).ok(), - Some(r#"{"secs":62,"frac":0}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms(7, 8, 9)).ok(), - Some(r#"{"secs":25689,"frac":0}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), - Some(r#"{"secs":45296,"frac":789000}"#.into())); - assert_eq!(encode(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#"{"secs":86399,"frac":1999999999}"#.into())); + super::test_encodable_json(json::encode); } #[test] fn test_decodable() { - use rustc_serialize::json; - - let decode = |s: &str| json::decode::(s); - - assert_eq!(decode(r#"{"secs":0,"frac":0}"#).ok(), - Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(decode(r#"{"frac":950000000,"secs":0}"#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(decode(r#"{"secs":59,"frac":1000000000}"#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); - assert_eq!(decode(r#"{"frac": 0, - "secs": 62}"#).ok(), - Some(NaiveTime::from_hms(0, 1, 2))); - assert_eq!(decode(r#"{"secs":25689,"frac":0}"#).ok(), - Some(NaiveTime::from_hms(7, 8, 9))); - assert_eq!(decode(r#"{"secs":45296,"frac":789000}"#).ok(), - Some(NaiveTime::from_hms_micro(12, 34, 56, 789))); - assert_eq!(decode(r#"{"secs":86399,"frac":1999999999}"#).ok(), - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(decode(r#"{"secs":0,"frac":-1}"#).is_err()); - assert!(decode(r#"{"secs":-1,"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":86400,"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":0,"frac":2000000000}"#).is_err()); - assert!(decode(r#"{"secs":0}"#).is_err()); - assert!(decode(r#"{"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":0.3,"frac":0}"#).is_err()); - assert!(decode(r#"{"secs":0,"frac":0.4}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"0"#).is_err()); - assert!(decode(r#"86399"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#""12:34:56""#).is_err()); // :( - assert!(decode(r#""12:34:56.789""#).is_err()); // :( - assert!(decode(r#"null"#).is_err()); + super::test_decodable_json(json::decode); } } @@ -1424,67 +1410,12 @@ mod serde { #[test] fn test_serde_serialize() { - use self::serde_json::to_string; - - assert_eq!(to_string(&NaiveTime::from_hms(0, 0, 0)).ok(), - Some(r#""00:00:00""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), - Some(r#""00:00:00.950""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#""00:00:60""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms(0, 1, 2)).ok(), - Some(r#""00:01:02""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_nano(3, 5, 7, 98765432)).ok(), - Some(r#""03:05:07.098765432""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms(7, 8, 9)).ok(), - Some(r#""07:08:09""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), - Some(r#""12:34:56.000789""#.into())); - assert_eq!(to_string(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#""23:59:60.999999999""#.into())); + super::test_encodable_json(self::serde_json::to_string); } #[test] fn test_serde_deserialize() { - let from_str = |s: &str| serde_json::from_str::(s); - - assert_eq!(from_str(r#""00:00:00""#).ok(), - Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(from_str(r#""0:0:0""#).ok(), - Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(from_str(r#""00:00:00.950""#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(from_str(r#""0:0:0.95""#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(from_str(r#""00:00:60""#).ok(), - Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); - assert_eq!(from_str(r#""00:01:02""#).ok(), - Some(NaiveTime::from_hms(0, 1, 2))); - assert_eq!(from_str(r#""03:05:07.098765432""#).ok(), - Some(NaiveTime::from_hms_nano(3, 5, 7, 98765432))); - assert_eq!(from_str(r#""07:08:09""#).ok(), - Some(NaiveTime::from_hms(7, 8, 9))); - assert_eq!(from_str(r#""12:34:56.000789""#).ok(), - Some(NaiveTime::from_hms_micro(12, 34, 56, 789))); - assert_eq!(from_str(r#""23:59:60.999999999""#).ok(), - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); - assert_eq!(from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999))); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""000000""#).is_err()); - assert!(from_str(r#""00:00:61""#).is_err()); - assert!(from_str(r#""00:60:00""#).is_err()); - assert!(from_str(r#""24:00:00""#).is_err()); - assert!(from_str(r#""23:59:59,1""#).is_err()); - assert!(from_str(r#""012:34:56""#).is_err()); - assert!(from_str(r#""hh:mm:ss""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"86399"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err()); // :( - assert!(from_str(r#"null"#).is_err()); + super::test_decodable_json(self::serde_json::from_str); } #[test] diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 8c545d2..885741a 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -23,23 +23,6 @@ pub struct FixedOffset { } impl FixedOffset { - /// Makes a new `FixedOffset` from the serialized representation. - /// Used for serialization formats. - #[cfg(feature = "rustc-serialize")] - fn from_serialized(secs: i32) -> Option { - // check if the values are in the range - if secs <= -86400 || 86400 <= secs { return None; } - - let offset = FixedOffset { local_minus_utc: secs }; - Some(offset) - } - - /// Returns a serialized representation of this `FixedOffset`. - #[cfg(feature = "rustc-serialize")] - fn to_serialized(&self) -> i32 { - self.local_minus_utc - } - /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference. /// The negative `secs` means the Western Hemisphere. /// @@ -139,72 +122,3 @@ impl fmt::Display for FixedOffset { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) } } -#[cfg(feature = "rustc-serialize")] -mod rustc_serialize { - use super::FixedOffset; - use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; - - // TODO the current serialization format is NEVER intentionally defined. - // this basically follows the automatically generated implementation for those traits, - // plus manual verification steps for avoiding security problem. - // in the future it is likely to be redefined to more sane and reasonable format. - - impl Encodable for FixedOffset { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let secs = self.to_serialized(); - s.emit_struct("FixedOffset", 1, |s| { - try!(s.emit_struct_field("local_minus_utc", 0, |s| secs.encode(s))); - Ok(()) - }) - } - } - - impl Decodable for FixedOffset { - fn decode(d: &mut D) -> Result { - d.read_struct("FixedOffset", 1, |d| { - let secs = try!(d.read_struct_field("local_minus_utc", 0, Decodable::decode)); - FixedOffset::from_serialized(secs).ok_or_else(|| d.error("invalid offset")) - }) - } - } - - #[test] - fn test_encodable() { - use rustc_serialize::json::encode; - - assert_eq!(encode(&FixedOffset::east(0)).ok(), - Some(r#"{"local_minus_utc":0}"#.into())); - assert_eq!(encode(&FixedOffset::east(1234)).ok(), - Some(r#"{"local_minus_utc":1234}"#.into())); - assert_eq!(encode(&FixedOffset::east(86399)).ok(), - Some(r#"{"local_minus_utc":86399}"#.into())); - assert_eq!(encode(&FixedOffset::west(1234)).ok(), - Some(r#"{"local_minus_utc":-1234}"#.into())); - assert_eq!(encode(&FixedOffset::west(86399)).ok(), - Some(r#"{"local_minus_utc":-86399}"#.into())); - } - - #[test] - fn test_decodable() { - use rustc_serialize::json; - - let decode = |s: &str| json::decode::(s); - - assert_eq!(decode(r#"{"local_minus_utc":0}"#).ok(), Some(FixedOffset::east(0))); - assert_eq!(decode(r#"{"local_minus_utc": 1234}"#).ok(), Some(FixedOffset::east(1234))); - assert_eq!(decode(r#"{"local_minus_utc":86399}"#).ok(), Some(FixedOffset::east(86399))); - assert_eq!(decode(r#"{"local_minus_utc":-1234}"#).ok(), Some(FixedOffset::west(1234))); - assert_eq!(decode(r#"{"local_minus_utc":-86399}"#).ok(), Some(FixedOffset::west(86399))); - - assert!(decode(r#"{"local_minus_utc":86400}"#).is_err()); - assert!(decode(r#"{"local_minus_utc":-86400}"#).is_err()); - assert!(decode(r#"{"local_minus_utc":0.1}"#).is_err()); - assert!(decode(r#"{"local_minus_utc":null}"#).is_err()); - assert!(decode(r#"{}"#).is_err()); - assert!(decode(r#"0"#).is_err()); - assert!(decode(r#"1234"#).is_err()); - assert!(decode(r#""string""#).is_err()); - assert!(decode(r#"null"#).is_err()); - } -} - diff --git a/src/offset/local.rs b/src/offset/local.rs index d37c15f..8f8c7ca 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -80,7 +80,6 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { /// let dt: DateTime = Local.timestamp(0, 0); /// ~~~~ #[derive(Copy, Clone)] -#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub struct Local; impl Local { diff --git a/src/offset/utc.rs b/src/offset/utc.rs index b1a97cc..2a0c9d4 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -33,7 +33,6 @@ use super::{TimeZone, Offset, LocalResult}; /// assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); /// ~~~~ #[derive(Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] pub struct UTC; impl UTC { From 36c45f2e02aa7c6d7ecb472c000a0245150c1263 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 07:08:19 +0900 Subject: [PATCH 16/53] Removed methods deprecated in 0.2. --- src/datetime.rs | 6 ------ src/naive/datetime.rs | 21 --------------------- 2 files changed, 27 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 62bb55c..7fddf5f 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -99,12 +99,6 @@ impl DateTime { self.datetime.timestamp_subsec_nanos() } - /// *Deprecated*: Same to `DateTime::timestamp`. - #[inline] - pub fn num_seconds_from_unix_epoch(&self) -> i64 { - self.timestamp() - } - /// Retrieves an associated offset from UTC. #[inline] pub fn offset<'a>(&'a self) -> &'a Tz::Offset { diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index b60a565..249659e 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -144,20 +144,6 @@ impl NaiveDateTime { } } - /// Same to [`NaiveDateTime::from_timestamp`](#method.from_timestamp). - #[inline] - #[deprecated(since = "0.2.0", note = "Renamed to `from_timestamp`")] - pub fn from_num_seconds_from_unix_epoch(secs: i64, nsecs: u32) -> NaiveDateTime { - NaiveDateTime::from_timestamp(secs, nsecs) - } - - /// Same to [`NaiveDateTime::from_timestamp_opt`](#method.from_timestamp_opt). - #[inline] - #[deprecated(since = "0.2.0", note = "Renamed to `from_timestamp_opt`")] - pub fn from_num_seconds_from_unix_epoch_opt(secs: i64, nsecs: u32) -> Option { - NaiveDateTime::from_timestamp_opt(secs, nsecs) - } - /// Parses a string with the specified format string and returns a new `NaiveDateTime`. /// See the [`format::strftime` module](../../format/strftime/index.html) /// on the supported escape sequences. @@ -341,13 +327,6 @@ impl NaiveDateTime { self.time.nanosecond() } - /// Same to [`NaiveDateTime::timestamp`](#method.timestamp). - #[inline] - #[deprecated(since = "0.2.0", note = "Renamed to `timestamp`")] - pub fn num_seconds_from_unix_epoch(&self) -> i64 { - self.timestamp() - } - /// Adds given `Duration` to the current date and time. /// /// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), From 2b5553ee7637fce1d317e9ee67c060adff72e9ad Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Mon, 6 Feb 2017 08:24:16 +0900 Subject: [PATCH 17/53] Made `Parsed` not fully destructible. So that we can add more fields without breaking a major compatibility (as per RFC 1105). --- src/format/parse.rs | 8 +++++--- src/format/parsed.rs | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index 5256829..89c8e81 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -358,9 +358,11 @@ fn test_parse() { ($fmt:expr, $items:expr; $err:tt) => ( assert_eq!(parse_all($fmt, &$items), Err($err)) ); - ($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => ( - assert_eq!(parse_all($fmt, &$items), Ok(Parsed { $($k: Some($v),)* ..Parsed::new() })) - ); + ($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] { + let mut expected = Parsed::new(); + $(expected.$k = Some($v);)* + assert_eq!(parse_all($fmt, &$items), Ok(expected)) + }); } // empty string diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 0cfe72f..423f19f 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -108,6 +108,9 @@ pub struct Parsed { /// Offset from the local time to UTC, in seconds. pub offset: Option, + + /// A dummy field to make this type not fully destructible (required for API stability). + _dummy: (), } /// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"), @@ -121,14 +124,23 @@ fn set_if_consistent(old: &mut Option, new: T) -> ParseResult<( } } +impl Default for Parsed { + fn default() -> Parsed { + Parsed { + year: None, year_div_100: None, year_mod_100: None, isoyear: None, + isoyear_div_100: None, isoyear_mod_100: None, month: None, + week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None, + ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None, + second: None, nanosecond: None, timestamp: None, offset: None, + _dummy: (), + } + } +} + impl Parsed { /// Returns the initial value of parsed parts. pub fn new() -> Parsed { - Parsed { year: None, year_div_100: None, year_mod_100: None, isoyear: None, - isoyear_div_100: None, isoyear_mod_100: None, month: None, - week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None, - ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None, - second: None, nanosecond: None, timestamp: None, offset: None } + Parsed::default() } /// Tries to set the [`year`](#structfield.year) field from given value. From 7ea1ce50801abf66867a100704946ea007c0ee7a Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 03:43:59 +0900 Subject: [PATCH 18/53] `FixedOffset` is now the official "fixed offset value" type. This may sound strange, but the final type for the offset "value" was originally `time::Duration` (returned by `Offset::local_minus_utc`). This caused a lot of problems becaus adding `Duration` fully interacts with leap seconds and `Duration` itself is somewhat deprecated. This commit entirely replaces this role of `Duration` with `FixedOffset`. So if we had `Offset` and `Duration` to represent the "storage" offset type and the offset "value" in the past, we now have `Offset` and `FixedOffset`. Storage-to-value conversion is called to "fix" the offset---an apt term for the type. The list of actual changes: - The time zone offset is now restricted to UTC-23:59:59 through UTC+23:59:59, and no subsecond value is allowed. As described above, `FixedOffset` is now fully used for this purpose. - One can now add and subtract `FixedOffset` to/from timelike values. Replaces a temporary `chrono::offset::add_with_leapsecond` function. Datelike & non-timelike values are never affected by the offset. - UTC and local views to `Date` are now identical. We keep relevant methods for the consistency right now. - `chrono::format::format` now receives `FixedOffset` in place of `(Old)Duration`. - `Offset` now has a `fix` method to resolve, or to "fix" the "storage" offset (`Offset`) to the offset "value" (`FixedOffset`). - `FixedOffset::{local_minus_utc, utc_minus_local}` methods are added. They no longer depend on `Duration` as well. --- src/date.rs | 67 +++------------------------- src/datetime.rs | 6 +-- src/format/mod.rs | 20 ++++----- src/format/parsed.rs | 11 +---- src/lib.rs | 2 +- src/offset/fixed.rs | 104 ++++++++++++++++++++++++++++++++++++++++++- src/offset/local.rs | 12 +++++ src/offset/mod.rs | 28 +++--------- src/offset/utc.rs | 8 ++-- 9 files changed, 146 insertions(+), 112 deletions(-) diff --git a/src/date.rs b/src/date.rs index 16374c4..aa63706 100644 --- a/src/date.rs +++ b/src/date.rs @@ -9,7 +9,7 @@ use std::ops::{Add, Sub}; use oldtime::Duration as OldDuration; use {Weekday, Datelike}; -use offset::{TimeZone, Offset}; +use offset::TimeZone; use offset::utc::UTC; use naive; use naive::date::NaiveDate; @@ -256,9 +256,13 @@ impl Date { } /// Returns a view to the naive local date. + /// + /// This is technically same to [`naive_utc`](#method.naive_utc) + /// because the offset is restricted to never exceed one day, + /// but provided for the consistency. #[inline] pub fn naive_local(&self) -> NaiveDate { - self.date + self.offset.local_minus_utc() + self.date } } @@ -398,62 +402,3 @@ impl fmt::Display for Date where Tz::Offset: fmt::Display { } } -#[cfg(test)] -mod tests { - use std::fmt; - use oldtime::Duration; - - use Datelike; - use naive::date::NaiveDate; - use naive::datetime::NaiveDateTime; - use offset::{TimeZone, Offset, LocalResult}; - use offset::local::Local; - - #[derive(Copy, Clone, PartialEq, Eq)] - struct UTC1y; // same to UTC but with an offset of 365 days - - #[derive(Copy, Clone, PartialEq, Eq)] - struct OneYear; - - impl TimeZone for UTC1y { - type Offset = OneYear; - - fn from_offset(_offset: &OneYear) -> UTC1y { UTC1y } - - fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult { - LocalResult::Single(OneYear) - } - fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult { - LocalResult::Single(OneYear) - } - - fn offset_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear } - fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear } - } - - impl Offset for OneYear { - fn local_minus_utc(&self) -> Duration { Duration::days(365) } - } - - impl fmt::Debug for OneYear { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") } - } - - #[test] - fn test_date_weird_offset() { - assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29)), - "2012-02-29+8760:00".to_string()); - assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7)), - "2012-02-29T05:06:07+8760:00".to_string()); - assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4)), - "2012-03-04+8760:00".to_string()); - assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7)), - "2012-03-04T05:06:07+8760:00".to_string()); - } - - #[test] - fn test_local_date_sanity_check() { // issue #27 - assert_eq!(Local.ymd(2999, 12, 28).day(), 28); - } -} - diff --git a/src/datetime.rs b/src/datetime.rs index 7fddf5f..c88d4c9 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -9,7 +9,7 @@ use std::ops::{Add, Sub}; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; -use offset::{TimeZone, Offset, add_with_leapsecond}; +use offset::{TimeZone, Offset}; use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; @@ -59,7 +59,7 @@ impl DateTime { /// Unlike `date`, this is not associated to the time zone. #[inline] pub fn time(&self) -> NaiveTime { - add_with_leapsecond(&self.datetime.time(), &self.offset.local_minus_utc()) + self.datetime.time() + self.offset.fix() } /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC @@ -152,7 +152,7 @@ impl DateTime { /// Returns a view to the naive local datetime. #[inline] pub fn naive_local(&self) -> NaiveDateTime { - add_with_leapsecond(&self.datetime, &self.offset.local_minus_utc()) + self.datetime + self.offset.fix() } } diff --git a/src/format/mod.rs b/src/format/mod.rs index 3f36251..74f9fb5 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -5,11 +5,11 @@ use std::fmt; use std::error::Error; -use oldtime::Duration as OldDuration; use {Datelike, Timelike}; use div::{div_floor, mod_floor}; -use offset::{Offset, add_with_leapsecond}; +use offset::Offset; +use offset::fixed::FixedOffset; use naive::date::NaiveDate; use naive::time::NaiveTime; @@ -252,7 +252,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>, - off: Option<&(String, OldDuration)>, items: I) -> fmt::Result + off: Option<&(String, FixedOffset)>, items: I) -> fmt::Result where I: Iterator> { // full and abbreviated month and weekday names static SHORT_MONTHS: [&'static str; 12] = @@ -302,7 +302,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt (Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()), (Some(d), Some(t), Some(&(_, off))) => - Some(add_with_leapsecond(&d.and_time(*t), &-off).timestamp()), + Some((d.and_time(*t) - off).timestamp()), (_, _, _) => None }), }; @@ -332,15 +332,15 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt /// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. /// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true. - fn write_local_minus_utc(w: &mut fmt::Formatter, off: OldDuration, + fn write_local_minus_utc(w: &mut fmt::Formatter, off: FixedOffset, allow_zulu: bool, use_colon: bool) -> fmt::Result { - let off = off.num_minutes(); + let off = off.local_minus_utc(); if !allow_zulu || off != 0 { let (sign, off) = if off < 0 {('-', -off)} else {('+', off)}; if use_colon { - write!(w, "{}{:02}:{:02}", sign, off / 60, off % 60) + write!(w, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60) } else { - write!(w, "{}{:02}{:02}", sign, off / 60, off % 60) + write!(w, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60) } } else { write!(w, "Z") @@ -452,7 +452,7 @@ pub struct DelayedFormat { /// The time view, if any. time: Option, /// The name and local-to-UTC difference for the offset (timezone), if any. - off: Option<(String, OldDuration)>, + off: Option<(String, FixedOffset)>, /// An iterator returning formatting items. items: I, } @@ -467,7 +467,7 @@ impl<'a, I: Iterator> + Clone> DelayedFormat { pub fn new_with_offset(date: Option, time: Option, offset: &Off, items: I) -> DelayedFormat where Off: Offset + fmt::Display { - let name_and_diff = (offset.to_string(), offset.local_minus_utc()); + let name_and_diff = (offset.to_string(), offset.fix()); DelayedFormat { date: date, time: time, off: Some(name_and_diff), items: items } } } diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 423f19f..7eb3a3e 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -604,20 +604,13 @@ impl Parsed { let nanosecond = self.nanosecond.unwrap_or(0); let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond); let dt = try!(dt.ok_or(OUT_OF_RANGE)); - - // we cannot handle offsets larger than i32 at all. give up if so. - // we can instead make `to_naive_datetime_with_offset` to accept i64, but this makes - // the algorithm too complex and tons of edge cases. i32 should be enough for all. - let offset = tz.offset_from_utc_datetime(&dt).local_minus_utc().num_seconds(); - guessed_offset = try!(offset.to_i32().ok_or(OUT_OF_RANGE)); + guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc(); } // checks if the given `DateTime` has a consistent `Offset` with given `self.offset`. let check_offset = |dt: &DateTime| { if let Some(offset) = self.offset { - let delta = dt.offset().local_minus_utc().num_seconds(); - // if `delta` does not fit in `i32`, it cannot equal to `self.offset` anyway. - delta.to_i32() == Some(offset) + dt.offset().fix().local_minus_utc() == offset } else { true } diff --git a/src/lib.rs b/src/lib.rs index 6badb30..87bd9cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,7 +177,7 @@ //! assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1 //! //! // time zone accessor and manipulation -//! assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9)); +//! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); //! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600)); //! assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); //! diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 885741a..884da6c 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -3,12 +3,16 @@ //! The time zone which has a fixed offset from UTC. +use std::ops::{Add, Sub}; use std::fmt; use oldtime::Duration as OldDuration; +use Timelike; use div::div_mod_floor; +use naive::time::NaiveTime; use naive::date::NaiveDate; use naive::datetime::NaiveDateTime; +use datetime::DateTime; use super::{TimeZone, Offset, LocalResult}; /// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. @@ -82,6 +86,16 @@ impl FixedOffset { None } } + + /// Returns the number of seconds to add to convert from UTC to the local time. + pub fn local_minus_utc(&self) -> i32 { + self.local_minus_utc + } + + /// Returns the number of seconds to add to convert from the local time to UTC. + pub fn utc_minus_local(&self) -> i32 { + -self.local_minus_utc + } } impl TimeZone for FixedOffset { @@ -101,7 +115,7 @@ impl TimeZone for FixedOffset { } impl Offset for FixedOffset { - fn local_minus_utc(&self) -> OldDuration { OldDuration::seconds(self.local_minus_utc as i64) } + fn fix(&self) -> FixedOffset { *self } } impl fmt::Debug for FixedOffset { @@ -122,3 +136,91 @@ impl fmt::Display for FixedOffset { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) } } +// addition or subtraction of FixedOffset to/from Timelike values is same to +// adding or subtracting the offset's local_minus_utc value +// but keep keeps the leap second information. +// this should be implemented more efficiently, but for the time being, this is generic right now. + +fn add_with_leapsecond(lhs: &T, rhs: i32) -> T + where T: Timelike + Add +{ + // extract and temporarily remove the fractional part and later recover it + let nanos = lhs.nanosecond(); + let lhs = lhs.with_nanosecond(0).unwrap(); + (lhs + OldDuration::seconds(rhs as i64)).with_nanosecond(nanos).unwrap() +} + +impl Add for NaiveTime { + type Output = NaiveTime; + + #[inline] + fn add(self, rhs: FixedOffset) -> NaiveTime { + add_with_leapsecond(&self, rhs.local_minus_utc) + } +} + +impl Sub for NaiveTime { + type Output = NaiveTime; + + #[inline] + fn sub(self, rhs: FixedOffset) -> NaiveTime { + add_with_leapsecond(&self, -rhs.local_minus_utc) + } +} + +impl Add for NaiveDateTime { + type Output = NaiveDateTime; + + #[inline] + fn add(self, rhs: FixedOffset) -> NaiveDateTime { + add_with_leapsecond(&self, rhs.local_minus_utc) + } +} + +impl Sub for NaiveDateTime { + type Output = NaiveDateTime; + + #[inline] + fn sub(self, rhs: FixedOffset) -> NaiveDateTime { + add_with_leapsecond(&self, -rhs.local_minus_utc) + } +} + +impl Add for DateTime { + type Output = DateTime; + + #[inline] + fn add(self, rhs: FixedOffset) -> DateTime { + add_with_leapsecond(&self, rhs.local_minus_utc) + } +} + +impl Sub for DateTime { + type Output = DateTime; + + #[inline] + fn sub(self, rhs: FixedOffset) -> DateTime { + add_with_leapsecond(&self, -rhs.local_minus_utc) + } +} + +#[cfg(test)] +mod tests { + use offset::TimeZone; + use super::FixedOffset; + + #[test] + fn test_date_extreme_offset() { + // starting from 0.3 we don't have an offset exceeding one day. + // this makes everything easier! + assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)), + "2012-02-29+23:59:59".to_string()); + assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)), + "2012-02-29T05:06:07+23:59:59".to_string()); + assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)), + "2012-03-04-23:59:59".to_string()); + assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)), + "2012-03-04T05:06:07-23:59:59".to_string()); + } +} + diff --git a/src/offset/local.rs b/src/offset/local.rs index 8f8c7ca..c470d9a 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -137,3 +137,15 @@ impl TimeZone for Local { } } +#[cfg(test)] +mod tests { + use Datelike; + use offset::TimeZone; + use super::Local; + + #[test] + fn test_local_date_sanity_check() { // issue #27 + assert_eq!(Local.ymd(2999, 12, 28).day(), 28); + } +} + diff --git a/src/offset/mod.rs b/src/offset/mod.rs index cf27da9..965497a 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -21,31 +21,15 @@ */ use std::fmt; -use std::ops::Add; -use oldtime::Duration as OldDuration; use Weekday; -use Timelike; use naive::date::NaiveDate; use naive::time::NaiveTime; use naive::datetime::NaiveDateTime; use date::Date; use datetime::DateTime; use format::{parse, Parsed, ParseResult, StrftimeItems}; - -/// Same to `*lhs + *rhs`, but keeps the leap second information. -/// `rhs` should *not* have a fractional second. -// TODO this should be replaced by the addition with FixedOffset in 0.3! -pub fn add_with_leapsecond(lhs: &T, rhs: &OldDuration) -> T - where T: Timelike + Add -{ - debug_assert!(*rhs == OldDuration::seconds(rhs.num_seconds())); - - // extract and temporarily remove the fractional part and later recover it - let nanos = lhs.nanosecond(); - let lhs = lhs.with_nanosecond(0).unwrap(); - (lhs + *rhs).with_nanosecond(nanos).unwrap() -} +use self::fixed::FixedOffset; /// The conversion result from the local time to the timezone-aware datetime types. #[derive(Clone, PartialEq, Debug)] @@ -175,8 +159,8 @@ impl LocalResult { /// The offset from the local time to UTC. pub trait Offset: Sized + Clone + fmt::Debug { - /// Returns the offset from UTC to the local time stored. - fn local_minus_utc(&self) -> OldDuration; + /// Returns the fixed offset from UTC to the local time stored. + fn fix(&self) -> FixedOffset; } /// The time zone. @@ -358,15 +342,15 @@ pub trait TimeZone: Sized + Clone { /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible. fn from_local_date(&self, local: &NaiveDate) -> LocalResult> { self.offset_from_local_date(local).map(|offset| { - Date::from_utc(*local - offset.local_minus_utc(), offset) + // since FixedOffset is within +/- 1 day, the date is never affected + Date::from_utc(*local, offset) }) } /// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible. fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult> { self.offset_from_local_datetime(local).map(|offset| { - let utc = add_with_leapsecond(local, &-offset.local_minus_utc()); - DateTime::from_utc(utc, offset) + DateTime::from_utc(*local - offset.fix(), offset) }) } diff --git a/src/offset/utc.rs b/src/offset/utc.rs index 2a0c9d4..aa028fc 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -1,19 +1,17 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -/*! - * The UTC (Coordinated Universal Time) time zone. - */ +//! The UTC (Coordinated Universal Time) time zone. use std::fmt; use oldtime; -use oldtime::Duration as OldDuration; use naive::date::NaiveDate; use naive::datetime::NaiveDateTime; use date::Date; use datetime::DateTime; use super::{TimeZone, Offset, LocalResult}; +use super::fixed::FixedOffset; /// The UTC time zone. This is the most efficient time zone when you don't need the local time. /// It is also used as an offset (which is also a dummy type). @@ -64,7 +62,7 @@ impl TimeZone for UTC { } impl Offset for UTC { - fn local_minus_utc(&self) -> OldDuration { OldDuration::zero() } + fn fix(&self) -> FixedOffset { FixedOffset::east(0) } } impl fmt::Debug for UTC { From 0ac41c70b1c0ee5d4b3af72b6902132dc8da2add Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 04:05:05 +0900 Subject: [PATCH 19/53] Minor additions to formatting items. - Formatting item types are no longer `Copy`. - `Numeric` and `Fixed` items now have `Internal` variants reserved for the future expansion. It had been hard to expand the items without totally breaking the backward compatibility (as per the API evolution guideline of RFC 1105). - `Item::Owned{Literal,Space}` for the owned variant of `Item::{Literal,Space}` has been added. Closes #76. --- README.md | 2 +- src/format/mod.rs | 57 +++++++++++++++++++++++++++++++++++++++--- src/format/parse.rs | 14 ++++++++++- src/format/strftime.rs | 6 ++--- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ccbe54d..1ceaf3a 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,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 // time zone accessor and manipulation -assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9)); +assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600)); assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); diff --git a/src/format/mod.rs b/src/format/mod.rs index 74f9fb5..e2a95f1 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -17,6 +17,10 @@ pub use self::strftime::StrftimeItems; pub use self::parsed::Parsed; pub use self::parse::parse; +/// An unhabitated type used for `InternalNumeric` and `InternalFixed` below. +#[derive(Clone, PartialEq, Eq)] +enum Void {} + /// Padding characters for numeric items. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Pad { @@ -41,7 +45,7 @@ pub enum Pad { /// It also trims the preceding whitespaces if any. /// It cannot parse the negative number, so some date and time cannot be formatted then /// parsed with the same formatting items. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum Numeric { /// Full Gregorian year (FW=4, PW=∞). /// May accept years before 1 BCE or after 9999 CE, given an initial sign. @@ -88,13 +92,31 @@ pub enum Numeric { /// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞). /// For formatting, it assumes UTC upon the absence of time zone offset. Timestamp, + + /// Internal uses only. + /// + /// This item exists so that one can add additional internal-only formatting + /// without breaking major compatibility (as enum variants cannot be selectively private). + Internal(InternalNumeric), +} + +/// An opaque type representing numeric item types for internal uses only. +#[derive(Clone, PartialEq, Eq)] +pub struct InternalNumeric { + _dummy: Void, +} + +impl fmt::Debug for InternalNumeric { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "") + } } /// Fixed-format item types. /// /// They have their own rules of formatting and parsing. /// Otherwise noted, they print in the specified cases but parse case-insensitively. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum Fixed { /// Abbreviated month names. /// @@ -157,15 +179,37 @@ pub enum Fixed { RFC2822, /// RFC 3339 & ISO 8601 date and time syntax. RFC3339, + + /// Internal uses only. + /// + /// This item exists so that one can add additional internal-only formatting + /// without breaking major compatibility (as enum variants cannot be selectively private). + Internal(InternalFixed), +} + +/// An opaque type representing fixed-format item types for internal uses only. +#[derive(Clone, PartialEq, Eq)] +pub struct InternalFixed { + _dummy: Void, +} + +impl fmt::Debug for InternalFixed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "") + } } /// A single formatting item. This is used for both formatting and parsing. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum Item<'a> { /// A literally printed and parsed text. Literal(&'a str), + /// Same to `Literal` but with the string owned by the item. + OwnedLiteral(Box), /// Whitespace. Prints literally but reads zero or more whitespace. Space(&'a str), + /// Same to `Space` but with the string owned by the item. + OwnedSpace(Box), /// Numeric item. Can be optionally padded to the maximal length (if any) when formatting; /// the parser simply ignores any padded whitespace and zeroes. Numeric(Numeric, Pad), @@ -268,6 +312,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt for item in items { match item { Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)), + Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => try!(write!(w, "{}", s)), Item::Numeric(spec, pad) => { use self::Numeric::*; @@ -305,6 +350,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt Some((d.and_time(*t) - off).timestamp()), (_, _, _) => None }), + + // for the future expansion + Internal(ref int) => match int._dummy {}, }; if let Some(v) = v { @@ -420,6 +468,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt } else { None }, + + // for the future expansion + Internal(ref int) => match int._dummy {}, }; match ret { diff --git a/src/format/parse.rs b/src/format/parse.rs index 89c8e81..3b58335 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -218,7 +218,13 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<( s = &s[prefix.len()..]; } - Item::Space(_) => { + Item::OwnedLiteral(ref prefix) => { + if s.len() < prefix.len() { return Err(TOO_SHORT); } + if !s.starts_with(&prefix[..]) { return Err(INVALID); } + s = &s[prefix.len()..]; + } + + Item::Space(_) | Item::OwnedSpace(_) => { s = s.trim_left(); } @@ -247,6 +253,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<( Second => (2, false, Parsed::set_second), Nanosecond => (9, false, Parsed::set_nanosecond), Timestamp => (usize::MAX, false, Parsed::set_timestamp), + + // for the future expansion + Internal(ref int) => match int._dummy {}, }; s = s.trim_left(); @@ -324,6 +333,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<( RFC2822 => try_consume!(parse_rfc2822(parsed, s)), RFC3339 => try_consume!(parse_rfc3339(parsed, s)), + + // for the future expansion + Internal(ref int) => match int._dummy {}, } } diff --git a/src/format/strftime.rs b/src/format/strftime.rs index 1553aad..2002d31 100644 --- a/src/format/strftime.rs +++ b/src/format/strftime.rs @@ -173,7 +173,7 @@ impl<'a> Iterator for StrftimeItems<'a> { fn next(&mut self) -> Option> { // we have some reconstructed items to return if !self.recons.is_empty() { - let item = self.recons[0]; + let item = self.recons[0].clone(); self.recons = &self.recons[1..]; return Some(item); } @@ -292,8 +292,8 @@ impl<'a> Iterator for StrftimeItems<'a> { // adjust `item` if we have any padding modifier if let Some(new_pad) = pad_override { match item { - Item::Numeric(kind, _pad) if self.recons.is_empty() => - Some(Item::Numeric(kind, new_pad)), + Item::Numeric(ref kind, _pad) if self.recons.is_empty() => + Some(Item::Numeric(kind.clone(), new_pad)), _ => Some(Item::Error), // no reconstructed or non-numeric item allowed } } else { From 96b5ba391b7a04b4c11c7043adbb9287d6fd4d68 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 04:26:29 +0900 Subject: [PATCH 20/53] Fixed a panic when the Local offset receives a leap second. Fixes #123. --- src/offset/local.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/offset/local.rs b/src/offset/local.rs index c470d9a..8dd8b2e 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -49,7 +49,7 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { // the number 1 is arbitrary but should be non-zero to trigger `mktime`. let tm_utcoff = if local {1} else {0}; - let tm = oldtime::Tm { + let mut tm = oldtime::Tm { tm_sec: d.second() as i32, tm_min: d.minute() as i32, tm_hour: d.hour() as i32, @@ -62,6 +62,13 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { tm_utcoff: tm_utcoff, tm_nsec: d.nanosecond() as i32, }; + + // adjustment for the leap second + if tm.tm_nsec >= 1_000_000_000 { + tm.tm_sec += 1; + tm.tm_nsec -= 1_000_000_000; + } + tm.to_timespec() } @@ -147,5 +154,19 @@ mod tests { fn test_local_date_sanity_check() { // issue #27 assert_eq!(Local.ymd(2999, 12, 28).day(), 28); } + + #[test] + fn test_leap_second() { // issue #123 + let today = Local::today(); + let dt = today.and_hms_milli(1, 2, 59, 1000); + let timestr = dt.time().to_string(); + // the OS API may or may not support the leap second, + // but there are only two sensible options. + assert!(timestr == "01:02:60" || timestr == "01:03:00", + "unexpected timestr {:?}", timestr); + + // this case, while unsupported by most APIs, should *not* panic. + let _ = today.and_hms_milli_opt(1, 2, 3, 1000); + } } From e9e7bdd99c515421083f4e5b6913d62e69f53c28 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 04:51:08 +0900 Subject: [PATCH 21/53] Removed older aliases (previously marked as deprecated). The intention was to add newer methods using `std::time::Duration` to the older names, but it will break the API compatibility anyway. Better to completely remove them right now. --- src/date.rs | 29 ----------------------------- src/naive/datetime.rs | 30 ------------------------------ src/naive/time.rs | 30 ------------------------------ 3 files changed, 89 deletions(-) diff --git a/src/date.rs b/src/date.rs index aa63706..a88ac18 100644 --- a/src/date.rs +++ b/src/date.rs @@ -231,24 +231,6 @@ impl Date { self.date.signed_duration_since(rhs.date) } - /// Same to [`Date::checked_add_signed`](#method.checked_add_signed). - #[inline] - #[deprecated(since = "0.2.26", - note = "Renamed to `checked_add_signed`, \ - will be replaced with a version with `std::time::Duration`")] - pub fn checked_add(self, rhs: OldDuration) -> Option> { - self.checked_add_signed(rhs) - } - - /// Same to [`Date::checked_sub_signed`](#method.checked_sub_signed). - #[inline] - #[deprecated(since = "0.2.26", - note = "Renamed to `checked_sub_signed`, \ - will be replaced with a version with `std::time::Duration`")] - pub fn checked_sub(self, rhs: OldDuration) -> Option> { - self.checked_sub_signed(rhs) - } - /// Returns a view to the naive UTC date. #[inline] pub fn naive_utc(&self) -> NaiveDate { @@ -370,17 +352,6 @@ impl Add for Date { } } -// XXX this does not really work yet -#[deprecated(since = "0.2.26", note = "Use `signed_duration_since` method instead")] -impl Sub> for Date { - type Output = OldDuration; - - #[inline] - fn sub(self, rhs: Date) -> OldDuration { - self.signed_duration_since(rhs) - } -} - impl Sub for Date { type Output = Date; diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 249659e..81ac8af 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -543,24 +543,6 @@ impl NaiveDateTime { self.date.signed_duration_since(rhs.date) + self.time.signed_duration_since(rhs.time) } - /// Same to [`NaiveDateTime::checked_add_signed`](#method.checked_add_signed). - #[inline] - #[deprecated(since = "0.2.26", - note = "Renamed to `checked_add_signed`, \ - will be replaced with a version with `std::time::Duration`")] - pub fn checked_add(self, rhs: OldDuration) -> Option { - self.checked_add_signed(rhs) - } - - /// Same to [`NaiveDateTime::checked_sub_signed`](#method.checked_sub_signed). - #[inline] - #[deprecated(since = "0.2.26", - note = "Renamed to `checked_sub_signed`, \ - will be replaced with a version with `std::time::Duration`")] - pub fn checked_sub(self, rhs: OldDuration) -> Option { - self.checked_sub_signed(rhs) - } - /// Formats the combined date and time with the specified formatting items. /// Otherwise it is same to the ordinary [`format`](#method.format) method. /// @@ -1193,18 +1175,6 @@ impl Add for NaiveDateTime { } } -/// Use [`NaiveDateTime::signed_duration_since`](#method.signed_duration_since) instead. -// XXX this does not really work yet -#[deprecated(since = "0.2.26", note = "Use `signed_duration_since` method instead")] -impl Sub for NaiveDateTime { - type Output = OldDuration; - - #[inline] - fn sub(self, rhs: NaiveDateTime) -> OldDuration { - self.signed_duration_since(rhs) - } -} - /// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`. /// It is same to the addition with a negated `Duration`. /// diff --git a/src/naive/time.rs b/src/naive/time.rs index 5171358..2d9c7bc 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -695,24 +695,6 @@ impl NaiveTime { OldDuration::seconds(secs + adjust) + OldDuration::nanoseconds(frac) } - /// Same to [`NaiveTime::overflowing_add_signed`](#method.overflowing_add_signed). - #[inline] - #[deprecated(since = "0.2.26", - note = "Renamed to `overflowing_add_signed`, \ - will be replaced with a version with `std::time::Duration`")] - pub fn overflowing_add(self, rhs: OldDuration) -> (NaiveTime, i64) { - self.overflowing_add_signed(rhs) - } - - /// Same to [`NaiveTime::overflowing_sub_signed`](#method.overflowing_sub_signed). - #[inline] - #[deprecated(since = "0.2.26", - note = "Renamed to `overflowing_sub_signed`, \ - will be replaced with a version with `std::time::Duration`")] - pub fn overflowing_sub(self, rhs: OldDuration) -> (NaiveTime, i64) { - self.overflowing_sub_signed(rhs) - } - /// Formats the time with the specified formatting items. /// Otherwise it is same to the ordinary [`format`](#method.format) method. /// @@ -1072,18 +1054,6 @@ impl Add for NaiveTime { } } -/// Use [`NaiveTime::signed_duration_since`](#method.signed_duration_since) instead. -// XXX this does not really work yet -#[deprecated(since = "0.2.26", note = "Use `signed_duration_since` method instead")] -impl Sub for NaiveTime { - type Output = OldDuration; - - #[inline] - fn sub(self, rhs: NaiveTime) -> OldDuration { - self.signed_duration_since(rhs) - } -} - /// A subtraction of `Duration` from `NaiveTime` wraps around and never overflows or underflows. /// In particular the addition ignores integral number of days. /// It is same to the addition with a negated `Duration`. From d7d152eff1109794cef7e1515220c5edae67274f Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 05:22:12 +0900 Subject: [PATCH 22/53] Avoid passing nanoseconds to the OS APIs at all. (#123) Windows still seems to have an issue---it does not accept 60 in the second field at all. Let's see if not passing it around improves the situation. --- src/offset/local.rs | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/offset/local.rs b/src/offset/local.rs index 8dd8b2e..dd037f8 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -49,7 +49,7 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { // the number 1 is arbitrary but should be non-zero to trigger `mktime`. let tm_utcoff = if local {1} else {0}; - let mut tm = oldtime::Tm { + let tm = oldtime::Tm { tm_sec: d.second() as i32, tm_min: d.minute() as i32, tm_hour: d.hour() as i32, @@ -60,15 +60,10 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { tm_yday: 0, // and this tm_isdst: -1, tm_utcoff: tm_utcoff, - tm_nsec: d.nanosecond() as i32, + // do not set this, OS APIs are heavily inconsistent in terms of leap second handling + tm_nsec: 0, }; - // adjustment for the leap second - if tm.tm_nsec >= 1_000_000_000 { - tm.tm_sec += 1; - tm.tm_nsec -= 1_000_000_000; - } - tm.to_timespec() } @@ -110,6 +105,7 @@ impl TimeZone for Local { fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult { self.from_local_date(local).map(|date| *date.offset()) } + fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult { self.from_local_datetime(local).map(|datetime| *datetime.offset()) } @@ -117,6 +113,7 @@ impl TimeZone for Local { fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset { *self.from_utc_date(utc).offset() } + fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset { *self.from_utc_datetime(utc).offset() } @@ -129,18 +126,32 @@ impl TimeZone for Local { let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0)); midnight.map(|datetime| Date::from_utc(*local, *datetime.offset())) } + fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult> { let timespec = datetime_to_timespec(local, true); - LocalResult::Single(tm_to_datetime(oldtime::at(timespec))) + + // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them + let mut tm = oldtime::at(timespec); + assert_eq!(tm.tm_nsec, 0); + tm.tm_nsec = local.nanosecond() as i32; + + LocalResult::Single(tm_to_datetime(tm)) } fn from_utc_date(&self, utc: &NaiveDate) -> Date { let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0)); Date::from_utc(*utc, *midnight.offset()) } + fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime { let timespec = datetime_to_timespec(utc, false); - tm_to_datetime(oldtime::at(timespec)) + + // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them + let mut tm = oldtime::at(timespec); + assert_eq!(tm.tm_nsec, 0); + tm.tm_nsec = utc.nanosecond() as i32; + + tm_to_datetime(tm) } } @@ -158,6 +169,7 @@ mod tests { #[test] fn test_leap_second() { // issue #123 let today = Local::today(); + let dt = today.and_hms_milli(1, 2, 59, 1000); let timestr = dt.time().to_string(); // the OS API may or may not support the leap second, @@ -165,8 +177,10 @@ mod tests { assert!(timestr == "01:02:60" || timestr == "01:03:00", "unexpected timestr {:?}", timestr); - // this case, while unsupported by most APIs, should *not* panic. - let _ = today.and_hms_milli_opt(1, 2, 3, 1000); + let dt = today.and_hms_milli(1, 2, 3, 1234); + let timestr = dt.time().to_string(); + assert!(timestr == "01:02:03.234" || timestr == "01:02:04.234", + "unexpected timestr {:?}", timestr); } } From 7b17d68474fa28a1df1cb562ab153d8ef1376bd0 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 05:51:31 +0900 Subject: [PATCH 23/53] Fixed warnings in the nightly. --- src/format/mod.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 360c3c5..efd6f76 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -101,11 +101,25 @@ pub enum Numeric { } /// An opaque type representing numeric item types for internal uses only. -#[derive(Clone, PartialEq, Eq)] pub struct InternalNumeric { _dummy: Void, } +impl Clone for InternalNumeric { + fn clone(&self) -> Self { + match self._dummy {} + } +} + +impl PartialEq for InternalNumeric { + fn eq(&self, _other: &InternalNumeric) -> bool { + match self._dummy {} + } +} + +impl Eq for InternalNumeric { +} + impl fmt::Debug for InternalNumeric { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") @@ -188,11 +202,25 @@ pub enum Fixed { } /// An opaque type representing fixed-format item types for internal uses only. -#[derive(Clone, PartialEq, Eq)] pub struct InternalFixed { _dummy: Void, } +impl Clone for InternalFixed { + fn clone(&self) -> Self { + match self._dummy {} + } +} + +impl PartialEq for InternalFixed { + fn eq(&self, _other: &InternalFixed) -> bool { + match self._dummy {} + } +} + +impl Eq for InternalFixed { +} + impl fmt::Debug for InternalFixed { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") From db9b98f691c858ab0929accff11263e1dcedbeb0 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 7 Feb 2017 05:38:38 +0900 Subject: [PATCH 24/53] 0.3.0: Planned breaking changes to the existing interface. So this is a much delayed major release, but this should not really change how you use Chrono---only the "required" breakages have been done (e.g. anything hindering API evolution). The "big" release used to be 0.3, but due to the dependency changes we are forced to push that to 0.4. I've took this opportunity to push all known planned breaking changes to 0.3, so this should be quite stable for a moment. See `CHANGELOG.md` for the full list of changes, but most importantly: - `chrono::prelude` module has been added for proper glob imports. - `FixedOffset` is now the official "value" type for time zone offsets. - Serde 0.9 support has landed, and serialization format used by rustc-serialize and Serde has been now synchronized. - Formatting items have been slightly adjusted to be future-proof. Fixes #126. --- AUTHORS.txt | 1 + CHANGELOG.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +-- README.md | 70 ++++++++++++++++++++-------------------- src/lib.rs | 10 +++--- src/naive/time.rs | 2 +- 6 files changed, 126 insertions(+), 43 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index cd72901..f23d3eb 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -21,6 +21,7 @@ Jisoo Park Joe Wilm John Heitmann John Nagle +Jonas mg János Illés Ken Tossell Martin Risell Lilja diff --git a/CHANGELOG.md b/CHANGELOG.md index 93f1dd2..1915bf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,90 @@ Chrono obeys the principle of [Semantic Versioning](http://semver.org/). There were/are numerous minor versions before 1.0 due to the language changes. Versions with only mechnical changes will be omitted from the following list. +## 0.3.0 (2017-02-07) + +The project has moved to the [Chronotope](https://github.com/chronotope/) organization. + +### Added + +- `chrono::prelude` module has been added. All other glob imports are now discouraged. + +- `FixedOffset` can be added to or subtracted from any timelike types. + + - `FixedOffset::local_minus_utc` and `FixedOffset::utc_minus_local` methods have been added. + Note that the old `Offset::local_minus_utc` method is gone; see below. + +- Serde support for non-self-describing formats like Bincode is added. (#89) + +- Added `Item::Owned{Literal,Space}` variants for owned formatting items. (#76) + +- Formatting items and the `Parsed` type have been slightly adjusted so that + they can be internally extended without breaking any compatibility. + +- `Weekday` is now `Hash`able. (#109) + +- `ParseError` now implements `Eq` as well as `PartialEq`. (#114) + +- More documentation improvements. (#101, #108, #112) + +### Changed + +- Chrono now only supports Rust 1.13.0 or later (previously: Rust 1.8.0 or later). + +- Serde 0.9 is now supported. + Due to the API difference, support for 0.8 or older is discontinued. (#122) + +- Rustc-serialize implementations are now on par with corresponding Serde implementations. + They both standardize on the `std::fmt::Debug` textual output. + + **This is a silent breaking change (hopefully the last though).** + You should be prepared for the format change if you depended on rustc-serialize. + +- `Offset::local_minus_utc` is now `Offset::fix`, and returns `FixedOffset` instead of a duration. + + This makes every time zone operation operate within a bias less than one day, + and vastly simplifies many logics. + +- `chrono::format::format` now receives `FixedOffset` instead of `time::Duration`. + +- The following methods and implementations have been renamed and older names have been *removed*. + The older names will be reused for the same methods with `std::time::Duration` in the future. + + - `checked_*` → `checked_*_signed` in `Date`, `DateTime`, `NaiveDate` and `NaiveDateTime` types + + - `overflowing_*` → `overflowing_*_signed` in the `NaiveTime` type + + - All subtraction implementations between two time instants have been moved to + `signed_duration_since`, following the naming in `std::time`. + +### Fixed + +- Fixed a panic when the `Local` offset receives a leap second. (#123) + +### Removed + +- Rustc-serialize support for `Date` types and all offset types has been dropped. + + These implementations were automatically derived and never had been in a good shape. + Moreover there are no corresponding Serde implementations, limiting their usefulness. + In the future they may be revived with more complete implementations. + +- The following method aliases deprecated in the 0.2 branch have been removed. + + - `DateTime::num_seconds_from_unix_epoch` (→ `DateTime::timestamp`) + - `NaiveDateTime::from_num_seconds_from_unix_epoch` (→ `NaiveDateTime::from_timestamp`) + - `NaiveDateTime::from_num_seconds_from_unix_epoch_opt` (→ `NaiveDateTime::from_timestamp_opt`) + - `NaiveDateTime::num_seconds_unix_epoch` (→ `NaiveDateTime::timestamp`) + +- Formatting items are no longer `Copy`, except for `chrono::format::Pad`. + +- `chrono::offset::add_with_leapsecond` has been removed. + Use a direct addition with `FixedOffset` instead. + ## 0.2.25 (2016-08-04) +This is the last version officially supports Rust 1.12.0 or older. + (0.2.24 was accidentally uploaded without a proper check for warnings in the default state, and replaced by 0.2.25 very shortly. Duh.) diff --git a/Cargo.toml b/Cargo.toml index 328cb55..51d71c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chrono" -version = "0.2.25" +version = "0.3.0" authors = ["Kang Seonghoon "] description = "Date and time library for Rust" @@ -20,7 +20,7 @@ appveyor = { repository = "chronotope/chrono" } name = "chrono" [dependencies] -time = "0.1" +time = "^0.1.36" num = { version = "0.1", default-features = false } rustc-serialize = { version = "0.3", optional = true } serde = { version = "0.9", optional = true } diff --git a/README.md b/README.md index 1ceaf3a..2519149 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[Chrono][docsrs] 0.2.25 -======================= +[Chrono][docsrs] 0.3.0 +====================== [![Chrono on Travis CI][travis-image]][travis] [![Chrono on Appveyor][appveyor-image]][appveyor] @@ -12,8 +12,8 @@ [appveyor]: https://ci.appveyor.com/project/chronotope/chrono [cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio]: https://crates.io/crates/chrono -[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.2.25 -[docsrs]: https://docs.rs/chrono/0.2.25/ +[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.3.0 +[docsrs]: https://docs.rs/chrono/0.3.0/ Date and time handling for Rust. @@ -40,7 +40,7 @@ Put this in your `Cargo.toml`: ```toml [dependencies] -chrono = "0.2" +chrono = "0.3" ``` Or, if you want [Serde](https://github.com/serde-rs/serde) or @@ -49,7 +49,7 @@ include the features like this: ```toml [dependencies] -chrono = { version = "0.2", features = ["serde", "rustc-serialize"] } +chrono = { version = "0.3", features = ["serde", "rustc-serialize"] } ``` Then put this in your crate root: @@ -90,7 +90,7 @@ methods. ### Date and Time Chrono provides a -[**`DateTime`**](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html) +[**`DateTime`**](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html) type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping @@ -101,15 +101,15 @@ which tracks your system clock, or is an opaque but monotonically-increasing representation of a moment in time. `DateTime` is timezone-aware and must be constructed from -the [**`TimeZone`**](https://docs.rs/chrono/0.2.25/chrono/offset/trait.TimeZone.html) object, +the [**`TimeZone`**](https://docs.rs/chrono/0.3.0/chrono/offset/trait.TimeZone.html) object, which defines how the local date is converted to and back from the UTC date. There are three well-known `TimeZone` implementations: -* [**`UTC`**](https://docs.rs/chrono/0.2.25/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. +* [**`UTC`**](https://docs.rs/chrono/0.3.0/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. -* [**`Local`**](https://docs.rs/chrono/0.2.25/chrono/offset/local/struct.Local.html) specifies the system local time zone. +* [**`Local`**](https://docs.rs/chrono/0.3.0/chrono/offset/local/struct.Local.html) specifies the system local time zone. -* [**`FixedOffset`**](https://docs.rs/chrono/0.2.25/chrono/offset/fixed/struct.FixedOffset.html) specifies +* [**`FixedOffset`**](https://docs.rs/chrono/0.3.0/chrono/offset/fixed/struct.FixedOffset.html) specifies an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. This often results from the parsed textual date and time. Since it stores the most information and does not depend on the system environment, @@ -117,12 +117,12 @@ There are three well-known `TimeZone` implementations: `DateTime`s with different `TimeZone` types are distinct and do not mix, but can be converted to each other using -the [`DateTime::with_timezone`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.with_timezone) method. +the [`DateTime::with_timezone`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.with_timezone) method. You can get the current date and time in the UTC time zone -([`UTC::now()`](https://docs.rs/chrono/0.2.25/chrono/offset/utc/struct.UTC.html#method.now)) +([`UTC::now()`](https://docs.rs/chrono/0.3.0/chrono/offset/utc/struct.UTC.html#method.now)) or in the local time zone -([`Local::now()`](https://docs.rs/chrono/0.2.25/chrono/offset/local/struct.Local.html#method.now)). +([`Local::now()`](https://docs.rs/chrono/0.3.0/chrono/offset/local/struct.Local.html#method.now)). ~~~~ {.rust} use chrono::prelude::*; @@ -163,8 +163,8 @@ assert_eq!(dt, fixed_dt); ~~~~ Various properties are available to the date and time, and can be altered individually. -Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.2.25/chrono/trait.Datelike.html) and -[`Timelike`](https://docs.rs/chrono/0.2.25/chrono/trait.Timelike.html) which you should `use` before. +Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.3.0/chrono/trait.Datelike.html) and +[`Timelike`](https://docs.rs/chrono/0.3.0/chrono/trait.Timelike.html) which you should `use` before. Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: @@ -205,14 +205,14 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00 UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); ~~~~ -Formatting is done via the [`format`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.format) method, +Formatting is done via the [`format`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.format) method, which format is equivalent to the familiar `strftime` format. -(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.2.25/chrono/format/strftime/index.html#specifiers) +(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.3.0/chrono/format/strftime/index.html#specifiers) for full syntax.) The default `to_string` method and `{:?}` specifier also give a reasonable representation. -Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and -[`to_rfc3339`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods +Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and +[`to_rfc3339`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods for well-known formats. ~~~~ {.rust} @@ -238,23 +238,23 @@ Parsing can be done with three methods: ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) format specifier prints, and requires the offset to be present. -2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses +2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses a date and time with offsets and returns `DateTime`. This should be used when the offset is a part of input and the caller cannot guess that. It *cannot* be used when the offset can be missing. - [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) + [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) and - [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) + [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) are similar but for well-known formats. -3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.2.25/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is +3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.3.0/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is similar but returns `DateTime` of given offset. When the explicit offset is missing from the input, it simply uses given offset. It issues an error when the input contains an explicit offset different from the current offset. More detailed control over the parsing process is available via -[`format`](https://docs.rs/chrono/0.2.25/chrono/format/index.html) module. +[`format`](https://docs.rs/chrono/0.3.0/chrono/format/index.html) module. ~~~~ {.rust} use chrono::prelude::*; @@ -288,7 +288,7 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e ### Individual date -Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.2.25/chrono/date/struct.Date.html)). +Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.3.0/chrono/date/struct.Date.html)). It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. @@ -307,26 +307,26 @@ assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_ There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. -`DateTime` has [`date`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.date) method +`DateTime` has [`date`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.date) method which returns a `Date` which represents its date component. -There is also a [`time`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.time) method, +There is also a [`time`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.time) method, which simply returns a naive local time described below. ### Naive date and time Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -as [**`NaiveDate`**](https://docs.rs/chrono/0.2.25/chrono/naive/date/struct.NaiveDate.html), -[**`NaiveTime`**](https://docs.rs/chrono/0.2.25/chrono/naive/time/struct.NaiveTime.html) and -[**`NaiveDateTime`**](https://docs.rs/chrono/0.2.25/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. +as [**`NaiveDate`**](https://docs.rs/chrono/0.3.0/chrono/naive/date/struct.NaiveDate.html), +[**`NaiveTime`**](https://docs.rs/chrono/0.3.0/chrono/naive/time/struct.NaiveTime.html) and +[**`NaiveDateTime`**](https://docs.rs/chrono/0.3.0/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. They have almost equivalent interfaces as their timezone-aware twins, but are not associated to time zones obviously and can be quite low-level. They are mostly useful for building blocks for higher-level types. Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -[`naive_local`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.naive_local) returns +[`naive_local`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.naive_local) returns a view to the naive local time, -and [`naive_utc`](https://docs.rs/chrono/0.2.25/chrono/datetime/struct.DateTime.html#method.naive_utc) returns +and [`naive_utc`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.naive_utc) returns a view to the naive UTC time. ## Limitations @@ -338,7 +338,7 @@ Date types are limited in about +/- 262,000 years from the common epoch. Time types are limited in the nanosecond accuracy. [Leap seconds are supported in the representation but -Chrono doesn't try to make use of them](https://docs.rs/chrono/0.2.25/chrono/naive/time/index.html#leap-second-handling). +Chrono doesn't try to make use of them](https://docs.rs/chrono/0.3.0/chrono/naive/time/index.html#leap-second-handling). (The main reason is that leap seconds are not really predictable.) Almost *every* operation over the possible leap seconds will ignore them. Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale @@ -349,5 +349,5 @@ Any operation that can be ambiguous will return `None` in such cases. For example, "a month later" of 2014-01-30 is not well-defined and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`. -Advanced time zone handling is not yet supported (but is planned in 0.3). +Advanced time zone handling is not yet supported (but is planned in 0.4). diff --git a/src/lib.rs b/src/lib.rs index 87bd9cd..dccc908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -//! # Chrono 0.2.25 +//! # Chrono 0.3.0 //! //! Date and time handling for Rust. //! It aims to be a feature-complete superset of @@ -26,7 +26,7 @@ //! //! ```toml //! [dependencies] -//! chrono = "0.2" +//! chrono = "0.3" //! ``` //! //! Or, if you want [Serde](https://github.com/serde-rs/serde) or @@ -35,7 +35,7 @@ //! //! ```toml //! [dependencies] -//! chrono = { version = "0.2", features = ["serde", "rustc-serialize"] } +//! chrono = { version = "0.3", features = ["serde", "rustc-serialize"] } //! ``` //! //! Then put this in your crate root: @@ -343,9 +343,9 @@ //! For example, "a month later" of 2014-01-30 is not well-defined //! and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`. //! -//! Advanced time zone handling is not yet supported (but is planned in 0.3). +//! Advanced time zone handling is not yet supported (but is planned in 0.4). -#![doc(html_root_url = "https://docs.rs/chrono/0.2.25/")] +#![doc(html_root_url = "https://docs.rs/chrono/0.3.0/")] #![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507 #![deny(missing_docs)] diff --git a/src/naive/time.rs b/src/naive/time.rs index 2d9c7bc..7becf85 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -34,7 +34,7 @@ //! If you cannot tolerate this behavior, //! you must use a separate `TimeZone` for the International Atomic Time (TAI). //! TAI is like UTC but has no leap seconds, and thus slightly differs from UTC. -//! Chrono 0.2 does not provide such implementation, but it is planned for 0.3. +//! Chrono 0.3 does not provide such implementation, but it is planned for 0.4. //! //! ## Representing Leap Seconds //! From 73a3ec5514ff615fcc609bc373104abf8bb35c9c Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 14 Feb 2017 03:07:06 +0900 Subject: [PATCH 25/53] Fixed a bug that the leap second can be mapped wrongly in the local tz. Turns out to be a relic of pre-FixedOffset offset calculation code. Only manifests itself when the local time zone is behind UTC. Fixes #128. --- src/offset/local.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/offset/local.rs b/src/offset/local.rs index dd037f8..800f1c9 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -4,7 +4,6 @@ //! The local (system) time zone. use oldtime; -use oldtime::Duration as OldDuration; use {Datelike, Timelike}; use naive::date::NaiveDate; @@ -39,7 +38,7 @@ fn tm_to_datetime(mut tm: oldtime::Tm) -> DateTime { let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32, tm.tm_sec as u32, tm.tm_nsec as u32); let offset = FixedOffset::east(tm.tm_utcoff); - DateTime::from_utc(date.and_time(time) + OldDuration::seconds(-tm.tm_utcoff as i64), offset) + DateTime::from_utc(date.and_time(time) - offset, offset) } /// Converts a local `NaiveDateTime` to the `time::Timespec`. From 1afa60f377e8886e966bc9ddc4464f9ac005fdd6 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 14 Feb 2017 03:15:07 +0900 Subject: [PATCH 26/53] Test with multiple time zones. Previously Chrono was mainly tested in UTC (Travis) and UTC+9 (the maintainer's local time zone), but we have frequently seen bugs that only appear in time zones behind UTC. This commit makes the test run all unit tests three times, in different time zones (UTC, UTC+9:30 and UTC-4 for now---the choice is mostly arbitrary but tries to cover DST and half-hour offsets for the future). Travis already ran them three times (for rust-serialize and Serde) so this won't slow the CI down. Closes #130. --- .travis.yml | 7 ++++--- Makefile | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index beb65cc..69920fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,13 +17,14 @@ env: - LD_LIBRARY_PATH: /usr/local/lib script: # interleave building and testing in hope that it saves time + # also vary the local time zone to (hopefully) catch tz-dependent bugs # also avoid doc-testing multiple times---it takes a lot and rarely helps - cargo build -v - - cargo test -v + - TZ=ACST-9:30 cargo test -v - cargo build -v --features rustc-serialize - - cargo test -v --features rustc-serialize --lib + - TZ=EST4 cargo test -v --features rustc-serialize --lib - cargo build -v --features 'serde bincode' - - cargo test -v --features 'serde bincode' --lib + - TZ=UTC0 cargo test -v --features 'serde bincode' --lib notifications: email: false irc: diff --git a/Makefile b/Makefile index e39ffcf..40c5fc1 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,9 @@ README.md: src/lib.rs .PHONY: test test: - cargo test --features 'serde rustc-serialize bincode' + TZ=UTC0 cargo test --features 'serde rustc-serialize bincode' --lib + TZ=ACST-9:30 cargo test --features 'serde rustc-serialize bincode' --lib + TZ=EST4 cargo test --features 'serde rustc-serialize bincode' .PHONY: doc doc: authors readme From d036c0737059fe002463c10756553393bcbcc440 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 14 Feb 2017 03:28:14 +0900 Subject: [PATCH 27/53] Bincode 1.0.0-alpha2 no longer has the `bincode::serde` module. --- Cargo.toml | 2 +- src/datetime.rs | 3 +-- src/naive/date.rs | 3 +-- src/naive/datetime.rs | 3 +-- src/naive/time.rs | 3 +-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 51d71c3..02f5f78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,4 @@ serde = { version = "0.9", optional = true } [dev-dependencies] serde_json = { version = ">=0.9.0" } -bincode = { version = "1.0.0-alpha1", features = ["serde"], default-features = false } +bincode = { version = "1.0.0-alpha2", features = ["serde"], default-features = false } diff --git a/src/datetime.rs b/src/datetime.rs index c88d4c9..702770a 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -584,8 +584,7 @@ mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::SizeLimit; - use self::bincode::serde::{serialize, deserialize}; + use self::bincode::{SizeLimit, serialize, deserialize}; let dt = UTC.ymd(2014, 7, 24).and_hms(12, 34, 6); let encoded = serialize(&dt, SizeLimit::Infinite).unwrap(); diff --git a/src/naive/date.rs b/src/naive/date.rs index f8fff8f..53b7686 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1616,8 +1616,7 @@ mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::SizeLimit; - use self::bincode::serde::{serialize, deserialize}; + use self::bincode::{SizeLimit, serialize, deserialize}; let d = NaiveDate::from_ymd(2014, 7, 24); let encoded = serialize(&d, SizeLimit::Infinite).unwrap(); diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 81ac8af..0839b0e 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1515,8 +1515,7 @@ mod serde { // Bincode is relevant to test separately from JSON because // it is not self-describing. use naive::date::NaiveDate; - use self::bincode::SizeLimit; - use self::bincode::serde::{serialize, deserialize}; + use self::bincode::{SizeLimit, serialize, deserialize}; let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90); let encoded = serialize(&dt, SizeLimit::Infinite).unwrap(); diff --git a/src/naive/time.rs b/src/naive/time.rs index 7becf85..8019b04 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1392,8 +1392,7 @@ mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::SizeLimit; - use self::bincode::serde::{serialize, deserialize}; + use self::bincode::{SizeLimit, serialize, deserialize}; let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); let encoded = serialize(&t, SizeLimit::Infinite).unwrap(); From 3d36feb234037d4e88d71dc30692774e600d0a6f Mon Sep 17 00:00:00 2001 From: Eric Findlay Date: Mon, 20 Feb 2017 16:04:32 +0900 Subject: [PATCH 28/53] Corrected spelling error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2519149..d2d9905 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ In particular, * 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: +which Chrono builds upon and should acknowledge: * [Initial research on the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md) From 701a3a548ac591978852b16570c270df6c9f487f Mon Sep 17 00:00:00 2001 From: Eric Findlay Date: Tue, 21 Feb 2017 11:52:05 +0900 Subject: [PATCH 29/53] Corrected spelling error. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index dccc908..c63b511 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! * 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: +//! which Chrono builds upon and should acknowledge: //! //! * [Initial research on //! the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md) From 5d07a5d9c10ca4ac0c7397802f1c31a1e0f6a6b1 Mon Sep 17 00:00:00 2001 From: Frans Skarman Date: Mon, 10 Apr 2017 22:24:32 +0200 Subject: [PATCH 30/53] Update code snippets to use syntax highlighting --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d2d9905..660b7a4 100644 --- a/README.md +++ b/README.md @@ -124,18 +124,18 @@ You can get the current date and time in the UTC time zone or in the local time zone ([`Local::now()`](https://docs.rs/chrono/0.3.0/chrono/offset/local/struct.Local.html#method.now)). -~~~~ {.rust} +```rust use chrono::prelude::*; 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` -~~~~ +``` 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} +```rust use chrono::prelude::*; use chrono::offset::LocalResult; @@ -160,7 +160,7 @@ assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12); assert_eq!(dt, fixed_dt); -~~~~ +``` Various properties are available to the date and time, and can be altered individually. Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.3.0/chrono/trait.Datelike.html) and @@ -168,7 +168,7 @@ Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.3.0 Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: -~~~~ {.rust} +```rust use chrono::prelude::*; use time::Duration; @@ -203,7 +203,7 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_00 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`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.format) method, which format is equivalent to the familiar `strftime` format. @@ -215,7 +215,7 @@ Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.3.0/chrono/datetime [`to_rfc3339`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods for well-known formats. -~~~~ {.rust} +```rust use chrono::prelude::*; let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); @@ -227,7 +227,7 @@ assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC"); assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000"); assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00"); assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); -~~~~ +``` Parsing can be done with three methods: @@ -256,7 +256,7 @@ Parsing can be done with three methods: More detailed control over the parsing process is available via [`format`](https://docs.rs/chrono/0.3.0/chrono/format/index.html) module. -~~~~ {.rust} +```rust use chrono::prelude::*; let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); @@ -284,7 +284,7 @@ assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()) assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); // oops, the weekday is incorrect! assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); -~~~~ +``` ### Individual date @@ -292,7 +292,7 @@ Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chron It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. -~~~~ {.rust} +```rust use chrono::prelude::*; use chrono::offset::LocalResult; @@ -303,7 +303,7 @@ assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri); assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None); assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809"); -~~~~ +``` There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. From 1583df95b548bad1d76e4cc65811d2196c2f9aab Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 19 Apr 2017 00:34:10 +0900 Subject: [PATCH 31/53] Upgrade bincode dev-dependency to >=1.0.0-alpha6. The test depends on the bincode API changed at that version. --- Cargo.toml | 2 +- src/datetime.rs | 4 ++-- src/naive/date.rs | 4 ++-- src/naive/datetime.rs | 4 ++-- src/naive/time.rs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02f5f78..90d1d31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,4 @@ serde = { version = "0.9", optional = true } [dev-dependencies] serde_json = { version = ">=0.9.0" } -bincode = { version = "1.0.0-alpha2", features = ["serde"], default-features = false } +bincode = { version = "1.0.0-alpha6", features = ["serde"], default-features = false } diff --git a/src/datetime.rs b/src/datetime.rs index 702770a..9052b81 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -584,10 +584,10 @@ mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::{SizeLimit, serialize, deserialize}; + use self::bincode::{Infinite, serialize, deserialize}; let dt = UTC.ymd(2014, 7, 24).and_hms(12, 34, 6); - let encoded = serialize(&dt, SizeLimit::Infinite).unwrap(); + let encoded = serialize(&dt, Infinite).unwrap(); let decoded: DateTime = deserialize(&encoded).unwrap(); assert_eq!(dt, decoded); assert_eq!(dt.offset(), decoded.offset()); diff --git a/src/naive/date.rs b/src/naive/date.rs index 53b7686..90ee26c 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1616,10 +1616,10 @@ mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::{SizeLimit, serialize, deserialize}; + use self::bincode::{Infinite, serialize, deserialize}; let d = NaiveDate::from_ymd(2014, 7, 24); - let encoded = serialize(&d, SizeLimit::Infinite).unwrap(); + let encoded = serialize(&d, Infinite).unwrap(); let decoded: NaiveDate = deserialize(&encoded).unwrap(); assert_eq!(d, decoded); } diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 0839b0e..33ba752 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1515,10 +1515,10 @@ mod serde { // Bincode is relevant to test separately from JSON because // it is not self-describing. use naive::date::NaiveDate; - use self::bincode::{SizeLimit, serialize, deserialize}; + use self::bincode::{Infinite, serialize, deserialize}; let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90); - let encoded = serialize(&dt, SizeLimit::Infinite).unwrap(); + let encoded = serialize(&dt, Infinite).unwrap(); let decoded: NaiveDateTime = deserialize(&encoded).unwrap(); assert_eq!(dt, decoded); } diff --git a/src/naive/time.rs b/src/naive/time.rs index 8019b04..a463eaf 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1392,10 +1392,10 @@ mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::{SizeLimit, serialize, deserialize}; + use self::bincode::{Infinite, serialize, deserialize}; let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); - let encoded = serialize(&t, SizeLimit::Infinite).unwrap(); + let encoded = serialize(&t, Infinite).unwrap(); let decoded: NaiveTime = deserialize(&encoded).unwrap(); assert_eq!(t, decoded); } From 80c3582531284426bef157403a6af5428d1e2566 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 21 Apr 2017 21:12:26 +1000 Subject: [PATCH 32/53] upgrade to serde 1.0 --- Cargo.toml | 6 +++--- src/datetime.rs | 28 ++++++++++++++-------------- src/naive/date.rs | 10 +++++----- src/naive/datetime.rs | 10 +++++----- src/naive/time.rs | 10 +++++----- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90d1d31..6196612 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,8 @@ name = "chrono" time = "^0.1.36" num = { version = "0.1", default-features = false } rustc-serialize = { version = "0.3", optional = true } -serde = { version = "0.9", optional = true } +serde = { version = "1", optional = true } [dev-dependencies] -serde_json = { version = ">=0.9.0" } -bincode = { version = "1.0.0-alpha6", features = ["serde"], default-features = false } +serde_json = { version = "1" } +bincode = { version = "0.7.0", features = ["serde"], default-features = false, git = "https://github.com/TyOverby/bincode.git" } diff --git a/src/datetime.rs b/src/datetime.rs index 9052b81..0a1fd14 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -416,11 +416,11 @@ fn test_encodable_json(to_string_utc: FUTC, to_string_fixed: FF #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(utc_from_str: FUTC, - fixed_from_str: FFixed, - local_from_str: FLocal) - where FUTC: Fn(&str) -> Result, E>, - FFixed: Fn(&str) -> Result, E>, - FLocal: Fn(&str) -> Result, E>, + fixed_from_str: FFixed, + local_from_str: FLocal) + where FUTC: for<'de> Fn(&'de str) -> Result, E>, + FFixed: for<'de> Fn(&'de str) -> Result, E>, + FLocal: for<'de> Fn(&'de str) -> Result, E>, E: ::std::fmt::Debug { // should check against the offset as well (the normal DateTime comparison will ignore them) @@ -527,7 +527,7 @@ mod serde { struct DateTimeVisitor; - impl de::Visitor for DateTimeVisitor { + impl<'de> de::Visitor<'de> for DateTimeVisitor { type Value = DateTime; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result @@ -542,25 +542,25 @@ mod serde { } } - impl de::Deserialize for DateTime { + impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(DateTimeVisitor) } } - impl de::Deserialize for DateTime { + impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&UTC)) } } - impl de::Deserialize for DateTime { + impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local)) } @@ -576,8 +576,8 @@ mod serde { #[test] fn test_serde_deserialize() { - super::test_decodable_json(self::serde_json::from_str, self::serde_json::from_str, - self::serde_json::from_str); + super::test_decodable_json(|input| self::serde_json::from_str(&input), |input| self::serde_json::from_str(&input), + |input| self::serde_json::from_str(&input)); } #[test] diff --git a/src/naive/date.rs b/src/naive/date.rs index 90ee26c..30061c8 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1495,7 +1495,7 @@ fn test_encodable_json(to_string: F) #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(from_str: F) - where F: Fn(&str) -> Result, E: ::std::fmt::Debug + where F: for<'de> Fn(&'de str) -> Result, E: ::std::fmt::Debug { use std::{i32, i64}; @@ -1576,7 +1576,7 @@ mod serde { struct NaiveDateVisitor; - impl de::Visitor for NaiveDateVisitor { + impl<'de> de::Visitor<'de> for NaiveDateVisitor { type Value = NaiveDate; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result @@ -1591,9 +1591,9 @@ mod serde { } } - impl de::Deserialize for NaiveDate { + impl<'de> de::Deserialize<'de> for NaiveDate { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(NaiveDateVisitor) } @@ -1609,7 +1609,7 @@ mod serde { #[test] fn test_serde_deserialize() { - super::test_decodable_json(self::serde_json::from_str); + super::test_decodable_json(|input| self::serde_json::from_str(&input)); } #[test] diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 33ba752..f029ffb 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1371,7 +1371,7 @@ fn test_encodable_json(to_string: F) #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(from_str: F) - where F: Fn(&str) -> Result, E: ::std::fmt::Debug + where F: for<'de> Fn(&'de str) -> Result, E: ::std::fmt::Debug { use naive::date; @@ -1474,7 +1474,7 @@ mod serde { struct NaiveDateTimeVisitor; - impl de::Visitor for NaiveDateTimeVisitor { + impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor { type Value = NaiveDateTime; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result @@ -1489,9 +1489,9 @@ mod serde { } } - impl de::Deserialize for NaiveDateTime { + impl<'de> de::Deserialize<'de> for NaiveDateTime { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(NaiveDateTimeVisitor) } @@ -1507,7 +1507,7 @@ mod serde { #[test] fn test_serde_deserialize() { - super::test_decodable_json(self::serde_json::from_str); + super::test_decodable_json(|input| self::serde_json::from_str(&input)); } #[test] diff --git a/src/naive/time.rs b/src/naive/time.rs index a463eaf..89e56bc 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1261,7 +1261,7 @@ fn test_encodable_json(to_string: F) #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(from_str: F) - where F: Fn(&str) -> Result, E: ::std::fmt::Debug + where F: for<'de> Fn(&'de str) -> Result, E: ::std::fmt::Debug { assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms(0, 0, 0))); @@ -1352,7 +1352,7 @@ mod serde { struct NaiveTimeVisitor; - impl de::Visitor for NaiveTimeVisitor { + impl<'de> de::Visitor<'de> for NaiveTimeVisitor { type Value = NaiveTime; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result @@ -1367,9 +1367,9 @@ mod serde { } } - impl de::Deserialize for NaiveTime { + impl<'de> de::Deserialize<'de> for NaiveTime { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(NaiveTimeVisitor) } @@ -1385,7 +1385,7 @@ mod serde { #[test] fn test_serde_deserialize() { - super::test_decodable_json(self::serde_json::from_str); + super::test_decodable_json(|input| self::serde_json::from_str(&input)); } #[test] From 5504f6c854edaee806cb28a752996ef292caa785 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 21 Apr 2017 21:31:50 +1000 Subject: [PATCH 33/53] optimise serialize --- src/datetime.rs | 12 +++++++++++- src/naive/date.rs | 12 +++++++++++- src/naive/datetime.rs | 12 +++++++++++- src/naive/time.rs | 2 +- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 0a1fd14..e3c3670 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -520,8 +520,18 @@ mod serde { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { + struct FormatWrapped<'a, D: 'a> { + inner: &'a D + } + + impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } + } + // Debug formatting is correct RFC3339, and it allows Zulu. - serializer.serialize_str(&format!("{:?}", self)) + serializer.collect_str(&FormatWrapped { inner: &self }) } } diff --git a/src/naive/date.rs b/src/naive/date.rs index 30061c8..79cb3f2 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1570,7 +1570,17 @@ mod serde { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { - serializer.serialize_str(&format!("{:?}", self)) + struct FormatWrapped<'a, D: 'a> { + inner: &'a D + } + + impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } + } + + serializer.collect_str(&FormatWrapped { inner: &self }) } } diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index f029ffb..f68210e 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1468,7 +1468,17 @@ mod serde { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { - serializer.serialize_str(&format!("{:?}", self)) + struct FormatWrapped<'a, D: 'a> { + inner: &'a D + } + + impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } + } + + serializer.collect_str(&FormatWrapped { inner: &self }) } } diff --git a/src/naive/time.rs b/src/naive/time.rs index 89e56bc..7f0e7d3 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1346,7 +1346,7 @@ mod serde { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { - serializer.serialize_str(&format!("{:?}", self)) + serializer.collect_str(&self) } } From 1d1bfd93da72587a69262dbefa478bde1261c62a Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 24 Apr 2017 07:17:37 +1000 Subject: [PATCH 34/53] remove unneeded hrtb --- src/datetime.rs | 6 +++--- src/naive/date.rs | 2 +- src/naive/datetime.rs | 2 +- src/naive/time.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index e3c3670..6f6f71d 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -418,9 +418,9 @@ fn test_encodable_json(to_string_utc: FUTC, to_string_fixed: FF fn test_decodable_json(utc_from_str: FUTC, fixed_from_str: FFixed, local_from_str: FLocal) - where FUTC: for<'de> Fn(&'de str) -> Result, E>, - FFixed: for<'de> Fn(&'de str) -> Result, E>, - FLocal: for<'de> Fn(&'de str) -> Result, E>, + where FUTC: Fn(&str) -> Result, E>, + FFixed: Fn(&str) -> Result, E>, + FLocal: Fn(&str) -> Result, E>, E: ::std::fmt::Debug { // should check against the offset as well (the normal DateTime comparison will ignore them) diff --git a/src/naive/date.rs b/src/naive/date.rs index 79cb3f2..b302c37 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1495,7 +1495,7 @@ fn test_encodable_json(to_string: F) #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(from_str: F) - where F: for<'de> Fn(&'de str) -> Result, E: ::std::fmt::Debug + where F: Fn(&str) -> Result, E: ::std::fmt::Debug { use std::{i32, i64}; diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index f68210e..0271218 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1371,7 +1371,7 @@ fn test_encodable_json(to_string: F) #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(from_str: F) - where F: for<'de> Fn(&'de str) -> Result, E: ::std::fmt::Debug + where F: Fn(&str) -> Result, E: ::std::fmt::Debug { use naive::date; diff --git a/src/naive/time.rs b/src/naive/time.rs index 7f0e7d3..9157e9a 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -1261,7 +1261,7 @@ fn test_encodable_json(to_string: F) #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(from_str: F) - where F: for<'de> Fn(&'de str) -> Result, E: ::std::fmt::Debug + where F: Fn(&str) -> Result, E: ::std::fmt::Debug { assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms(0, 0, 0))); From c86f5924b071c271687f5c35cd5e281673d9b546 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 2 May 2017 02:31:02 +0900 Subject: [PATCH 35/53] Limited what `.parse::()` parses to match with scanning. Amends #113. --- src/format/mod.rs | 41 ++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 28 +++++++++++++--------------- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index efd6f76..f120014 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -4,9 +4,10 @@ //! Formatting utilities for date and time. use std::fmt; +use std::str::FromStr; use std::error::Error; -use {Datelike, Timelike}; +use {Datelike, Timelike, Weekday, ParseWeekdayError}; use div::{div_floor, mod_floor}; use offset::Offset; use offset::fixed::FixedOffset; @@ -557,3 +558,41 @@ impl<'a, I: Iterator> + Clone> fmt::Display for DelayedFormat { } } +// this implementation is here only because we need some private code from `scan` + +/// Parsing a `str` into a `Weekday` uses the format [`%W`](../../format/strftime/index.html). +/// +/// # Example +/// +/// ~~~~ +/// use chrono::Weekday; +/// +/// assert_eq!("Sunday".parse::(), Ok(Weekday::Sun)); +/// assert!("any day".parse::().is_err()); +/// ~~~~ +/// +/// The parsing is case-insensitive. +/// +/// ~~~~ +/// # use chrono::Weekday; +/// assert_eq!("mON".parse::(), Ok(Weekday::Mon)); +/// ~~~~ +/// +/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted. +/// +/// ~~~~ +/// # use chrono::Weekday; +/// assert!("thurs".parse::().is_err()); +/// ~~~~ +impl FromStr for Weekday { + type Err = ParseWeekdayError; + + fn from_str(s: &str) -> Result { + if let Ok(("", w)) = scan::short_or_long_weekday(s) { + Ok(w) + } else { + Err(ParseWeekdayError { _dummy: () }) + } + } +} + diff --git a/src/lib.rs b/src/lib.rs index 685cbf1..a6af13b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -573,25 +573,22 @@ impl num::traits::FromPrimitive for Weekday { } } -use std::str::FromStr; +use std::fmt; -impl FromStr for Weekday { - type Err = String; +/// An error resulting from reading `Weekday` value with `FromStr`. +#[derive(Clone, PartialEq)] +pub struct ParseWeekdayError { + _dummy: (), +} - fn from_str(s: &str) -> Result { - match s.to_string().to_lowercase().as_ref() { - "mon" | "monday" => Ok(Weekday::Mon), - "tue" | "tuesday" => Ok(Weekday::Tue), - "wed" | "wednesday" => Ok(Weekday::Wed), - "thu" | "thur" | "thursday" => Ok(Weekday::Thu), - "fri" | "friday" => Ok(Weekday::Fri), - "sat" | "saturday" => Ok(Weekday::Sat), - "sun" | "sunday" => Ok(Weekday::Sun), - other => Err(format!("unknown Weekday {}", other)), - } +impl fmt::Debug for ParseWeekdayError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ParseWeekdayError {{ .. }}") } } +// the actual `FromStr` implementation is in the `format` module to leverage the existing code + #[cfg(feature = "serde")] mod weekday_serde { use super::Weekday; @@ -665,7 +662,6 @@ mod weekday_serde { ("\"wednesday\"", Wed), ("\"thu\"", Thu), ("\"thursday\"", Thu), - ("\"thur\"", Thu), ("\"fri\"", Fri), ("\"friday\"", Fri), ("\"sat\"", Sat), @@ -684,6 +680,8 @@ mod weekday_serde { "\"monDAYs\"", "\"mond\"", "mon", + "\"thur\"", + "\"thurs\"", ]; for str in errors { From fcb74481ee9d24da7c8c5f7938144b2c88864314 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 2 May 2017 02:37:54 +0900 Subject: [PATCH 36/53] Updated Weekday impls for serde to 1.0. --- src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a6af13b..d5096b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -592,10 +592,11 @@ impl fmt::Debug for ParseWeekdayError { #[cfg(feature = "serde")] mod weekday_serde { use super::Weekday; + use std::fmt; use serde::{ser, de}; impl ser::Serialize for Weekday { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: S) -> Result where S: ser::Serializer { serializer.serialize_str(&format!("{:?}", self)) @@ -604,19 +605,23 @@ mod weekday_serde { struct WeekdayVisitor; - impl de::Visitor for WeekdayVisitor { + impl<'de> de::Visitor<'de> for WeekdayVisitor { type Value = Weekday; - fn visit_str(&mut self, value: &str) -> Result + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Weekday") + } + + fn visit_str(self, value: &str) -> Result where E: de::Error { - value.parse().map_err(|err| de::Error::custom(err)) + value.parse().map_err(|_| E::custom("short or long weekday names expected")) } } - impl de::Deserialize for Weekday { - fn deserialize(deserializer: &mut D) -> Result - where D: de::Deserializer + impl<'de> de::Deserialize<'de> for Weekday { + fn deserialize(deserializer: D) -> Result + where D: de::Deserializer<'de> { deserializer.deserialize_str(WeekdayVisitor) } From dcf193323b59b8c85f0be708d5ba2b6a1a1654d2 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Tue, 2 May 2017 03:04:22 +0900 Subject: [PATCH 37/53] 0.3.1: Minor maintenance release, Serde 1.0 support. - Serde 1.0 is now supported. (#142) Technically this is a breaking change, but the minor version was not effective in avoiding dependency breakages anyway (because Cargo will silently compile two versions of crates). Provided that this is likely the last breakage from Serde, we tolerate this more-than-last-minute change in this version. - `Weekday` now implements `FromStr`, `Serialize` and `Deserialize`. (#113) - Fixed a bug that the leap second can be mapped wrongly in the local tz with some conditions. (#130) - Some changes to the tests to avoid previously known issues. Note that the actually published version is very slightly different from the repository because no published version of bincode supports Serde 1.0 right now. --- AUTHORS.txt | 2 ++ CHANGELOG.md | 22 ++++++++++++++++++++++ Cargo.toml | 2 +- src/lib.rs | 26 +++++++++++++------------- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index f23d3eb..5c790cc 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -14,7 +14,9 @@ David Hewson David Ross David Tolnay David Willie +Eric Findlay Eunchong Yu +Frans Skarman Huon Wilson Jim Turner Jisoo Park diff --git a/CHANGELOG.md b/CHANGELOG.md index 1915bf5..30b7381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ Chrono obeys the principle of [Semantic Versioning](http://semver.org/). There were/are numerous minor versions before 1.0 due to the language changes. Versions with only mechnical changes will be omitted from the following list. +## 0.3.1 (2017-05-02) + +### Added + +- `Weekday` now implements `FromStr`, `Serialize` and `Deserialize`. (#113) + + The syntax is identical to `%A`, i.e. either the shortest or the longest form of English names. + +### Changed + +- Serde 1.0 is now supported. (#142) + + This is technically a breaking change because Serde 0.9 and 1.0 are not compatible, + but this time we decided not to issue a minor version because + we have already seen Serde 0.8 and 0.9 compatibility problems even after 0.3.0 and + a new minor version turned out to be not very helpful for this kind of issues. + +### Fixed + +- Fixed a bug that the leap second can be mapped wrongly in the local time zone. + Only occurs when the local time zone is behind UTC. (#130) + ## 0.3.0 (2017-02-07) The project has moved to the [Chronotope](https://github.com/chronotope/) organization. diff --git a/Cargo.toml b/Cargo.toml index 6196612..31eb1bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chrono" -version = "0.3.0" +version = "0.3.1" authors = ["Kang Seonghoon "] description = "Date and time library for Rust" diff --git a/src/lib.rs b/src/lib.rs index d5096b4..0b66e91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -//! # Chrono 0.3.0 +//! # Chrono 0.3.1 //! //! Date and time handling for Rust. //! It aims to be a feature-complete superset of @@ -110,19 +110,19 @@ //! or in the local time zone //! ([`Local::now()`](./offset/local/struct.Local.html#method.now)). //! -//! ~~~~ {.rust} +//! ```rust //! use chrono::prelude::*; //! //! 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} +//! ```rust //! use chrono::prelude::*; //! use chrono::offset::LocalResult; //! @@ -148,7 +148,7 @@ //! let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12); //! assert_eq!(dt, fixed_dt); //! # let _ = local_dt; -//! ~~~~ +//! ``` //! //! Various properties are available to the date and time, and can be altered individually. //! Most of them are defined in the traits [`Datelike`](./trait.Datelike.html) and @@ -156,7 +156,7 @@ //! Addition and subtraction is also supported. //! The following illustrates most supported operations to the date and time: //! -//! ~~~~ {.rust} +//! ```rust //! # extern crate chrono; extern crate time; fn main() { //! use chrono::prelude::*; //! use time::Duration; @@ -196,7 +196,7 @@ //! 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`](./datetime/struct.DateTime.html#method.format) method, //! which format is equivalent to the familiar `strftime` format. @@ -208,7 +208,7 @@ //! [`to_rfc3339`](./datetime/struct.DateTime.html#method.to_rfc3339) methods //! for well-known formats. //! -//! ~~~~ {.rust} +//! ```rust //! use chrono::prelude::*; //! //! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); @@ -220,7 +220,7 @@ //! assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000"); //! assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00"); //! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); -//! ~~~~ +//! ``` //! //! Parsing can be done with three methods: //! @@ -249,7 +249,7 @@ //! More detailed control over the parsing process is available via //! [`format`](./format/index.html) module. //! -//! ~~~~ {.rust} +//! ```rust //! use chrono::prelude::*; //! //! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); @@ -277,7 +277,7 @@ //! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); //! // oops, the weekday is incorrect! //! assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); -//! ~~~~ +//! ``` //! //! ### Individual date //! @@ -285,7 +285,7 @@ //! It also has time zones attached, and have to be constructed via time zones. //! Most operations available to `DateTime` are also available to `Date` whenever appropriate. //! -//! ~~~~ {.rust} +//! ```rust //! use chrono::prelude::*; //! use chrono::offset::LocalResult; //! @@ -297,7 +297,7 @@ //! assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None); //! assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), //! "070809"); -//! ~~~~ +//! ``` //! //! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. //! From ede06d6beb6d38130475a0c31c2149369e2ed2d3 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 4 May 2017 16:53:53 -0400 Subject: [PATCH 38/53] date: mention that `NaiveDate` is better for serialization Fixes #92. --- src/date.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/date.rs b/src/date.rs index a88ac18..d6fe6e3 100644 --- a/src/date.rs +++ b/src/date.rs @@ -21,6 +21,7 @@ use format::{Item, DelayedFormat, StrftimeItems}; /// /// This type should be considered ambiguous at best, /// due to the inherent lack of precision required for the time zone resolution. +/// For serialization and deserialization uses, it is best to use `NaiveDate` instead. /// There are some guarantees on the usage of `Date`: /// /// - If properly constructed via `TimeZone::ymd` and others without an error, From a91981af026052d6a1b9591f3c7f18a46842a5c5 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Fri, 5 May 2017 19:06:45 +0900 Subject: [PATCH 39/53] Bump the bincode dependency. Fixes the CI test. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 31eb1bf..3f407ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,4 @@ serde = { version = "1", optional = true } [dev-dependencies] serde_json = { version = "1" } -bincode = { version = "0.7.0", features = ["serde"], default-features = false, git = "https://github.com/TyOverby/bincode.git" } +bincode = { version = "0.8.0", features = ["serde"], default-features = false } From 52de957c6074acf6ba0238ad3c76055a08184e61 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 11 May 2017 01:50:19 +0900 Subject: [PATCH 40/53] I forgot to update the README and docs link (whoops). Fixes #149. --- README.md | 62 +++++++++++++++++++++++++++--------------------------- src/lib.rs | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 660b7a4..528dd3a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[Chrono][docsrs] 0.3.0 +[Chrono][docsrs] 0.3.1 ====================== [![Chrono on Travis CI][travis-image]][travis] @@ -12,8 +12,8 @@ [appveyor]: https://ci.appveyor.com/project/chronotope/chrono [cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio]: https://crates.io/crates/chrono -[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.3.0 -[docsrs]: https://docs.rs/chrono/0.3.0/ +[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.3.1 +[docsrs]: https://docs.rs/chrono/0.3.1/ Date and time handling for Rust. @@ -90,7 +90,7 @@ methods. ### Date and Time Chrono provides a -[**`DateTime`**](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html) +[**`DateTime`**](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html) type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping @@ -101,15 +101,15 @@ which tracks your system clock, or is an opaque but monotonically-increasing representation of a moment in time. `DateTime` is timezone-aware and must be constructed from -the [**`TimeZone`**](https://docs.rs/chrono/0.3.0/chrono/offset/trait.TimeZone.html) object, +the [**`TimeZone`**](https://docs.rs/chrono/0.3.1/chrono/offset/trait.TimeZone.html) object, which defines how the local date is converted to and back from the UTC date. There are three well-known `TimeZone` implementations: -* [**`UTC`**](https://docs.rs/chrono/0.3.0/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. +* [**`UTC`**](https://docs.rs/chrono/0.3.1/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. -* [**`Local`**](https://docs.rs/chrono/0.3.0/chrono/offset/local/struct.Local.html) specifies the system local time zone. +* [**`Local`**](https://docs.rs/chrono/0.3.1/chrono/offset/local/struct.Local.html) specifies the system local time zone. -* [**`FixedOffset`**](https://docs.rs/chrono/0.3.0/chrono/offset/fixed/struct.FixedOffset.html) specifies +* [**`FixedOffset`**](https://docs.rs/chrono/0.3.1/chrono/offset/fixed/struct.FixedOffset.html) specifies an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. This often results from the parsed textual date and time. Since it stores the most information and does not depend on the system environment, @@ -117,12 +117,12 @@ There are three well-known `TimeZone` implementations: `DateTime`s with different `TimeZone` types are distinct and do not mix, but can be converted to each other using -the [`DateTime::with_timezone`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.with_timezone) method. +the [`DateTime::with_timezone`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.with_timezone) method. You can get the current date and time in the UTC time zone -([`UTC::now()`](https://docs.rs/chrono/0.3.0/chrono/offset/utc/struct.UTC.html#method.now)) +([`UTC::now()`](https://docs.rs/chrono/0.3.1/chrono/offset/utc/struct.UTC.html#method.now)) or in the local time zone -([`Local::now()`](https://docs.rs/chrono/0.3.0/chrono/offset/local/struct.Local.html#method.now)). +([`Local::now()`](https://docs.rs/chrono/0.3.1/chrono/offset/local/struct.Local.html#method.now)). ```rust use chrono::prelude::*; @@ -163,8 +163,8 @@ assert_eq!(dt, fixed_dt); ``` Various properties are available to the date and time, and can be altered individually. -Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.3.0/chrono/trait.Datelike.html) and -[`Timelike`](https://docs.rs/chrono/0.3.0/chrono/trait.Timelike.html) which you should `use` before. +Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.3.1/chrono/trait.Datelike.html) and +[`Timelike`](https://docs.rs/chrono/0.3.1/chrono/trait.Timelike.html) which you should `use` before. Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: @@ -205,14 +205,14 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00 UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); ``` -Formatting is done via the [`format`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.format) method, +Formatting is done via the [`format`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.format) method, which format is equivalent to the familiar `strftime` format. -(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.3.0/chrono/format/strftime/index.html#specifiers) +(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.3.1/chrono/format/strftime/index.html#specifiers) for full syntax.) The default `to_string` method and `{:?}` specifier also give a reasonable representation. -Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and -[`to_rfc3339`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods +Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and +[`to_rfc3339`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods for well-known formats. ```rust @@ -238,23 +238,23 @@ Parsing can be done with three methods: ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) format specifier prints, and requires the offset to be present. -2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses +2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses a date and time with offsets and returns `DateTime`. This should be used when the offset is a part of input and the caller cannot guess that. It *cannot* be used when the offset can be missing. - [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) + [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) and - [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) + [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) are similar but for well-known formats. -3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.3.0/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is +3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.3.1/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is similar but returns `DateTime` of given offset. When the explicit offset is missing from the input, it simply uses given offset. It issues an error when the input contains an explicit offset different from the current offset. More detailed control over the parsing process is available via -[`format`](https://docs.rs/chrono/0.3.0/chrono/format/index.html) module. +[`format`](https://docs.rs/chrono/0.3.1/chrono/format/index.html) module. ```rust use chrono::prelude::*; @@ -288,7 +288,7 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e ### Individual date -Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.3.0/chrono/date/struct.Date.html)). +Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.3.1/chrono/date/struct.Date.html)). It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. @@ -307,26 +307,26 @@ assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_ There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. -`DateTime` has [`date`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.date) method +`DateTime` has [`date`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.date) method which returns a `Date` which represents its date component. -There is also a [`time`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.time) method, +There is also a [`time`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.time) method, which simply returns a naive local time described below. ### Naive date and time Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -as [**`NaiveDate`**](https://docs.rs/chrono/0.3.0/chrono/naive/date/struct.NaiveDate.html), -[**`NaiveTime`**](https://docs.rs/chrono/0.3.0/chrono/naive/time/struct.NaiveTime.html) and -[**`NaiveDateTime`**](https://docs.rs/chrono/0.3.0/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. +as [**`NaiveDate`**](https://docs.rs/chrono/0.3.1/chrono/naive/date/struct.NaiveDate.html), +[**`NaiveTime`**](https://docs.rs/chrono/0.3.1/chrono/naive/time/struct.NaiveTime.html) and +[**`NaiveDateTime`**](https://docs.rs/chrono/0.3.1/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. They have almost equivalent interfaces as their timezone-aware twins, but are not associated to time zones obviously and can be quite low-level. They are mostly useful for building blocks for higher-level types. Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -[`naive_local`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.naive_local) returns +[`naive_local`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.naive_local) returns a view to the naive local time, -and [`naive_utc`](https://docs.rs/chrono/0.3.0/chrono/datetime/struct.DateTime.html#method.naive_utc) returns +and [`naive_utc`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.naive_utc) returns a view to the naive UTC time. ## Limitations @@ -338,7 +338,7 @@ Date types are limited in about +/- 262,000 years from the common epoch. Time types are limited in the nanosecond accuracy. [Leap seconds are supported in the representation but -Chrono doesn't try to make use of them](https://docs.rs/chrono/0.3.0/chrono/naive/time/index.html#leap-second-handling). +Chrono doesn't try to make use of them](https://docs.rs/chrono/0.3.1/chrono/naive/time/index.html#leap-second-handling). (The main reason is that leap seconds are not really predictable.) Almost *every* operation over the possible leap seconds will ignore them. Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale diff --git a/src/lib.rs b/src/lib.rs index 0b66e91..54449fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,7 +345,7 @@ //! //! Advanced time zone handling is not yet supported (but is planned in 0.4). -#![doc(html_root_url = "https://docs.rs/chrono/0.3.0/")] +#![doc(html_root_url = "https://docs.rs/chrono/0.3.1/")] #![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507 #![deny(missing_docs)] From 58dcb4ffd7970f45a19f382ed90305b1f71c7b2f Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Fri, 16 Jun 2017 10:03:24 +0200 Subject: [PATCH 41/53] bincode doesn't have serde feature It is built-in. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3f407ac..0b69d56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,4 @@ serde = { version = "1", optional = true } [dev-dependencies] serde_json = { version = "1" } -bincode = { version = "0.8.0", features = ["serde"], default-features = false } +bincode = { version = "0.8.0" } From 388c04002bced0cd1c08d739e34ba70d1c95e493 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 21 Jun 2017 03:27:46 +0900 Subject: [PATCH 42/53] Bumped the version number. This marks the beginning of 0.4.0 series. - Added a mention to Chrono-tz. --- AUTHORS.txt | 1 + Cargo.toml | 2 +- README.md | 69 +++++++++++++++++++++++++++-------------------------- src/lib.rs | 11 +++++---- 4 files changed, 43 insertions(+), 40 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index 5c790cc..39225ec 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -4,6 +4,7 @@ and also the following people (in ascending order): Alex Mikhalev Alexander Bulaev Ashley Mannix +Ben Boeckel Ben Eills Brandon W Maister Colin Ray diff --git a/Cargo.toml b/Cargo.toml index 3f407ac..018469a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chrono" -version = "0.3.1" +version = "0.4.0" authors = ["Kang Seonghoon "] description = "Date and time library for Rust" diff --git a/README.md b/README.md index 528dd3a..0f987e7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[Chrono][docsrs] 0.3.1 +[Chrono][docsrs] 0.4.0 ====================== [![Chrono on Travis CI][travis-image]][travis] @@ -12,8 +12,8 @@ [appveyor]: https://ci.appveyor.com/project/chronotope/chrono [cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio]: https://crates.io/crates/chrono -[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.3.1 -[docsrs]: https://docs.rs/chrono/0.3.1/ +[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.4.0 +[docsrs]: https://docs.rs/chrono/0.4.0/ Date and time handling for Rust. @@ -40,7 +40,7 @@ Put this in your `Cargo.toml`: ```toml [dependencies] -chrono = "0.3" +chrono = "0.4" ``` Or, if you want [Serde](https://github.com/serde-rs/serde) or @@ -49,7 +49,7 @@ include the features like this: ```toml [dependencies] -chrono = { version = "0.3", features = ["serde", "rustc-serialize"] } +chrono = { version = "0.4", features = ["serde", "rustc-serialize"] } ``` Then put this in your crate root: @@ -90,7 +90,7 @@ methods. ### Date and Time Chrono provides a -[**`DateTime`**](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html) +[**`DateTime`**](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html) type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping @@ -101,15 +101,15 @@ which tracks your system clock, or is an opaque but monotonically-increasing representation of a moment in time. `DateTime` is timezone-aware and must be constructed from -the [**`TimeZone`**](https://docs.rs/chrono/0.3.1/chrono/offset/trait.TimeZone.html) object, +the [**`TimeZone`**](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.html) object, which defines how the local date is converted to and back from the UTC date. There are three well-known `TimeZone` implementations: -* [**`UTC`**](https://docs.rs/chrono/0.3.1/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. +* [**`UTC`**](https://docs.rs/chrono/0.4.0/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. -* [**`Local`**](https://docs.rs/chrono/0.3.1/chrono/offset/local/struct.Local.html) specifies the system local time zone. +* [**`Local`**](https://docs.rs/chrono/0.4.0/chrono/offset/local/struct.Local.html) specifies the system local time zone. -* [**`FixedOffset`**](https://docs.rs/chrono/0.3.1/chrono/offset/fixed/struct.FixedOffset.html) specifies +* [**`FixedOffset`**](https://docs.rs/chrono/0.4.0/chrono/offset/fixed/struct.FixedOffset.html) specifies an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. This often results from the parsed textual date and time. Since it stores the most information and does not depend on the system environment, @@ -117,12 +117,12 @@ There are three well-known `TimeZone` implementations: `DateTime`s with different `TimeZone` types are distinct and do not mix, but can be converted to each other using -the [`DateTime::with_timezone`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.with_timezone) method. +the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.with_timezone) method. You can get the current date and time in the UTC time zone -([`UTC::now()`](https://docs.rs/chrono/0.3.1/chrono/offset/utc/struct.UTC.html#method.now)) +([`UTC::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/utc/struct.UTC.html#method.now)) or in the local time zone -([`Local::now()`](https://docs.rs/chrono/0.3.1/chrono/offset/local/struct.Local.html#method.now)). +([`Local::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/local/struct.Local.html#method.now)). ```rust use chrono::prelude::*; @@ -163,8 +163,8 @@ assert_eq!(dt, fixed_dt); ``` Various properties are available to the date and time, and can be altered individually. -Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.3.1/chrono/trait.Datelike.html) and -[`Timelike`](https://docs.rs/chrono/0.3.1/chrono/trait.Timelike.html) which you should `use` before. +Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.4.0/chrono/trait.Datelike.html) and +[`Timelike`](https://docs.rs/chrono/0.4.0/chrono/trait.Timelike.html) which you should `use` before. Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: @@ -205,14 +205,14 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00 UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); ``` -Formatting is done via the [`format`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.format) method, +Formatting is done via the [`format`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.format) method, which format is equivalent to the familiar `strftime` format. -(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.3.1/chrono/format/strftime/index.html#specifiers) +(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers) for full syntax.) The default `to_string` method and `{:?}` specifier also give a reasonable representation. -Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and -[`to_rfc3339`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods +Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and +[`to_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods for well-known formats. ```rust @@ -238,23 +238,23 @@ Parsing can be done with three methods: ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) format specifier prints, and requires the offset to be present. -2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses +2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses a date and time with offsets and returns `DateTime`. This should be used when the offset is a part of input and the caller cannot guess that. It *cannot* be used when the offset can be missing. - [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) + [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) and - [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) + [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) are similar but for well-known formats. -3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.3.1/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is +3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is similar but returns `DateTime` of given offset. When the explicit offset is missing from the input, it simply uses given offset. It issues an error when the input contains an explicit offset different from the current offset. More detailed control over the parsing process is available via -[`format`](https://docs.rs/chrono/0.3.1/chrono/format/index.html) module. +[`format`](https://docs.rs/chrono/0.4.0/chrono/format/index.html) module. ```rust use chrono::prelude::*; @@ -288,7 +288,7 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e ### Individual date -Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.3.1/chrono/date/struct.Date.html)). +Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4.0/chrono/date/struct.Date.html)). It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. @@ -307,26 +307,26 @@ assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_ There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. -`DateTime` has [`date`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.date) method +`DateTime` has [`date`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.date) method which returns a `Date` which represents its date component. -There is also a [`time`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.time) method, +There is also a [`time`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.time) method, which simply returns a naive local time described below. ### Naive date and time Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -as [**`NaiveDate`**](https://docs.rs/chrono/0.3.1/chrono/naive/date/struct.NaiveDate.html), -[**`NaiveTime`**](https://docs.rs/chrono/0.3.1/chrono/naive/time/struct.NaiveTime.html) and -[**`NaiveDateTime`**](https://docs.rs/chrono/0.3.1/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. +as [**`NaiveDate`**](https://docs.rs/chrono/0.4.0/chrono/naive/date/struct.NaiveDate.html), +[**`NaiveTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/time/struct.NaiveTime.html) and +[**`NaiveDateTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. They have almost equivalent interfaces as their timezone-aware twins, but are not associated to time zones obviously and can be quite low-level. They are mostly useful for building blocks for higher-level types. Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -[`naive_local`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.naive_local) returns +[`naive_local`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.naive_local) returns a view to the naive local time, -and [`naive_utc`](https://docs.rs/chrono/0.3.1/chrono/datetime/struct.DateTime.html#method.naive_utc) returns +and [`naive_utc`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.naive_utc) returns a view to the naive UTC time. ## Limitations @@ -338,7 +338,7 @@ Date types are limited in about +/- 262,000 years from the common epoch. Time types are limited in the nanosecond accuracy. [Leap seconds are supported in the representation but -Chrono doesn't try to make use of them](https://docs.rs/chrono/0.3.1/chrono/naive/time/index.html#leap-second-handling). +Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4.0/chrono/naive/time/index.html#leap-second-handling). (The main reason is that leap seconds are not really predictable.) Almost *every* operation over the possible leap seconds will ignore them. Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale @@ -349,5 +349,6 @@ Any operation that can be ambiguous will return `None` in such cases. For example, "a month later" of 2014-01-30 is not well-defined and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`. -Advanced time zone handling is not yet supported (but is planned in 0.4). +Advanced time zone handling is not yet supported. +For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. diff --git a/src/lib.rs b/src/lib.rs index 54449fc..b771030 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -//! # Chrono 0.3.1 +//! # Chrono 0.4.0 (not yet released) //! //! Date and time handling for Rust. //! It aims to be a feature-complete superset of @@ -26,7 +26,7 @@ //! //! ```toml //! [dependencies] -//! chrono = "0.3" +//! chrono = "0.4" //! ``` //! //! Or, if you want [Serde](https://github.com/serde-rs/serde) or @@ -35,7 +35,7 @@ //! //! ```toml //! [dependencies] -//! chrono = { version = "0.3", features = ["serde", "rustc-serialize"] } +//! chrono = { version = "0.4", features = ["serde", "rustc-serialize"] } //! ``` //! //! Then put this in your crate root: @@ -343,9 +343,10 @@ //! For example, "a month later" of 2014-01-30 is not well-defined //! and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`. //! -//! Advanced time zone handling is not yet supported (but is planned in 0.4). +//! Advanced time zone handling is not yet supported. +//! For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. -#![doc(html_root_url = "https://docs.rs/chrono/0.3.1/")] +#![doc(html_root_url = "https://docs.rs/chrono/0.4.0/")] #![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507 #![deny(missing_docs)] From c0c61b5bfa6cb2553f41e8b9697355ae65488b43 Mon Sep 17 00:00:00 2001 From: Brandon W Maister Date: Sat, 4 Feb 2017 16:51:19 -0500 Subject: [PATCH 43/53] Add the ability for serde to deserialize timestamps Timestamps are defined in terms of UTC, so what this does is, if we encounter an integer instead of a str, create a FixedOffset timestamp with an offset of zero and create the timestamp from that. --- src/datetime.rs | 65 ++++++++++++++++++++++++++++++++++++++++--- src/naive/datetime.rs | 28 +++++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 6f6f71d..eb4166c 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -432,11 +432,20 @@ fn test_decodable_json(utc_from_str: FUTC, norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); assert_eq!(norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + assert_eq!(norm(&utc_from_str("0").ok()), + norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); + assert_eq!(norm(&utc_from_str("-1").unwrap()), + norm(&UTC.ymd(1969, 12, 31).and_hms(23, 59, 59))); + assert_eq!(norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6)))); assert_eq!(norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), norm(&Some(FixedOffset::east(60*60 + 23*60).ymd(2014, 7, 24).and_hms(13, 57, 6)))); + assert_eq!(norm(&fixed_from_str("0").ok()), + norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); + assert_eq!(norm(&fixed_from_str("-1").unwrap()), + norm(&UTC.ymd(1969, 12, 31).and_hms(23, 59, 59))); // we don't know the exact local offset but we can check that // the conversion didn't change the instant itself @@ -444,6 +453,10 @@ fn test_decodable_json(utc_from_str: FUTC, UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#).unwrap(), UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + assert_eq!(fixed_from_str("0").unwrap(), + UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)); + assert_eq!(local_from_str("-1").unwrap(), + &UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)); assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); @@ -508,7 +521,7 @@ mod rustc_serialize { mod serde { use std::fmt; use super::DateTime; - use offset::TimeZone; + use offset::{TimeZone, LocalResult}; use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; @@ -535,14 +548,30 @@ mod serde { } } + // try!-like function to convert a LocalResult into a serde-ish Result + fn from(me: LocalResult, ts: V) -> Result + where E: de::Error, + V: Display, + T: Display, + { + match me { + LocalResult::None => Err(E::custom( + format!("value is not a legal timestamp: {}", ts))), + LocalResult::Ambiguous(min, max) => Err(E::custom( + format!("value is an ambiguous timestamp: {}, could be either of {}, {}", + ts, min, max))), + LocalResult::Single(val) => Ok(val) + } + } + struct DateTimeVisitor; impl<'de> de::Visitor<'de> for DateTimeVisitor { type Value = DateTime; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted date and time string") + write!(formatter, "a formatted date and time string or a unix timestamp") } fn visit_str(self, value: &str) -> Result, E> @@ -550,8 +579,28 @@ mod serde { { value.parse().map_err(|err| E::custom(format!("{}", err))) } + + // Deserialize a timestamp in seconds since the epoch + fn visit_i64(self, value: i64) -> Result, E> + where E: de::Error + { + from(FixedOffset::east(0).timestamp_opt(value, 0), value) + } + + // Deserialize a timestamp in seconds since the epoch + fn visit_u64(self, value: u64) -> Result, E> + where E: de::Error + { + from(FixedOffset::east(0).timestamp_opt(value as i64, 0), value) + } + } + /// Deserialize a value that optionally includes a timezone offset in its + /// string representation + /// + /// The serialized value can be either a string representation or a unix + /// timestamp impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de> @@ -560,6 +609,10 @@ mod serde { } } + /// Deserialize into a UTC value + /// + /// The serialized value can be either a string representation or a unix + /// timestamp impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de> @@ -568,6 +621,11 @@ mod serde { } } + /// Deserialize a value that includes no timezone in its string + /// representation + /// + /// The serialized value can be either a string representation or a unix + /// timestamp impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de> @@ -785,4 +843,3 @@ mod tests { assert_eq!(1234567, datetime.timestamp_subsec_nanos()); } } - diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 0271218..9d00c2f 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1402,6 +1402,16 @@ fn test_decodable_json(from_str: F) assert_eq!( from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + assert_eq!( + from_str("0").unwrap(), + NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0), + "should parse integers as timestamps" + ); + assert_eq!( + from_str("-1").unwrap(), + NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59), + "should parse integers as timestamps" + ); // bad formats assert!(from_str(r#""""#).is_err()); @@ -1487,9 +1497,9 @@ mod serde { impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor { type Value = NaiveDateTime; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted date and time string") + write!(formatter, "a formatted date and time string or a unix timestamp") } fn visit_str(self, value: &str) -> Result @@ -1497,6 +1507,20 @@ mod serde { { value.parse().map_err(|err| E::custom(format!("{}", err))) } + + fn visit_i64(self, value: i64) -> Result + where E: de::Error + { + NaiveDateTime::from_timestamp_opt(value, 0) + .ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value))) + } + + fn visit_u64(self, value: u64) -> Result + where E: de::Error + { + NaiveDateTime::from_timestamp_opt(value as i64, 0) + .ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value))) + } } impl<'de> de::Deserialize<'de> for NaiveDateTime { From bc879d705e012319187fbd2686725da8a3192bb0 Mon Sep 17 00:00:00 2001 From: Brandon W Maister Date: Sat, 11 Feb 2017 17:21:40 -0500 Subject: [PATCH 44/53] Deserialize both Serde and Serialize from timestamps This introduces a newtype around DateTime and NaiveDateTime that deserlization is implemented for. There are two advantages to this over the previous implementation: * It is expandable to other timestamp representations (e.g. millisecond and microsecond timestamps) * It works with RustcSerialize::Decodable. AFAICT Decodable will error if you try to call more than one of the `read_*` functions in the same `decode` invocation. This is slightly annoying compared to serde which just calls the correct `visit_*` function for whatever type the deserializer encounters. On the whole I think that I prefer this to the previous implementation of deserializing timestamps (even though I don't care about RustcSerialize in the post-1.15 world) because it is much more explicit. On the other hand, this feels like it's introducing a lot of types, and possibly making downstream crates introduce a variety of different structs for ser/de and translating into different struct types. --- src/datetime.rs | 219 +++++++++++++++++++++++++++++++++--------- src/naive/datetime.rs | 97 +++++++++++++++---- 2 files changed, 255 insertions(+), 61 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index eb4166c..4679e38 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -5,7 +5,7 @@ use std::{str, fmt, hash}; use std::cmp::Ordering; -use std::ops::{Add, Sub}; +use std::ops::{Add, Sub, Deref}; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; @@ -30,6 +30,26 @@ pub struct DateTime { offset: Tz::Offset, } +/// A DateTime that can be deserialized from a timestamp +/// +/// A timestamp here is seconds since the epoch +pub struct TsSeconds(DateTime); + +impl From> for DateTime { + /// Pull the inner DateTime out + fn from(obj: TsSeconds) -> DateTime { + obj.0 + } +} + +impl Deref for TsSeconds { + type Target = DateTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl DateTime { /// Makes a new `DateTime` with given *UTC* datetime and offset. /// The local datetime should be constructed via the `TimeZone` trait. @@ -432,40 +452,61 @@ fn test_decodable_json(utc_from_str: FUTC, norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); assert_eq!(norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); - assert_eq!(norm(&utc_from_str("0").ok()), - norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); - assert_eq!(norm(&utc_from_str("-1").unwrap()), - norm(&UTC.ymd(1969, 12, 31).and_hms(23, 59, 59))); - assert_eq!(norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6)))); assert_eq!(norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), norm(&Some(FixedOffset::east(60*60 + 23*60).ymd(2014, 7, 24).and_hms(13, 57, 6)))); - assert_eq!(norm(&fixed_from_str("0").ok()), - norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); - assert_eq!(norm(&fixed_from_str("-1").unwrap()), - norm(&UTC.ymd(1969, 12, 31).and_hms(23, 59, 59))); // we don't know the exact local offset but we can check that // the conversion didn't change the instant itself - assert_eq!(local_from_str(r#""2014-07-24T12:34:06Z""#).unwrap(), + assert_eq!(local_from_str(r#""2014-07-24T12:34:06Z""#) + .expect("local shouuld parse"), UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); - assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#).unwrap(), + assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#) + .expect("local should parse with offset"), UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); - assert_eq!(fixed_from_str("0").unwrap(), - UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)); - assert_eq!(local_from_str("-1").unwrap(), - &UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)); assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); } + + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json_timestamps(utc_from_str: FUTC, + fixed_from_str: FFixed, + local_from_str: FLocal) + where FUTC: Fn(&str) -> Result, E>, + FFixed: Fn(&str) -> Result, E>, + FLocal: Fn(&str) -> Result, E>, + E: ::std::fmt::Debug +{ + fn norm(dt: &Option>) -> Option<(&DateTime, &Tz::Offset)> { + dt.as_ref().map(|dt| (dt, dt.offset())) + } + + assert_eq!(norm(&utc_from_str("0").ok().map(DateTime::from)), + norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); + assert_eq!(norm(&utc_from_str("-1").ok().map(DateTime::from)), + norm(&Some(UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)))); + + assert_eq!(norm(&fixed_from_str("0").ok().map(DateTime::from)), + norm(&Some(FixedOffset::east(0).ymd(1970, 1, 1).and_hms(0, 0, 0)))); + assert_eq!(norm(&fixed_from_str("-1").ok().map(DateTime::from)), + norm(&Some(FixedOffset::east(0).ymd(1969, 12, 31).and_hms(23, 59, 59)))); + + assert_eq!(*fixed_from_str("0").expect("0 timestamp should parse"), + UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)); + assert_eq!(*local_from_str("-1").expect("-1 timestamp should parse"), + UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { - use super::DateTime; - use offset::TimeZone; + use std::fmt; + use super::{DateTime, TsSeconds}; + use offset::{TimeZone, LocalResult}; use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; @@ -477,21 +518,47 @@ mod rustc_serialize { } } + // try!-like function to convert a LocalResult into a serde-ish Result + fn from(me: LocalResult, d: &mut D) -> Result + where D: Decoder, + T: fmt::Display, + { + match me { + LocalResult::None => Err(d.error( + "value is not a legal timestamp")), + LocalResult::Ambiguous(..) => Err(d.error( + "value is an ambiguous timestamp")), + LocalResult::Single(val) => Ok(val) + } + } + impl Decodable for DateTime { fn decode(d: &mut D) -> Result, D::Error> { - match d.read_str()?.parse::>() { - Ok(dt) => Ok(dt), - Err(_) => Err(d.error("invalid date and time")), - } + d.read_str()?.parse::>() + .map_err(|_| d.error("invalid date and time")) + } + } + + impl Decodable for TsSeconds { + fn decode(d: &mut D) -> Result, D::Error> { + from(FixedOffset::east(0).timestamp_opt(d.read_i64()?, 0), d) + .map(|dt| TsSeconds(dt)) } } impl Decodable for DateTime { fn decode(d: &mut D) -> Result, D::Error> { - match d.read_str()?.parse::>() { - Ok(dt) => Ok(dt.with_timezone(&UTC)), - Err(_) => Err(d.error("invalid date and time")), - } + d.read_str()? + .parse::>() + .map(|dt| dt.with_timezone(&UTC)) + .map_err(|_| d.error("invalid date and time")) + } + } + + impl Decodable for TsSeconds { + fn decode(d: &mut D) -> Result, D::Error> { + from(UTC.timestamp_opt(d.read_i64()?, 0), d) + .map(|dt| TsSeconds(dt)) } } @@ -504,6 +571,13 @@ mod rustc_serialize { } } + impl Decodable for TsSeconds { + fn decode(d: &mut D) -> Result, D::Error> { + from(UTC.timestamp_opt(d.read_i64()?, 0), d) + .map(|dt| TsSeconds(dt.with_timezone(&Local))) + } + } + #[cfg(test)] use rustc_serialize::json; #[test] @@ -515,12 +589,18 @@ mod rustc_serialize { fn test_decodable() { super::test_decodable_json(json::decode, json::decode, json::decode); } + + #[test] + fn test_decodable_timestamps() { + super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); + } + } #[cfg(feature = "serde")] mod serde { use std::fmt; - use super::DateTime; + use super::{DateTime, TsSeconds}; use offset::{TimeZone, LocalResult}; use offset::utc::UTC; use offset::local::Local; @@ -551,8 +631,8 @@ mod serde { // try!-like function to convert a LocalResult into a serde-ish Result fn from(me: LocalResult, ts: V) -> Result where E: de::Error, - V: Display, - T: Display, + V: fmt::Display, + T: fmt::Display, { match me { LocalResult::None => Err(E::custom( @@ -579,21 +659,6 @@ mod serde { { value.parse().map_err(|err| E::custom(format!("{}", err))) } - - // Deserialize a timestamp in seconds since the epoch - fn visit_i64(self, value: i64) -> Result, E> - where E: de::Error - { - from(FixedOffset::east(0).timestamp_opt(value, 0), value) - } - - // Deserialize a timestamp in seconds since the epoch - fn visit_u64(self, value: u64) -> Result, E> - where E: de::Error - { - from(FixedOffset::east(0).timestamp_opt(value as i64, 0), value) - } - } /// Deserialize a value that optionally includes a timezone offset in its @@ -634,6 +699,65 @@ mod serde { } } + struct SecondsTimestampVisitor; + + impl de::Visitor for SecondsTimestampVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a unix timestamp in seconds") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_i64(self, value: i64) -> Result, E> + where E: de::Error + { + from(FixedOffset::east(0).timestamp_opt(value, 0), value) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_u64(self, value: u64) -> Result, E> + where E: de::Error + { + from(FixedOffset::east(0).timestamp_opt(value as i64, 0), value) + } + } + + impl de::Deserialize for TsSeconds { + fn deserialize(deserializer: D) -> Result + where D: de::Deserializer + { + Ok(TsSeconds(try!( + deserializer + .deserialize_str(SecondsTimestampVisitor) + .map(|dt| dt.with_timezone(&Local))))) + } + } + + /// Can deserialize a timestamp into a FixedOffset + /// + /// The offset will always be 0, because timestamps are defined as UTC. + impl de::Deserialize for TsSeconds { + fn deserialize(deserializer: D) -> Result + where D: de::Deserializer + { + Ok(TsSeconds(try!( + deserializer.deserialize_str(SecondsTimestampVisitor)))) + } + } + + /// Deserialize into a UTC value + impl de::Deserialize for TsSeconds { + fn deserialize(deserializer: D) -> Result + where D: de::Deserializer + { + Ok(TsSeconds(try!( + deserializer.deserialize_str(SecondsTimestampVisitor) + .map(|dt| dt.with_timezone(&UTC))))) + } + } + #[cfg(test)] extern crate serde_json; #[cfg(test)] extern crate bincode; @@ -648,6 +772,13 @@ mod serde { |input| self::serde_json::from_str(&input)); } + #[test] + fn test_serde_deserialize_timestamps() { + super::test_decodable_json_timestamps(self::serde_json::from_str, + self::serde_json::from_str, + self::serde_json::from_str); + } + #[test] fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 9d00c2f..a6f7604 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -4,7 +4,7 @@ //! ISO 8601 date and time without timezone. use std::{str, fmt, hash}; -use std::ops::{Add, Sub}; +use std::ops::{Add, Sub, Deref}; use num::traits::ToPrimitive; use oldtime::Duration as OldDuration; @@ -54,6 +54,24 @@ pub struct NaiveDateTime { time: NaiveTime, } +/// A DateTime that can be deserialized from a seconds-based timestamp +pub struct TsSeconds(NaiveDateTime); + +impl From for NaiveDateTime { + /// Pull the internal NaiveDateTime out + fn from(obj: TsSeconds) -> NaiveDateTime { + obj.0 + } +} + +impl Deref for TsSeconds { + type Target = NaiveDateTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl NaiveDateTime { /// Makes a new `NaiveDateTime` from date and time components. /// Equivalent to [`date.and_time(time)`](../date/struct.NaiveDate.html#method.and_time) @@ -1402,16 +1420,6 @@ fn test_decodable_json(from_str: F) assert_eq!( from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); - assert_eq!( - from_str("0").unwrap(), - NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0), - "should parse integers as timestamps" - ); - assert_eq!( - from_str("-1").unwrap(), - NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59), - "should parse integers as timestamps" - ); // bad formats assert!(from_str(r#""""#).is_err()); @@ -1428,7 +1436,6 @@ fn test_decodable_json(from_str: F) assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); - assert!(from_str(r#"0"#).is_err()); assert!(from_str(r#"20160708000000"#).is_err()); assert!(from_str(r#"{}"#).is_err()); // pre-0.3.0 rustc-serialize format is now invalid @@ -1436,9 +1443,27 @@ fn test_decodable_json(from_str: F) assert!(from_str(r#"null"#).is_err()); } + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json_timestamp(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + assert_eq!( + *from_str("0").unwrap(), + NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0), + "should parse integers as timestamps" + ); + assert_eq!( + *from_str("-1").unwrap(), + NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59), + "should parse integers as timestamps" + ); +} + + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { - use super::NaiveDateTime; + use super::{NaiveDateTime, TsSeconds}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; impl Encodable for NaiveDateTime { @@ -1449,7 +1474,15 @@ mod rustc_serialize { impl Decodable for NaiveDateTime { fn decode(d: &mut D) -> Result { - d.read_str()?.parse().map_err(|_| d.error("invalid date and time")) + d.read_str()?.parse().map_err(|_| d.error("invalid date time string")) + } + } + + impl Decodable for TsSeconds { + fn decode(d: &mut D) -> Result { + Ok(TsSeconds( + NaiveDateTime::from_timestamp_opt(d.read_i64()?, 0) + .ok_or_else(|| d.error("invalid timestamp"))?)) } } @@ -1464,12 +1497,17 @@ mod rustc_serialize { fn test_decodable() { super::test_decodable_json(json::decode); } + + #[test] + fn test_decodable_timestamps() { + super::test_decodable_json_timestamp(json::decode); + } } #[cfg(feature = "serde")] mod serde { use std::fmt; - use super::NaiveDateTime; + use super::{NaiveDateTime, TsSeconds}; use serde::{ser, de}; // TODO not very optimized for space (binary formats would want something better) @@ -1499,7 +1537,7 @@ mod serde { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted date and time string or a unix timestamp") + write!(formatter, "a formatted date and time string") } fn visit_str(self, value: &str) -> Result @@ -1507,6 +1545,25 @@ mod serde { { value.parse().map_err(|err| E::custom(format!("{}", err))) } + } + + impl de::Deserialize for NaiveDateTime { + fn deserialize(deserializer: D) -> Result + where D: de::Deserializer + { + deserializer.deserialize_str(NaiveDateTimeVisitor) + } + } + + struct NaiveDateTimeFromSecondsVisitor; + + impl de::Visitor for NaiveDateTimeFromSecondsVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a unix timestamp") + } fn visit_i64(self, value: i64) -> Result where E: de::Error @@ -1527,7 +1584,8 @@ mod serde { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de> { - deserializer.deserialize_str(NaiveDateTimeVisitor) + Ok(TsSeconds(try!( + deserializer.deserialize_str(NaiveDateTimeFromSecondsVisitor)))) } } @@ -1544,6 +1602,11 @@ mod serde { super::test_decodable_json(|input| self::serde_json::from_str(&input)); } + #[test] + fn test_serde_deserialize_timestamp() { + super::test_decodable_json_timestamp(self::serde_json::from_str); + } + #[test] fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because From 44fc13d7df729f69a1f628362d66b275bde389c3 Mon Sep 17 00:00:00 2001 From: Brandon W Maister Date: Fri, 24 Feb 2017 11:58:21 -0500 Subject: [PATCH 45/53] Use serde's serialize_with instead of a newtype This is a significantly less horrible API than in the previous commit. --- .travis.sh | 62 ++++++++++ .travis.yml | 13 +-- Cargo.toml | 1 + appveyor.yml | 8 +- src/datetime.rs | 263 +++++++++++++++++++++++++----------------- src/naive/datetime.rs | 206 +++++++++++++++++++++++---------- 6 files changed, 372 insertions(+), 181 deletions(-) create mode 100755 .travis.sh diff --git a/.travis.sh b/.travis.sh new file mode 100755 index 0000000..cba5909 --- /dev/null +++ b/.travis.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# This is the script that's executed by travis, you can run it yourself to run +# the exact same suite + +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +channel() { + if [ -n "${TRAVIS}" ]; then + if [ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}" ]; then + pwd + (set -x; cargo "$@") + fi + elif [ -n "${APPVEYOR}" ]; then + if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then + pwd + (set -x; cargo "$@") + fi + else + pwd + (set -x; cargo "+${CHANNEL}" "$@") + fi +} + +build_and_test() { + # interleave building and testing in hope that it saves time + # also vary the local time zone to (hopefully) catch tz-dependent bugs + # also avoid doc-testing multiple times---it takes a lot and rarely helps + cargo clean + channel build -v + TZ=ACST-9:30 channel test -v --lib + channel build -v --features rustc-serialize + TZ=EST4 channel test -v --features rustc-serialize --lib + channel build -v --features 'serde bincode' + TZ=UTC0 channel test -v --features 'serde bincode' +} + +build_only() { + # Rust 1.13 doesn't support custom derive, so, to avoid doctests which + # validate that, we just build there. + cargo clean + channel build -v + channel build -v --features rustc-serialize + channel build -v --features 'serde bincode' +} + +rustc --version +cargo --version + +CHANNEL=nightly +build_and_test + +CHANNEL=beta +build_and_test + +CHANNEL=stable +build_and_test + +CHANNEL=1.13.0 +build_only diff --git a/.travis.yml b/.travis.yml index 69920fd..74a29cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: rust sudo: false rust: - # 1.13.0 is the earliest version that Serde 0.9 tests, so we follow suit + # 1.13.0 is the earliest version that Serde 1.0 tests, so we follow suit - 1.13.0 - stable - beta @@ -15,16 +15,7 @@ matrix: env: global: - LD_LIBRARY_PATH: /usr/local/lib -script: - # interleave building and testing in hope that it saves time - # also vary the local time zone to (hopefully) catch tz-dependent bugs - # also avoid doc-testing multiple times---it takes a lot and rarely helps - - cargo build -v - - TZ=ACST-9:30 cargo test -v - - cargo build -v --features rustc-serialize - - TZ=EST4 cargo test -v --features rustc-serialize --lib - - cargo build -v --features 'serde bincode' - - TZ=UTC0 cargo test -v --features 'serde bincode' --lib +script: ./.travis.sh notifications: email: false irc: diff --git a/Cargo.toml b/Cargo.toml index 018469a..5bfb6a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,5 @@ serde = { version = "1", optional = true } [dev-dependencies] serde_json = { version = "1" } +serde_derive = { version = "1" } bincode = { version = "0.8.0", features = ["serde"], default-features = false } diff --git a/appveyor.yml b/appveyor.yml index 241d049..9831503 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,8 +14,8 @@ install: - ps: $env:PATH="$env:PATH;C:\rust\bin" - rustc -vV - cargo -vV -build_script: - # do not test all combinations, Travis will handle that - - cargo build -v --features "serde rustc-serialize" + +build: false + test_script: - - cargo test -v --features "serde rustc-serialize" + - sh -c 'PATH=`rustc --print sysroot`/bin:$PATH ./.travis.sh' diff --git a/src/datetime.rs b/src/datetime.rs index 4679e38..fcacc65 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -5,7 +5,9 @@ use std::{str, fmt, hash}; use std::cmp::Ordering; -use std::ops::{Add, Sub, Deref}; +use std::ops::{Add, Sub}; +#[cfg(feature = "rustc-serialize")] +use std::ops::Deref; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; @@ -33,8 +35,10 @@ pub struct DateTime { /// A DateTime that can be deserialized from a timestamp /// /// A timestamp here is seconds since the epoch +#[cfg(feature = "rustc-serialize")] pub struct TsSeconds(DateTime); +#[cfg(feature = "rustc-serialize")] impl From> for DateTime { /// Pull the inner DateTime out fn from(obj: TsSeconds) -> DateTime { @@ -42,6 +46,7 @@ impl From> for DateTime { } } +#[cfg(feature = "rustc-serialize")] impl Deref for TsSeconds { type Target = DateTime; @@ -50,6 +55,7 @@ impl Deref for TsSeconds { } } + impl DateTime { /// Makes a new `DateTime` with given *UTC* datetime and offset. /// The local datetime should be constructed via the `TimeZone` trait. @@ -471,37 +477,6 @@ fn test_decodable_json(utc_from_str: FUTC, assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); } - - -#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_decodable_json_timestamps(utc_from_str: FUTC, - fixed_from_str: FFixed, - local_from_str: FLocal) - where FUTC: Fn(&str) -> Result, E>, - FFixed: Fn(&str) -> Result, E>, - FLocal: Fn(&str) -> Result, E>, - E: ::std::fmt::Debug -{ - fn norm(dt: &Option>) -> Option<(&DateTime, &Tz::Offset)> { - dt.as_ref().map(|dt| (dt, dt.offset())) - } - - assert_eq!(norm(&utc_from_str("0").ok().map(DateTime::from)), - norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); - assert_eq!(norm(&utc_from_str("-1").ok().map(DateTime::from)), - norm(&Some(UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)))); - - assert_eq!(norm(&fixed_from_str("0").ok().map(DateTime::from)), - norm(&Some(FixedOffset::east(0).ymd(1970, 1, 1).and_hms(0, 0, 0)))); - assert_eq!(norm(&fixed_from_str("-1").ok().map(DateTime::from)), - norm(&Some(FixedOffset::east(0).ymd(1969, 12, 31).and_hms(23, 59, 59)))); - - assert_eq!(*fixed_from_str("0").expect("0 timestamp should parse"), - UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)); - assert_eq!(*local_from_str("-1").expect("-1 timestamp should parse"), - UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)); -} - #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use std::fmt; @@ -518,7 +493,7 @@ mod rustc_serialize { } } - // try!-like function to convert a LocalResult into a serde-ish Result + // try!-like function to convert a LocalResult into a serde-ish Result fn from(me: LocalResult, d: &mut D) -> Result where D: Decoder, T: fmt::Display, @@ -590,23 +565,165 @@ mod rustc_serialize { super::test_decodable_json(json::decode, json::decode, json::decode); } - #[test] - fn test_decodable_timestamps() { - super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); - } - } +/// Ser/de helpers +/// +/// The various modules in here are intended to be used with serde's [`with` +/// annotation](https://serde.rs/attributes.html#field-attributes). #[cfg(feature = "serde")] -mod serde { +pub mod serde { use std::fmt; - use super::{DateTime, TsSeconds}; + use super::DateTime; use offset::{TimeZone, LocalResult}; use offset::utc::UTC; use offset::local::Local; use offset::fixed::FixedOffset; use serde::{ser, de}; + /// Ser/de to/from timestamps in seconds + /// + /// Intended for use with `serde`'s `with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # // We mark this ignored so that we can test on 1.13 (which does not + /// # // support custom derive), and run tests with --ignored on beta and + /// # // nightly to actually trigger these. + /// # + /// # #[macro_use] extern crate serde_derive; + /// # #[macro_use] extern crate serde_json; + /// # extern crate chrono; + /// # use chrono::{TimeZone, DateTime, UTC}; + /// use chrono::datetime::serde::ts_seconds; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_seconds")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let time = UTC.ymd(2015, 5, 15).and_hms(10, 0, 0); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// let my_s: S = serde_json::from_str(&as_string)?; + /// assert_eq!(my_s.time, time); + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub mod ts_seconds { + use std::fmt; + use serde::{ser, de}; + + use {DateTime, UTC, FixedOffset}; + use offset::TimeZone; + use super::from; + + /// Deserialize a DateTime from a seconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # // We mark this ignored so that we can test on 1.13 (which does not + /// # // support custom derive), and run tests with --ignored on beta and + /// # // nightly to actually trigger these. + /// # + /// # #[macro_use] extern crate serde_derive; + /// # #[macro_use] extern crate serde_json; + /// # extern crate chrono; + /// # use chrono::{DateTime, UTC}; + /// use chrono::datetime::serde::ts_seconds::deserialize as from_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_ts")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where D: de::Deserializer<'de> + { + Ok(try!(d.deserialize_i64(SecondsTimestampVisitor).map(|dt| dt.with_timezone(&UTC)))) + } + + /// Serialize a UTC datetime into an integer number of seconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # // We mark this ignored so that we can test on 1.13 (which does not + /// # // support custom derive), and run tests with --ignored on beta and + /// # // nightly to actually trigger these. + /// # + /// # #[macro_use] extern crate serde_derive; + /// # #[macro_use] extern crate serde_json; + /// # extern crate chrono; + /// # use chrono::{TimeZone, DateTime, UTC}; + /// use chrono::datetime::serde::ts_seconds::serialize as to_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_ts")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: UTC.ymd(2015, 5, 15).and_hms(10, 0, 0), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(dt: &DateTime, serializer: S) -> Result + where S: ser::Serializer + { + serializer.serialize_i64(dt.timestamp()) + } + + struct SecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a unix timestamp in seconds") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_i64(self, value: i64) -> Result, E> + where E: de::Error + { + from(FixedOffset::east(0).timestamp_opt(value, 0), value) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_u64(self, value: u64) -> Result, E> + where E: de::Error + { + from(FixedOffset::east(0).timestamp_opt(value as i64, 0), value) + } + } + + } + // TODO not very optimized for space (binary formats would want something better) impl ser::Serialize for DateTime { @@ -699,65 +816,6 @@ mod serde { } } - struct SecondsTimestampVisitor; - - impl de::Visitor for SecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result - { - write!(formatter, "a unix timestamp in seconds") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_i64(self, value: i64) -> Result, E> - where E: de::Error - { - from(FixedOffset::east(0).timestamp_opt(value, 0), value) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_u64(self, value: u64) -> Result, E> - where E: de::Error - { - from(FixedOffset::east(0).timestamp_opt(value as i64, 0), value) - } - } - - impl de::Deserialize for TsSeconds { - fn deserialize(deserializer: D) -> Result - where D: de::Deserializer - { - Ok(TsSeconds(try!( - deserializer - .deserialize_str(SecondsTimestampVisitor) - .map(|dt| dt.with_timezone(&Local))))) - } - } - - /// Can deserialize a timestamp into a FixedOffset - /// - /// The offset will always be 0, because timestamps are defined as UTC. - impl de::Deserialize for TsSeconds { - fn deserialize(deserializer: D) -> Result - where D: de::Deserializer - { - Ok(TsSeconds(try!( - deserializer.deserialize_str(SecondsTimestampVisitor)))) - } - } - - /// Deserialize into a UTC value - impl de::Deserialize for TsSeconds { - fn deserialize(deserializer: D) -> Result - where D: de::Deserializer - { - Ok(TsSeconds(try!( - deserializer.deserialize_str(SecondsTimestampVisitor) - .map(|dt| dt.with_timezone(&UTC))))) - } - } - #[cfg(test)] extern crate serde_json; #[cfg(test)] extern crate bincode; @@ -772,13 +830,6 @@ mod serde { |input| self::serde_json::from_str(&input)); } - #[test] - fn test_serde_deserialize_timestamps() { - super::test_decodable_json_timestamps(self::serde_json::from_str, - self::serde_json::from_str, - self::serde_json::from_str); - } - #[test] fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index a6f7604..d200635 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1443,24 +1443,6 @@ fn test_decodable_json(from_str: F) assert!(from_str(r#"null"#).is_err()); } - -#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_decodable_json_timestamp(from_str: F) - where F: Fn(&str) -> Result, E: ::std::fmt::Debug -{ - assert_eq!( - *from_str("0").unwrap(), - NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0), - "should parse integers as timestamps" - ); - assert_eq!( - *from_str("-1").unwrap(), - NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59), - "should parse integers as timestamps" - ); -} - - #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::{NaiveDateTime, TsSeconds}; @@ -1498,20 +1480,19 @@ mod rustc_serialize { super::test_decodable_json(json::decode); } - #[test] - fn test_decodable_timestamps() { - super::test_decodable_json_timestamp(json::decode); - } } +/// Tools to help serializing/deserializing NaiveDateTimes #[cfg(feature = "serde")] -mod serde { +pub mod serde { use std::fmt; - use super::{NaiveDateTime, TsSeconds}; + use super::{NaiveDateTime}; use serde::{ser, de}; - // TODO not very optimized for space (binary formats would want something better) - + /// Serialize a NaiveDateTime as a string + /// + /// See the [`ts_seconds`](./ts_seconds/index.html) module to serialize as + /// a timestamp. impl ser::Serialize for NaiveDateTime { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer @@ -1547,46 +1528,156 @@ mod serde { } } - impl de::Deserialize for NaiveDateTime { + impl<'de> de::Deserialize<'de> for NaiveDateTime { fn deserialize(deserializer: D) -> Result - where D: de::Deserializer + where D: de::Deserializer<'de> { deserializer.deserialize_str(NaiveDateTimeVisitor) } } - struct NaiveDateTimeFromSecondsVisitor; + /// Used to serialize/deserialize from second-precision timestamps + /// + /// # Example: + /// + /// ```rust + /// # // We mark this ignored so that we can test on 1.13 (which does not + /// # // support custom derive), and run tests with --ignored on beta and + /// # // nightly to actually trigger these. + /// # + /// # #[macro_use] extern crate serde_derive; + /// # extern crate serde_json; + /// # extern crate serde; + /// # extern crate chrono; + /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, UTC}; + /// use chrono::naive::datetime::serde::ts_seconds; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_seconds")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let time = NaiveDate::from_ymd(2015, 5, 15).and_hms(10, 0, 0); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// let my_s: S = serde_json::from_str(&as_string)?; + /// assert_eq!(my_s.time, time); + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub mod ts_seconds { + use std::fmt; + use serde::{ser, de}; - impl de::Visitor for NaiveDateTimeFromSecondsVisitor { - type Value = NaiveDateTime; + use NaiveDateTime; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result - { - write!(formatter, "a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where E: de::Error - { - NaiveDateTime::from_timestamp_opt(value, 0) - .ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value))) - } - - fn visit_u64(self, value: u64) -> Result - where E: de::Error - { - NaiveDateTime::from_timestamp_opt(value as i64, 0) - .ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value))) - } - } - - impl<'de> de::Deserialize<'de> for NaiveDateTime { - fn deserialize(deserializer: D) -> Result + /// Deserialize a DateTime from a seconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # // We mark this ignored so that we can test on 1.13 (which does not + /// # // support custom derive), and run tests with --ignored on beta and + /// # // nightly to actually trigger these. + /// # + /// # #[macro_use] extern crate serde_derive; + /// # #[macro_use] extern crate serde_json; + /// # extern crate serde; + /// # extern crate chrono; + /// # use chrono::{NaiveDateTime, UTC}; + /// # use serde::Deserialize; + /// use chrono::naive::datetime::serde::ts_seconds::deserialize as from_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_ts")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result where D: de::Deserializer<'de> { - Ok(TsSeconds(try!( - deserializer.deserialize_str(NaiveDateTimeFromSecondsVisitor)))) + Ok(try!(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor))) } + + /// Serialize a UTC datetime into an integer number of seconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # // We mark this ignored so that we can test on 1.13 (which does not + /// # // support custom derive), and run tests with --ignored on beta and + /// # // nightly to actually trigger these. + /// # + /// # #[macro_use] extern crate serde_derive; + /// # #[macro_use] extern crate serde_json; + /// # #[macro_use] extern crate serde; + /// # extern crate chrono; + /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, UTC}; + /// # use serde::Serialize; + /// use chrono::naive::datetime::serde::ts_seconds::serialize as to_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_ts")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: NaiveDate::from_ymd(2015, 5, 15).and_hms(10, 0, 0), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result + where S: ser::Serializer + { + serializer.serialize_i64(dt.timestamp()) + } + + struct NaiveDateTimeFromSecondsVisitor; + + impl<'de> de::Visitor<'de> for NaiveDateTimeFromSecondsVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + { + write!(formatter, "a unix timestamp") + } + + fn visit_i64(self, value: i64) -> Result + where E: de::Error + { + NaiveDateTime::from_timestamp_opt(value, 0) + .ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value))) + } + + fn visit_u64(self, value: u64) -> Result + where E: de::Error + { + NaiveDateTime::from_timestamp_opt(value as i64, 0) + .ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value))) + } + } + } #[cfg(test)] extern crate serde_json; @@ -1602,11 +1693,6 @@ mod serde { super::test_decodable_json(|input| self::serde_json::from_str(&input)); } - #[test] - fn test_serde_deserialize_timestamp() { - super::test_decodable_json_timestamp(self::serde_json::from_str); - } - #[test] fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because From 4e7b84064f86c7afb60cd844307b099dc7725b75 Mon Sep 17 00:00:00 2001 From: Brandon W Maister Date: Mon, 27 Feb 2017 21:53:19 -0500 Subject: [PATCH 46/53] re-add TsSeconds for RustcDecodable I think it's a terrible API, but AFAIK rustc-serialize doesn't support anything like serde's `with` attribute. I think it would be better to just not include this API at all and require that people who want to use this move to serde, which is the recommended rust encoding/decoding library. --- src/datetime.rs | 34 ++++++++++++++++++++++++++++++++++ src/naive/datetime.rs | 23 +++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/datetime.rs b/src/datetime.rs index fcacc65..e657a7c 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -477,6 +477,35 @@ fn test_decodable_json(utc_from_str: FUTC, assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); } +#[cfg(all(test, feature = "rustc-serialize"))] +fn test_decodable_json_timestamps(utc_from_str: FUTC, + fixed_from_str: FFixed, + local_from_str: FLocal) + where FUTC: Fn(&str) -> Result, E>, + FFixed: Fn(&str) -> Result, E>, + FLocal: Fn(&str) -> Result, E>, + E: ::std::fmt::Debug +{ + fn norm(dt: &Option>) -> Option<(&DateTime, &Tz::Offset)> { + dt.as_ref().map(|dt| (dt, dt.offset())) + } + + assert_eq!(norm(&utc_from_str("0").ok().map(DateTime::from)), + norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); + assert_eq!(norm(&utc_from_str("-1").ok().map(DateTime::from)), + norm(&Some(UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)))); + + assert_eq!(norm(&fixed_from_str("0").ok().map(DateTime::from)), + norm(&Some(FixedOffset::east(0).ymd(1970, 1, 1).and_hms(0, 0, 0)))); + assert_eq!(norm(&fixed_from_str("-1").ok().map(DateTime::from)), + norm(&Some(FixedOffset::east(0).ymd(1969, 12, 31).and_hms(23, 59, 59)))); + + assert_eq!(*fixed_from_str("0").expect("0 timestamp should parse"), + UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)); + assert_eq!(*local_from_str("-1").expect("-1 timestamp should parse"), + UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use std::fmt; @@ -565,6 +594,11 @@ mod rustc_serialize { super::test_decodable_json(json::decode, json::decode, json::decode); } + #[test] + fn test_decodable_timestamps() { + super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); + } + } /// Ser/de helpers diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index d200635..0a00a8f 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1443,6 +1443,23 @@ fn test_decodable_json(from_str: F) assert!(from_str(r#"null"#).is_err()); } + +#[cfg(all(test, feature = "rustc-serialize"))] +fn test_decodable_json_timestamp(from_str: F) + where F: Fn(&str) -> Result, E: ::std::fmt::Debug +{ + assert_eq!( + *from_str("0").unwrap(), + NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0), + "should parse integers as timestamps" + ); + assert_eq!( + *from_str("-1").unwrap(), + NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59), + "should parse integers as timestamps" + ); +} + #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use super::{NaiveDateTime, TsSeconds}; @@ -1480,6 +1497,12 @@ mod rustc_serialize { super::test_decodable_json(json::decode); } + #[test] + fn test_decodable_timestamps() { + super::test_decodable_json_timestamp(json::decode); + + } + } /// Tools to help serializing/deserializing NaiveDateTimes From c06bc01f0beb750daa6021453fedce7b487b8656 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 21 Jun 2017 14:03:49 +0900 Subject: [PATCH 47/53] Flattened intermediate implementation modules. There used to be multiple modules like `chrono::datetime` which only provide a single type `DateTime`. In retrospect, this module structure never reflected how people use those types; with the release of 0.3.0 `chrono::prelude` is a preferred way to glob-import types, and due to reexports `chrono::DateTime` and likes are also common enough. Therefore this commit removes those implementation modules and flattens the module structure. Specifically: Before After ---------------------------------- ---------------------------- chrono::date::Date chrono::Date chrono::date::MIN chrono::MIN_DATE chrono::date::MAX chrono::MAX_DATE chrono::datetime::DateTime chrono::DateTime chrono::datetime::TsSeconds chrono::TsSeconds chrono::datetime::serde::* chrono::serde::* chrono::naive::time::NaiveTime chrono::naive::NaiveTime chrono::naive::date::NaiveDate chrono::naive::NaiveDate chrono::naive::date::MIN chrono::naive::MIN_DATE chrono::naive::date::MAX chrono::naive::MAX_DATE chrono::naive::datetime::NaiveDateTime chrono::naive::NaiveDateTime chrono::naive::datetime::TsSeconds chrono::naive::TsSeconds chrono::naive::datetime::serde::* chrono::naive::serde::* chrono::offset::utc::UTC chrono::offset::UTC chrono::offset::fixed::FixedOffset chrono::offset::FixedOffset chrono::offset::local::Local chrono::offset::Local chrono::format::parsed::Parsed chrono::format::Parsed All internal documentation links have been updated (phew!) and verified with LinkChecker [1]. Probably we can automate this check in the future. [1] https://wummel.github.io/linkchecker/ Closes #161. Compared to the original proposal, `chrono::naive` is retained as we had `TsSeconds` types duplicated for `NaiveDateTime` and `DateTime` (legitimately). --- AUTHORS.txt | 2 + README.md | 44 +++--- src/date.rs | 13 +- src/datetime.rs | 38 ++--- src/format/mod.rs | 12 +- src/format/parse.rs | 8 +- src/format/parsed.rs | 26 ++-- src/lib.rs | 115 +++++++++------- src/naive/date.rs | 184 ++++++++++++------------- src/naive/datetime.rs | 118 ++++++++-------- src/naive/time.rs | 312 +++++++++++++++++++++--------------------- src/offset/fixed.rs | 6 +- src/offset/local.rs | 7 +- src/offset/mod.rs | 56 ++++---- src/offset/utc.rs | 9 +- 15 files changed, 461 insertions(+), 489 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index 39225ec..9e73819 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -7,6 +7,7 @@ Ashley Mannix Ben Boeckel Ben Eills Brandon W Maister +Brandon W Maister Colin Ray Corey Farwell Dan @@ -19,6 +20,7 @@ Eric Findlay Eunchong Yu Frans Skarman Huon Wilson +Igor Gnatenko Jim Turner Jisoo Park Joe Wilm diff --git a/README.md b/README.md index 0f987e7..4149de7 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ methods. ### Date and Time Chrono provides a -[**`DateTime`**](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html) +[**`DateTime`**](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html) type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping @@ -105,11 +105,11 @@ the [**`TimeZone`**](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.h which defines how the local date is converted to and back from the UTC date. There are three well-known `TimeZone` implementations: -* [**`UTC`**](https://docs.rs/chrono/0.4.0/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. +* [**`UTC`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.UTC.html) specifies the UTC time zone. It is most efficient. -* [**`Local`**](https://docs.rs/chrono/0.4.0/chrono/offset/local/struct.Local.html) specifies the system local time zone. +* [**`Local`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Local.html) specifies the system local time zone. -* [**`FixedOffset`**](https://docs.rs/chrono/0.4.0/chrono/offset/fixed/struct.FixedOffset.html) specifies +* [**`FixedOffset`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.FixedOffset.html) specifies an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. This often results from the parsed textual date and time. Since it stores the most information and does not depend on the system environment, @@ -117,12 +117,12 @@ There are three well-known `TimeZone` implementations: `DateTime`s with different `TimeZone` types are distinct and do not mix, but can be converted to each other using -the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.with_timezone) method. +the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.with_timezone) method. You can get the current date and time in the UTC time zone -([`UTC::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/utc/struct.UTC.html#method.now)) +([`UTC::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.UTC.html#method.now)) or in the local time zone -([`Local::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/local/struct.Local.html#method.now)). +([`Local::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Local.html#method.now)). ```rust use chrono::prelude::*; @@ -205,14 +205,14 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00 UTC.ymd(1938, 4, 24).and_hms(22, 13, 20)); ``` -Formatting is done via the [`format`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.format) method, +Formatting is done via the [`format`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.format) method, which format is equivalent to the familiar `strftime` format. (See the [`format::strftime` module documentation](https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers) for full syntax.) The default `to_string` method and `{:?}` specifier also give a reasonable representation. -Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and -[`to_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods +Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.to_rfc2822) and +[`to_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.to_rfc3339) methods for well-known formats. ```rust @@ -238,13 +238,13 @@ Parsing can be done with three methods: ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) format specifier prints, and requires the offset to be present. -2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses +2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_str) parses a date and time with offsets and returns `DateTime`. This should be used when the offset is a part of input and the caller cannot guess that. It *cannot* be used when the offset can be missing. - [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822) + [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_rfc2822) and - [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339) + [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_rfc3339) are similar but for well-known formats. 3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is @@ -288,7 +288,7 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e ### Individual date -Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4.0/chrono/date/struct.Date.html)). +Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4.0/chrono/struct.Date.html)). It also has time zones attached, and have to be constructed via time zones. Most operations available to `DateTime` are also available to `Date` whenever appropriate. @@ -307,26 +307,26 @@ assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_ There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. -`DateTime` has [`date`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.date) method +`DateTime` has [`date`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.date) method which returns a `Date` which represents its date component. -There is also a [`time`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.time) method, +There is also a [`time`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.time) method, which simply returns a naive local time described below. ### Naive date and time Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -as [**`NaiveDate`**](https://docs.rs/chrono/0.4.0/chrono/naive/date/struct.NaiveDate.html), -[**`NaiveTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/time/struct.NaiveTime.html) and -[**`NaiveDateTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/datetime/struct.NaiveDateTime.html) respectively. +as [**`NaiveDate`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveDate.html), +[**`NaiveTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveTime.html) and +[**`NaiveDateTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveDateTime.html) respectively. They have almost equivalent interfaces as their timezone-aware twins, but are not associated to time zones obviously and can be quite low-level. They are mostly useful for building blocks for higher-level types. Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -[`naive_local`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.naive_local) returns +[`naive_local`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.naive_local) returns a view to the naive local time, -and [`naive_utc`](https://docs.rs/chrono/0.4.0/chrono/datetime/struct.DateTime.html#method.naive_utc) returns +and [`naive_utc`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.naive_utc) returns a view to the naive UTC time. ## Limitations @@ -338,7 +338,7 @@ Date types are limited in about +/- 262,000 years from the common epoch. Time types are limited in the nanosecond accuracy. [Leap seconds are supported in the representation but -Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4.0/chrono/naive/time/index.html#leap-second-handling). +Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveTime.html#leap-second-handling). (The main reason is that leap seconds are not really predictable.) Almost *every* operation over the possible leap seconds will ignore them. Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale diff --git a/src/date.rs b/src/date.rs index d6fe6e3..05a1c32 100644 --- a/src/date.rs +++ b/src/date.rs @@ -9,12 +9,9 @@ use std::ops::{Add, Sub}; use oldtime::Duration as OldDuration; use {Weekday, Datelike}; -use offset::TimeZone; -use offset::utc::UTC; -use naive; -use naive::date::NaiveDate; -use naive::time::NaiveTime; -use datetime::DateTime; +use offset::{TimeZone, UTC}; +use naive::{self, NaiveDate, NaiveTime}; +use DateTime; use format::{Item, DelayedFormat, StrftimeItems}; /// ISO 8601 calendar date with time zone. @@ -48,9 +45,9 @@ pub struct Date { } /// The minimum possible `Date`. -pub const MIN: Date = Date { date: naive::date::MIN, offset: UTC }; +pub const MIN_DATE: Date = Date { date: naive::MIN_DATE, offset: UTC }; /// The maximum possible `Date`. -pub const MAX: Date = Date { date: naive::date::MAX, offset: UTC }; +pub const MAX_DATE: Date = Date { date: naive::MAX_DATE, offset: UTC }; impl Date { /// Makes a new `Date` with given *UTC* date and offset. diff --git a/src/datetime.rs b/src/datetime.rs index e657a7c..ecf6e73 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -11,13 +11,9 @@ use std::ops::Deref; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; -use offset::{TimeZone, Offset}; -use offset::utc::UTC; -use offset::local::Local; -use offset::fixed::FixedOffset; -use naive::time::NaiveTime; -use naive::datetime::NaiveDateTime; -use date::Date; +use offset::{TimeZone, Offset, UTC, Local, FixedOffset}; +use naive::{NaiveTime, NaiveDateTime}; +use Date; use format::{Item, Numeric, Pad, Fixed}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; @@ -510,10 +506,7 @@ fn test_decodable_json_timestamps(utc_from_str: FUTC, mod rustc_serialize { use std::fmt; use super::{DateTime, TsSeconds}; - use offset::{TimeZone, LocalResult}; - use offset::utc::UTC; - use offset::local::Local; - use offset::fixed::FixedOffset; + use offset::{TimeZone, LocalResult, UTC, Local, FixedOffset}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; impl Encodable for DateTime { @@ -609,11 +602,8 @@ mod rustc_serialize { pub mod serde { use std::fmt; use super::DateTime; - use offset::{TimeZone, LocalResult}; - use offset::utc::UTC; - use offset::local::Local; - use offset::fixed::FixedOffset; - use serde::{ser, de}; + use offset::{TimeZone, LocalResult, UTC, Local, FixedOffset}; + use serdelib::{ser, de}; /// Ser/de to/from timestamps in seconds /// @@ -630,7 +620,7 @@ pub mod serde { /// # #[macro_use] extern crate serde_json; /// # extern crate chrono; /// # use chrono::{TimeZone, DateTime, UTC}; - /// use chrono::datetime::serde::ts_seconds; + /// use chrono::serde::ts_seconds; /// #[derive(Deserialize, Serialize)] /// struct S { /// #[serde(with = "ts_seconds")] @@ -653,7 +643,7 @@ pub mod serde { /// ``` pub mod ts_seconds { use std::fmt; - use serde::{ser, de}; + use serdelib::{ser, de}; use {DateTime, UTC, FixedOffset}; use offset::TimeZone; @@ -674,7 +664,7 @@ pub mod serde { /// # #[macro_use] extern crate serde_json; /// # extern crate chrono; /// # use chrono::{DateTime, UTC}; - /// use chrono::datetime::serde::ts_seconds::deserialize as from_ts; + /// use chrono::serde::ts_seconds::deserialize as from_ts; /// #[derive(Deserialize)] /// struct S { /// #[serde(deserialize_with = "from_ts")] @@ -708,7 +698,7 @@ pub mod serde { /// # #[macro_use] extern crate serde_json; /// # extern crate chrono; /// # use chrono::{TimeZone, DateTime, UTC}; - /// use chrono::datetime::serde::ts_seconds::serialize as to_ts; + /// use chrono::serde::ts_seconds::serialize as to_ts; /// #[derive(Serialize)] /// struct S { /// #[serde(serialize_with = "to_ts")] @@ -882,12 +872,8 @@ pub mod serde { mod tests { use super::DateTime; use Datelike; - use naive::time::NaiveTime; - use naive::date::NaiveDate; - use offset::TimeZone; - use offset::utc::UTC; - use offset::local::Local; - use offset::fixed::FixedOffset; + use naive::{NaiveTime, NaiveDate}; + use offset::{TimeZone, UTC, Local, FixedOffset}; use oldtime::Duration; #[test] diff --git a/src/format/mod.rs b/src/format/mod.rs index f120014..503ef16 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -9,10 +9,8 @@ use std::error::Error; use {Datelike, Timelike, Weekday, ParseWeekdayError}; use div::{div_floor, mod_floor}; -use offset::Offset; -use offset::fixed::FixedOffset; -use naive::date::NaiveDate; -use naive::time::NaiveTime; +use offset::{Offset, FixedOffset}; +use naive::{NaiveDate, NaiveTime}; pub use self::strftime::StrftimeItems; pub use self::parsed::Parsed; @@ -175,14 +173,14 @@ pub enum Fixed { /// /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces. /// The offset is limited from `-24:00` to `+24:00`, - /// which is same to [`FixedOffset`](../offset/fixed/struct.FixedOffset.html)'s range. + /// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. TimezoneOffsetColon, /// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`). /// /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces, /// and `Z` can be either in upper case or in lower case. /// The offset is limited from `-24:00` to `+24:00`, - /// which is same to [`FixedOffset`](../offset/fixed/struct.FixedOffset.html)'s range. + /// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. TimezoneOffsetColonZ, /// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon. /// Parsing allows an optional colon. @@ -515,7 +513,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt Ok(()) } -pub mod parsed; +mod parsed; // due to the size of parsing routines, they are in separate modules. mod scan; diff --git a/src/format/parse.rs b/src/format/parse.rs index 3b58335..0c8724b 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -603,8 +603,8 @@ fn test_parse() { #[cfg(test)] #[test] fn test_rfc2822() { - use datetime::DateTime; - use offset::fixed::FixedOffset; + use DateTime; + use offset::FixedOffset; use super::*; use super::NOT_ENOUGH; @@ -687,8 +687,8 @@ fn parse_rfc850() { #[cfg(test)] #[test] fn test_rfc3339() { - use datetime::DateTime; - use offset::fixed::FixedOffset; + use DateTime; + use offset::FixedOffset; use super::*; // Test data - (input, Ok(expected result after parse and format) or Err(error code)) diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 7eb3a3e..1ab0099 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -10,12 +10,9 @@ use oldtime::Duration as OldDuration; use {Datelike, Timelike}; use Weekday; use div::div_rem; -use offset::{TimeZone, Offset, LocalResult}; -use offset::fixed::FixedOffset; -use naive::date::NaiveDate; -use naive::time::NaiveTime; -use naive::datetime::NaiveDateTime; -use datetime::DateTime; +use offset::{TimeZone, Offset, LocalResult, FixedOffset}; +use naive::{NaiveDate, NaiveTime, NaiveDateTime}; +use DateTime; use super::{ParseResult, OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH}; /// Parsed parts of date and time. There are two classes of methods: @@ -641,11 +638,8 @@ mod tests { use super::super::{OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH}; use Datelike; use Weekday::*; - use naive::date::{self, NaiveDate}; - use naive::time::NaiveTime; - use offset::TimeZone; - use offset::utc::UTC; - use offset::fixed::FixedOffset; + use naive::{MIN_DATE, MAX_DATE, NaiveDate, NaiveTime}; + use offset::{TimeZone, UTC, FixedOffset}; #[test] fn test_parsed_set_fields() { @@ -764,7 +758,7 @@ mod tests { ymd(0, 1, 1)); assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(OUT_OF_RANGE)); - let max_year = date::MAX.year(); + let max_year = MAX_DATE.year(); assert_eq!(parse!(year_div_100: max_year / 100, year_mod_100: max_year % 100, month: 1, day: 1), ymd(max_year, 1, 1)); @@ -963,17 +957,17 @@ mod tests { // more timestamps let max_days_from_year_1970 = - date::MAX.signed_duration_since(NaiveDate::from_ymd(1970,1,1)); + MAX_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1)); let year_0_from_year_1970 = NaiveDate::from_ymd(0,1,1).signed_duration_since(NaiveDate::from_ymd(1970,1,1)); let min_days_from_year_1970 = - date::MIN.signed_duration_since(NaiveDate::from_ymd(1970,1,1)); + MIN_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1)); assert_eq!(parse!(timestamp: min_days_from_year_1970.num_seconds()), - ymdhms(date::MIN.year(),1,1, 0,0,0)); + ymdhms(MIN_DATE.year(),1,1, 0,0,0)); assert_eq!(parse!(timestamp: year_0_from_year_1970.num_seconds()), ymdhms(0,1,1, 0,0,0)); assert_eq!(parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399), - ymdhms(date::MAX.year(),12,31, 23,59,59)); + ymdhms(MAX_DATE.year(),12,31, 23,59,59)); // leap seconds #1: partial fields assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE)); diff --git a/src/lib.rs b/src/lib.rs index b771030..0266a1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ //! ### Date and Time //! //! Chrono provides a -//! [**`DateTime`**](./datetime/struct.DateTime.html) +//! [**`DateTime`**](./struct.DateTime.html) //! type to represent a date and a time in a timezone. //! //! For more abstract moment-in-time tracking such as internal timekeeping @@ -91,11 +91,11 @@ //! which defines how the local date is converted to and back from the UTC date. //! There are three well-known `TimeZone` implementations: //! -//! * [**`UTC`**](./offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient. +//! * [**`UTC`**](./offset/struct.UTC.html) specifies the UTC time zone. It is most efficient. //! -//! * [**`Local`**](./offset/local/struct.Local.html) specifies the system local time zone. +//! * [**`Local`**](./offset/struct.Local.html) specifies the system local time zone. //! -//! * [**`FixedOffset`**](./offset/fixed/struct.FixedOffset.html) specifies +//! * [**`FixedOffset`**](./offset/struct.FixedOffset.html) specifies //! an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. //! This often results from the parsed textual date and time. //! Since it stores the most information and does not depend on the system environment, @@ -103,12 +103,12 @@ //! //! `DateTime`s with different `TimeZone` types are distinct and do not mix, //! but can be converted to each other using -//! the [`DateTime::with_timezone`](./datetime/struct.DateTime.html#method.with_timezone) method. +//! the [`DateTime::with_timezone`](./struct.DateTime.html#method.with_timezone) method. //! //! You can get the current date and time in the UTC time zone -//! ([`UTC::now()`](./offset/utc/struct.UTC.html#method.now)) +//! ([`UTC::now()`](./offset/struct.UTC.html#method.now)) //! or in the local time zone -//! ([`Local::now()`](./offset/local/struct.Local.html#method.now)). +//! ([`Local::now()`](./offset/struct.Local.html#method.now)). //! //! ```rust //! use chrono::prelude::*; @@ -198,14 +198,14 @@ //! # } //! ``` //! -//! Formatting is done via the [`format`](./datetime/struct.DateTime.html#method.format) method, +//! Formatting is done via the [`format`](./struct.DateTime.html#method.format) method, //! which format is equivalent to the familiar `strftime` format. //! (See the [`format::strftime` module documentation](./format/strftime/index.html#specifiers) //! for full syntax.) //! //! The default `to_string` method and `{:?}` specifier also give a reasonable representation. -//! Chrono also provides [`to_rfc2822`](./datetime/struct.DateTime.html#method.to_rfc2822) and -//! [`to_rfc3339`](./datetime/struct.DateTime.html#method.to_rfc3339) methods +//! Chrono also provides [`to_rfc2822`](./struct.DateTime.html#method.to_rfc2822) and +//! [`to_rfc3339`](./struct.DateTime.html#method.to_rfc3339) methods //! for well-known formats. //! //! ```rust @@ -231,13 +231,13 @@ //! ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) //! format specifier prints, and requires the offset to be present. //! -//! 2. [`DateTime::parse_from_str`](./datetime/struct.DateTime.html#method.parse_from_str) parses +//! 2. [`DateTime::parse_from_str`](./struct.DateTime.html#method.parse_from_str) parses //! a date and time with offsets and returns `DateTime`. //! This should be used when the offset is a part of input and the caller cannot guess that. //! It *cannot* be used when the offset can be missing. -//! [`DateTime::parse_from_rfc2822`](./datetime/struct.DateTime.html#method.parse_from_rfc2822) +//! [`DateTime::parse_from_rfc2822`](./struct.DateTime.html#method.parse_from_rfc2822) //! and -//! [`DateTime::parse_from_rfc3339`](./datetime/struct.DateTime.html#method.parse_from_rfc3339) +//! [`DateTime::parse_from_rfc3339`](./struct.DateTime.html#method.parse_from_rfc3339) //! are similar but for well-known formats. //! //! 3. [`Offset::datetime_from_str`](./offset/trait.TimeZone.html#method.datetime_from_str) is @@ -281,7 +281,7 @@ //! //! ### Individual date //! -//! Chrono also provides an individual date type ([**`Date`**](./date/struct.Date.html)). +//! Chrono also provides an individual date type ([**`Date`**](./struct.Date.html)). //! It also has time zones attached, and have to be constructed via time zones. //! Most operations available to `DateTime` are also available to `Date` whenever appropriate. //! @@ -301,26 +301,26 @@ //! //! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. //! -//! `DateTime` has [`date`](./datetime/struct.DateTime.html#method.date) method +//! `DateTime` has [`date`](./struct.DateTime.html#method.date) method //! which returns a `Date` which represents its date component. -//! There is also a [`time`](./datetime/struct.DateTime.html#method.time) method, +//! There is also a [`time`](./struct.DateTime.html#method.time) method, //! which simply returns a naive local time described below. //! //! ### Naive date and time //! //! Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -//! as [**`NaiveDate`**](./naive/date/struct.NaiveDate.html), -//! [**`NaiveTime`**](./naive/time/struct.NaiveTime.html) and -//! [**`NaiveDateTime`**](./naive/datetime/struct.NaiveDateTime.html) respectively. +//! as [**`NaiveDate`**](./naive/struct.NaiveDate.html), +//! [**`NaiveTime`**](./naive/struct.NaiveTime.html) and +//! [**`NaiveDateTime`**](./naive/struct.NaiveDateTime.html) respectively. //! //! They have almost equivalent interfaces as their timezone-aware twins, //! but are not associated to time zones obviously and can be quite low-level. //! They are mostly useful for building blocks for higher-level types. //! //! Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -//! [`naive_local`](./datetime/struct.DateTime.html#method.naive_local) returns +//! [`naive_local`](./struct.DateTime.html#method.naive_local) returns //! a view to the naive local time, -//! and [`naive_utc`](./datetime/struct.DateTime.html#method.naive_utc) returns +//! and [`naive_utc`](./struct.DateTime.html#method.naive_utc) returns //! a view to the naive UTC time. //! //! ## Limitations @@ -332,7 +332,7 @@ //! Time types are limited in the nanosecond accuracy. //! //! [Leap seconds are supported in the representation but -//! Chrono doesn't try to make use of them](./naive/time/index.html#leap-second-handling). +//! Chrono doesn't try to make use of them](./naive/struct.NaiveTime.html#leap-second-handling). //! (The main reason is that leap seconds are not really predictable.) //! Almost *every* operation over the possible leap seconds will ignore them. //! Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale @@ -356,34 +356,26 @@ extern crate num; #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; #[cfg(feature = "serde")] -extern crate serde; +extern crate serde as serdelib; // this reexport is to aid the transition and should not be in the prelude! pub use oldtime::Duration; -pub use offset::{TimeZone, Offset, LocalResult}; -pub use offset::utc::UTC; -pub use offset::fixed::FixedOffset; -pub use offset::local::Local; -pub use naive::date::NaiveDate; -pub use naive::time::NaiveTime; -pub use naive::datetime::NaiveDateTime; -pub use date::Date; -pub use datetime::DateTime; +#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, UTC, FixedOffset, Local}; +#[doc(no_inline)] pub use naive::{NaiveDate, NaiveTime, NaiveDateTime}; +pub use date_::{Date, MIN_DATE, MAX_DATE}; +pub use datetime_::DateTime; +#[cfg(feature = "rustc-serialize")] pub use datetime_::TsSeconds; pub use format::{ParseError, ParseResult}; /// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). pub mod prelude { - pub use {Datelike, Timelike, Weekday}; - pub use offset::{TimeZone, Offset}; - pub use offset::utc::UTC; - pub use offset::fixed::FixedOffset; - pub use offset::local::Local; - pub use naive::date::NaiveDate; - pub use naive::time::NaiveTime; - pub use naive::datetime::NaiveDateTime; - pub use date::Date; - pub use datetime::DateTime; + #[doc(no_inline)] pub use {Datelike, Timelike, Weekday}; + #[doc(no_inline)] pub use {TimeZone, Offset}; + #[doc(no_inline)] pub use {UTC, FixedOffset, Local}; + #[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime}; + #[doc(no_inline)] pub use Date; + #[doc(no_inline)] pub use DateTime; } // useful throughout the codebase @@ -399,14 +391,35 @@ pub mod naive { //! They are primarily building blocks for other types //! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)), //! but can be also used for the simpler date and time handling. - pub mod date; - pub mod time; - pub mod datetime; + + // avoid using them directly even in the crate itself + #[path = "date.rs"] mod date_; + #[path = "time.rs"] mod time_; + #[path = "datetime.rs"] mod datetime_; + + pub use self::date_::{NaiveDate, MIN_DATE, MAX_DATE}; + pub use self::time_::NaiveTime; + pub use self::datetime_::{NaiveDateTime, TsSeconds}; + + /// Tools to help serializing/deserializing naive types. + #[cfg(feature = "serde")] + pub mod serde { + pub use super::datetime_::serde::*; + } } -pub mod date; -pub mod datetime; +#[path = "date.rs"] mod date_; +#[path = "datetime.rs"] mod datetime_; pub mod format; +/// Ser/de helpers +/// +/// The various modules in here are intended to be used with serde's [`with` +/// annotation](https://serde.rs/attributes.html#field-attributes). +#[cfg(feature = "serde")] +pub mod serde { + pub use super::datetime_::serde::*; +} + /// The day of week. /// /// The order of the days of week depends on the context. @@ -594,7 +607,7 @@ impl fmt::Debug for ParseWeekdayError { mod weekday_serde { use super::Weekday; use std::fmt; - use serde::{ser, de}; + use serdelib::{ser, de}; impl ser::Serialize for Weekday { fn serialize(&self, serializer: S) -> Result @@ -698,7 +711,7 @@ mod weekday_serde { /// The common set of methods for date component. pub trait Datelike: Sized { - /// Returns the year number in the [calendar date](./naive/date/index.html#calendar-date). + /// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date). fn year(&self) -> i32; /// Returns the absolute year number starting from 1 with a boolean flag, @@ -826,7 +839,7 @@ pub trait Timelike: Sized { /// Returns the number of nanoseconds since the whole non-leap second. /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./naive/time/index.html#leap-second-handling). + /// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling). fn nanosecond(&self) -> u32; /// Makes a new value with the hour number changed. @@ -864,7 +877,7 @@ pub trait Timelike: Sized { fn test_readme_doomsday() { use num::iter::range_inclusive; - for y in range_inclusive(naive::date::MIN.year(), naive::date::MAX.year()) { + for y in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) { // even months let d4 = NaiveDate::from_ymd(y, 4, 4); let d6 = NaiveDate::from_ymd(y, 6, 6); diff --git a/src/naive/date.rs b/src/naive/date.rs index b302c37..db7d5e1 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -2,49 +2,6 @@ // See README.md and LICENSE.txt for details. //! ISO 8601 calendar date without timezone. -//! -//! # Calendar Date -//! -//! The ISO 8601 **calendar date** follows the proleptic Gregorian calendar. -//! It is like a normal civil calendar but note some slight differences: -//! -//! * Dates before the Gregorian calendar's inception in 1582 are defined via the extrapolation. -//! Be careful, as historical dates are often noted in the Julian calendar and others -//! and the transition to Gregorian may differ across countries (as late as early 20C). -//! -//! (Some example: Both Shakespeare from Britain and Cervantes from Spain seemingly died -//! on the same calendar date---April 23, 1616---but in the different calendar. -//! Britain used the Julian calendar at that time, so Shakespeare's death is later.) -//! -//! * ISO 8601 calendars has the year 0, which is 1 BCE (a year before 1 CE). -//! If you need a typical BCE/BC and CE/AD notation for year numbers, -//! use the [`Datelike::year_ce`](../../trait.Datelike.html#method.year_ce) method. -//! -//! # Week Date -//! -//! The ISO 8601 **week date** is a triple of year number, week number -//! and [day of the week](../../enum.Weekday.html) with the following rules: -//! -//! * A week consists of Monday through Sunday, and is always numbered within some year. -//! The week number ranges from 1 to 52 or 53 depending on the year. -//! -//! * The week 1 of given year is defined as the first week containing January 4 of that year, -//! or equivalently, the first week containing four or more days in that year. -//! -//! * The year number in the week date may *not* correspond to the actual Gregorian year. -//! For example, January 3, 2016 (Sunday) was on the last (53rd) week of 2015. -//! -//! Chrono's date types default to the ISO 8601 [calendar date](#calendar-date), -//! but the [`Datelike::isoweekdate`](../../trait.Datelike.html#tymethod.isoweekdate) method -//! can be used to get the corresponding week date. -//! -//! # Ordinal Date -//! -//! The ISO 8601 **ordinal date** is a pair of year number and day of the year ("ordinal"). -//! The ordinal number ranges from 1 to 365 or 366 depending on the year. -//! The year number is same to that of the [calendar date](#calendar-date). -//! -//! This is currently the internal format of Chrono's date types. use std::{str, fmt, hash}; use std::ops::{Add, Sub}; @@ -53,8 +10,7 @@ use oldtime::Duration as OldDuration; use {Weekday, Datelike}; use div::div_mod_floor; -use naive::time::NaiveTime; -use naive::datetime::NaiveDateTime; +use naive::{NaiveTime, NaiveDateTime}; use format::{Item, Numeric, Pad}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; @@ -89,32 +45,76 @@ const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 + const MAX_BITS: usize = 44; /// ISO 8601 calendar date without timezone. -/// Allows for every [proleptic Gregorian date](./index.html#calendar-date) +/// Allows for every [proleptic Gregorian date](#calendar-date) /// from Jan 1, 262145 BCE to Dec 31, 262143 CE. /// Also supports the conversion from ISO 8601 ordinal and week date. +/// +/// # Calendar Date +/// +/// The ISO 8601 **calendar date** follows the proleptic Gregorian calendar. +/// It is like a normal civil calendar but note some slight differences: +/// +/// * Dates before the Gregorian calendar's inception in 1582 are defined via the extrapolation. +/// Be careful, as historical dates are often noted in the Julian calendar and others +/// and the transition to Gregorian may differ across countries (as late as early 20C). +/// +/// (Some example: Both Shakespeare from Britain and Cervantes from Spain seemingly died +/// on the same calendar date---April 23, 1616---but in the different calendar. +/// Britain used the Julian calendar at that time, so Shakespeare's death is later.) +/// +/// * ISO 8601 calendars has the year 0, which is 1 BCE (a year before 1 CE). +/// If you need a typical BCE/BC and CE/AD notation for year numbers, +/// use the [`Datelike::year_ce`](../../trait.Datelike.html#method.year_ce) method. +/// +/// # Week Date +/// +/// The ISO 8601 **week date** is a triple of year number, week number +/// and [day of the week](../../enum.Weekday.html) with the following rules: +/// +/// * A week consists of Monday through Sunday, and is always numbered within some year. +/// The week number ranges from 1 to 52 or 53 depending on the year. +/// +/// * The week 1 of given year is defined as the first week containing January 4 of that year, +/// or equivalently, the first week containing four or more days in that year. +/// +/// * The year number in the week date may *not* correspond to the actual Gregorian year. +/// For example, January 3, 2016 (Sunday) was on the last (53rd) week of 2015. +/// +/// Chrono's date types default to the ISO 8601 [calendar date](#calendar-date), +/// but the [`Datelike::isoweekdate`](../../trait.Datelike.html#tymethod.isoweekdate) method +/// can be used to get the corresponding week date. +/// +/// # Ordinal Date +/// +/// The ISO 8601 **ordinal date** is a pair of year number and day of the year ("ordinal"). +/// The ordinal number ranges from 1 to 365 or 366 depending on the year. +/// The year number is same to that of the [calendar date](#calendar-date). +/// +/// This is currently the internal format of Chrono's date types. #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub struct NaiveDate { ymdf: DateImpl, // (year << 13) | of } /// The minimum possible `NaiveDate` (January 1, 262145 BCE). -pub const MIN: NaiveDate = NaiveDate { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o07 /*FE*/ }; +pub const MIN_DATE: NaiveDate = NaiveDate { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o07 /*FE*/ }; /// The maximum possible `NaiveDate` (December 31, 262143 CE). -pub const MAX: NaiveDate = NaiveDate { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o17 /*F*/ }; +pub const MAX_DATE: NaiveDate = NaiveDate { 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. +// as it is hard to verify year flags in `MIN_DATE` and `MAX_DATE`, +// we use a separate run-time test. #[test] fn test_date_bounds() { let calculated_min = NaiveDate::from_ymd(MIN_YEAR, 1, 1); let calculated_max = NaiveDate::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()); + assert!(MIN_DATE == calculated_min, + "`MIN_DATE` should have a year flag {:?}", calculated_min.of().flags()); + assert!(MAX_DATE == calculated_max, + "`MAX_DATE` should have a year flag {:?}", calculated_max.of().flags()); // let's also check that the entire range do not exceed 2^44 seconds // (sometimes used for bounding `Duration` against overflow) - let maxsecs = MAX.signed_duration_since(MIN).num_seconds(); + let maxsecs = MAX_DATE.signed_duration_since(MIN_DATE).num_seconds(); let maxsecs = maxsecs + 86401; // also take care of DateTime assert!(maxsecs < (1 << MAX_BITS), "The entire `NaiveDate` range somehow exceeds 2^{} seconds", MAX_BITS); @@ -136,7 +136,7 @@ impl NaiveDate { NaiveDate::from_of(year, mdf.to_of()) } - /// Makes a new `NaiveDate` from the [calendar date](./index.html#calendar-date) + /// Makes a new `NaiveDate` from the [calendar date](#calendar-date) /// (year, month and day). /// /// Panics on the out-of-range date, invalid month and/or day. @@ -158,7 +158,7 @@ impl NaiveDate { NaiveDate::from_ymd_opt(year, month, day).expect("invalid or out-of-range date") } - /// Makes a new `NaiveDate` from the [calendar date](./index.html#calendar-date) + /// Makes a new `NaiveDate` from the [calendar date](#calendar-date) /// (year, month and day). /// /// Returns `None` on the out-of-range date, invalid month and/or day. @@ -182,7 +182,7 @@ impl NaiveDate { NaiveDate::from_mdf(year, Mdf::new(month, day, flags)) } - /// Makes a new `NaiveDate` from the [ordinal date](./index.html#ordinal-date) + /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) /// (year and day of the year). /// /// Panics on the out-of-range date and/or invalid day of year. @@ -204,7 +204,7 @@ impl NaiveDate { NaiveDate::from_yo_opt(year, ordinal).expect("invalid or out-of-range date") } - /// Makes a new `NaiveDate` from the [ordinal date](./index.html#ordinal-date) + /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) /// (year and day of the year). /// /// Returns `None` on the out-of-range date and/or invalid day of year. @@ -229,7 +229,7 @@ impl NaiveDate { NaiveDate::from_of(year, Of::new(ordinal, flags)) } - /// Makes a new `NaiveDate` from the [ISO week date](./index.html#week-date) + /// Makes a new `NaiveDate` from the [ISO week date](#week-date) /// (year, week number and day of the week). /// The resulting `NaiveDate` may have a different year from the input year. /// @@ -252,7 +252,7 @@ impl NaiveDate { NaiveDate::from_isoywd_opt(year, week, weekday).expect("invalid or out-of-range date") } - /// Makes a new `NaiveDate` from the [ISO week date](./index.html#week-date) + /// Makes a new `NaiveDate` from the [ISO week date](#week-date) /// (year, week number and day of the week). /// The resulting `NaiveDate` may have a different year from the input year. /// @@ -466,7 +466,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. /// - /// No [leap second](../time/index.html#leap-second-handling) is allowed here; + /// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here; /// use `NaiveDate::and_hms_*` methods with a subsecond parameter instead. /// /// Panics on invalid hour, minute and/or second. @@ -490,7 +490,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. /// - /// No [leap second](../time/index.html#leap-second-handling) is allowed here; + /// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here; /// use `NaiveDate::and_hms_*_opt` methods with a subsecond parameter instead. /// /// Returns `None` on invalid hour, minute and/or second. @@ -514,7 +514,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. /// /// The millisecond part can exceed 1,000 - /// in order to represent the [leap second](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or millisecond. /// @@ -539,7 +539,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. /// /// The millisecond part can exceed 1,000 - /// in order to represent the [leap second](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Returns `None` on invalid hour, minute, second and/or millisecond. /// @@ -565,7 +565,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. /// /// The microsecond part can exceed 1,000,000 - /// in order to represent the [leap second](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or microsecond. /// @@ -590,7 +590,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. /// /// The microsecond part can exceed 1,000,000 - /// in order to represent the [leap second](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Returns `None` on invalid hour, minute, second and/or microsecond. /// @@ -616,7 +616,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` 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](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or nanosecond. /// @@ -641,7 +641,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` 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](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Returns `None` on invalid hour, minute, second and/or nanosecond. /// @@ -723,11 +723,11 @@ impl NaiveDate { /// /// ~~~~ /// use chrono::NaiveDate; - /// use chrono::naive::date::MAX; + /// use chrono::naive::MAX_DATE; /// /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).succ_opt(), /// Some(NaiveDate::from_ymd(2015, 6, 4))); - /// assert_eq!(MAX.succ_opt(), None); + /// assert_eq!(MAX_DATE.succ_opt(), None); /// ~~~~ #[inline] pub fn succ_opt(&self) -> Option { @@ -760,11 +760,11 @@ impl NaiveDate { /// /// ~~~~ /// use chrono::NaiveDate; - /// use chrono::naive::date::MIN; + /// use chrono::naive::MIN_DATE; /// /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).pred_opt(), /// Some(NaiveDate::from_ymd(2015, 6, 2))); - /// assert_eq!(MIN.pred_opt(), None); + /// assert_eq!(MIN_DATE.pred_opt(), None); /// ~~~~ #[inline] pub fn pred_opt(&self) -> Option { @@ -780,7 +780,7 @@ impl NaiveDate { /// ~~~~ /// # extern crate chrono; extern crate time; fn main() { /// use chrono::NaiveDate; - /// use chrono::naive::date::MAX; + /// use chrono::naive::MAX_DATE; /// use time::Duration; /// /// let d = NaiveDate::from_ymd(2015, 9, 5); @@ -790,7 +790,7 @@ impl NaiveDate { /// Some(NaiveDate::from_ymd(2015, 7, 27))); /// assert_eq!(d.checked_add_signed(Duration::days(1_000_000_000)), None); /// assert_eq!(d.checked_add_signed(Duration::days(-1_000_000_000)), None); - /// assert_eq!(MAX.checked_add_signed(Duration::days(1)), None); + /// assert_eq!(MAX_DATE.checked_add_signed(Duration::days(1)), None); /// # } /// ~~~~ pub fn checked_add_signed(self, rhs: OldDuration) -> Option { @@ -816,7 +816,7 @@ impl NaiveDate { /// ~~~~ /// # extern crate chrono; extern crate time; fn main() { /// use chrono::NaiveDate; - /// use chrono::naive::date::MIN; + /// use chrono::naive::MIN_DATE; /// use time::Duration; /// /// let d = NaiveDate::from_ymd(2015, 9, 5); @@ -826,7 +826,7 @@ impl NaiveDate { /// Some(NaiveDate::from_ymd(2015, 10, 15))); /// assert_eq!(d.checked_sub_signed(Duration::days(1_000_000_000)), None); /// assert_eq!(d.checked_sub_signed(Duration::days(-1_000_000_000)), None); - /// assert_eq!(MIN.checked_sub_signed(Duration::days(1)), None); + /// assert_eq!(MIN_DATE.checked_sub_signed(Duration::days(1)), None); /// # } /// ~~~~ pub fn checked_sub_signed(self, rhs: OldDuration) -> Option { @@ -951,7 +951,7 @@ impl NaiveDate { } impl Datelike for NaiveDate { - /// Returns the year number in the [calendar date](./index.html#calendar-date). + /// Returns the year number in the [calendar date](#calendar-date). /// /// # Example /// @@ -1013,7 +1013,7 @@ impl Datelike for NaiveDate { /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).day(), 14); /// ~~~~ /// - /// Combined with [`NaiveDate::pred`](./struct.NaiveDate.html#method.pred), + /// Combined with [`NaiveDate::pred`](#method.pred), /// one can determine the number of days in a particular month. /// (Note that this panics when `year` is out of range.) /// @@ -1070,7 +1070,7 @@ impl Datelike for NaiveDate { /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).ordinal(), 74); /// ~~~~ /// - /// Combined with [`NaiveDate::pred`](./struct.NaiveDate.html#method.pred), + /// Combined with [`NaiveDate::pred`](#method.pred), /// one can determine the number of days in a particular year. /// (Note that this panics when `year` is out of range.) /// @@ -1487,9 +1487,9 @@ fn test_encodable_json(to_string: F) Some(r#""0000-01-01""#.into())); assert_eq!(to_string(&NaiveDate::from_ymd(-1, 12, 31)).ok(), Some(r#""-0001-12-31""#.into())); - assert_eq!(to_string(&MIN).ok(), + assert_eq!(to_string(&MIN_DATE).ok(), Some(r#""-262144-01-01""#.into())); - assert_eq!(to_string(&MAX).ok(), + assert_eq!(to_string(&MAX_DATE).ok(), Some(r#""+262143-12-31""#.into())); } @@ -1505,8 +1505,8 @@ fn test_decodable_json(from_str: F) assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); assert_eq!(from_str(r#""-0001-12-31""#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); - assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(MIN)); - assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(MAX)); + assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(MIN_DATE)); + assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(MAX_DATE)); // bad formats assert!(from_str(r#""""#).is_err()); @@ -1562,7 +1562,7 @@ mod rustc_serialize { mod serde { use std::fmt; use super::NaiveDate; - use serde::{ser, de}; + use serdelib::{ser, de}; // TODO not very optimized for space (binary formats would want something better) @@ -1638,8 +1638,8 @@ mod serde { #[cfg(test)] mod tests { use super::NaiveDate; - use super::{MIN, MIN_YEAR, MIN_DAYS_FROM_YEAR_0}; - use super::{MAX, MAX_YEAR, MAX_DAYS_FROM_YEAR_0}; + use super::{MIN_DATE, MIN_YEAR, MIN_DAYS_FROM_YEAR_0}; + use super::{MAX_DATE, MAX_YEAR, MAX_DAYS_FROM_YEAR_0}; use {Datelike, Weekday}; use std::{i32, u32}; use oldtime::Duration; @@ -1778,10 +1778,10 @@ mod tests { assert_eq!(from_ndays_from_ce(days).map(|d| d.num_days_from_ce()), Some(days)); } - assert_eq!(from_ndays_from_ce(MIN.num_days_from_ce()), Some(MIN)); - assert_eq!(from_ndays_from_ce(MIN.num_days_from_ce() - 1), None); - assert_eq!(from_ndays_from_ce(MAX.num_days_from_ce()), Some(MAX)); - assert_eq!(from_ndays_from_ce(MAX.num_days_from_ce() + 1), None); + assert_eq!(from_ndays_from_ce(MIN_DATE.num_days_from_ce()), Some(MIN_DATE)); + assert_eq!(from_ndays_from_ce(MIN_DATE.num_days_from_ce() - 1), None); + assert_eq!(from_ndays_from_ce(MAX_DATE.num_days_from_ce()), Some(MAX_DATE)); + assert_eq!(from_ndays_from_ce(MAX_DATE.num_days_from_ce() + 1), None); } #[test] @@ -1887,7 +1887,7 @@ mod tests { assert_eq!(ymd(2014, 5, 31).succ_opt(), Some(ymd(2014, 6, 1))); assert_eq!(ymd(2014, 12, 31).succ_opt(), Some(ymd(2015, 1, 1))); assert_eq!(ymd(2016, 2, 28).succ_opt(), Some(ymd(2016, 2, 29))); - assert_eq!(ymd(MAX.year(), 12, 31).succ_opt(), None); + assert_eq!(ymd(MAX_DATE.year(), 12, 31).succ_opt(), None); } #[test] @@ -1897,7 +1897,7 @@ mod tests { assert_eq!(ymd(2015, 1, 1).pred_opt(), Some(ymd(2014, 12, 31))); assert_eq!(ymd(2014, 6, 1).pred_opt(), Some(ymd(2014, 5, 31))); assert_eq!(ymd(2014, 5, 7).pred_opt(), Some(ymd(2014, 5, 6))); - assert_eq!(ymd(MIN.year(), 1, 1).pred_opt(), None); + assert_eq!(ymd(MIN_DATE.year(), 1, 1).pred_opt(), None); } #[test] diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 0a00a8f..fbdd0eb 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -10,8 +10,7 @@ use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; use div::div_mod_floor; -use naive::time::NaiveTime; -use naive::date::NaiveDate; +use naive::{NaiveTime, NaiveDate}; use format::{Item, Numeric, Pad, Fixed}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; @@ -27,7 +26,7 @@ const MAX_SECS_BITS: usize = 44; /// /// # Example /// -/// `NaiveDateTime` is commonly created from [`NaiveDate`](../date/struct.NaiveDate.html). +/// `NaiveDateTime` is commonly created from [`NaiveDate`](./struct.NaiveDate.html). /// /// ~~~~ /// use chrono::{NaiveDate, NaiveDateTime}; @@ -74,7 +73,7 @@ impl Deref for TsSeconds { impl NaiveDateTime { /// Makes a new `NaiveDateTime` from date and time components. - /// Equivalent to [`date.and_time(time)`](../date/struct.NaiveDate.html#method.and_time) + /// Equivalent to [`date.and_time(time)`](./struct.NaiveDate.html#method.and_time) /// and many other helper constructors on `NaiveDate`. /// /// # Example @@ -103,7 +102,7 @@ impl NaiveDateTime { /// [`TimeZone::timestamp`](../../offset/trait.TimeZone.html#method.timestamp). /// /// The nanosecond part can exceed 1,000,000,000 in order to represent the - /// [leap second](../time/index.html#leap-second-handling). (The true "UNIX + /// [leap second](./struct.NaiveTime.html#leap-second-handling). (The true "UNIX /// timestamp" cannot represent a leap second unambiguously.) /// /// Panics on the out-of-range number of seconds and/or invalid nanosecond. @@ -131,7 +130,7 @@ impl NaiveDateTime { /// and the number of nanoseconds since the last whole non-leap second. /// /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](../time/index.html#leap-second-handling). + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) /// /// Returns `None` on the out-of-range number of seconds and/or invalid nanosecond. @@ -285,7 +284,7 @@ impl NaiveDateTime { /// Returns the number of milliseconds since the last whole non-leap second. /// /// The return value ranges from 0 to 999, - /// or for [leap seconds](../time/index.html#leap-second-handling), to 1,999. + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999. /// /// # Example /// @@ -306,7 +305,7 @@ impl NaiveDateTime { /// Returns the number of microseconds since the last whole non-leap second. /// /// The return value ranges from 0 to 999,999, - /// or for [leap seconds](../time/index.html#leap-second-handling), to 1,999,999. + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999. /// /// # Example /// @@ -327,7 +326,7 @@ impl NaiveDateTime { /// Returns the number of nanoseconds since the last whole non-leap second. /// /// The return value ranges from 0 to 999,999,999, - /// or for [leap seconds](../time/index.html#leap-second-handling), to 1,999,999,999. + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999,999. /// /// # Example /// @@ -347,7 +346,7 @@ impl NaiveDateTime { /// Adds given `Duration` to the current date and time. /// - /// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), + /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), /// the addition assumes that **there is no leap second ever**, /// except when the `NaiveDateTime` itself represents a leap second /// in which case the assumption becomes that **there is exactly a single leap second ever**. @@ -433,7 +432,7 @@ impl NaiveDateTime { /// Subtracts given `Duration` from the current date and time. /// - /// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), + /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), /// the subtraction assumes that **there is no leap second ever**, /// except when the `NaiveDateTime` itself represents a leap second /// in which case the assumption becomes that **there is exactly a single leap second ever**. @@ -516,7 +515,7 @@ impl NaiveDateTime { /// Subtracts another `NaiveDateTime` from the current date and time. /// This does not overflow or underflow at all. /// - /// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), + /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), /// the subtraction assumes that **there is no leap second ever**, /// except when any of the `NaiveDateTime`s themselves represents a leap second /// in which case the assumption becomes that @@ -635,7 +634,7 @@ impl NaiveDateTime { impl Datelike for NaiveDateTime { /// Returns the year number in the [calendar date](./index.html#calendar-date). /// - /// See also the [`NaiveDate::year`](../date/struct.NaiveDate.html#method.year) method. + /// See also the [`NaiveDate::year`](./struct.NaiveDate.html#method.year) method. /// /// # Example /// @@ -654,7 +653,7 @@ impl Datelike for NaiveDateTime { /// /// The return value ranges from 1 to 12. /// - /// See also the [`NaiveDate::month`](../date/struct.NaiveDate.html#method.month) method. + /// See also the [`NaiveDate::month`](./struct.NaiveDate.html#method.month) method. /// /// # Example /// @@ -673,7 +672,7 @@ impl Datelike for NaiveDateTime { /// /// The return value ranges from 0 to 11. /// - /// See also the [`NaiveDate::month0`](../date/struct.NaiveDate.html#method.month0) method. + /// See also the [`NaiveDate::month0`](./struct.NaiveDate.html#method.month0) method. /// /// # Example /// @@ -692,7 +691,7 @@ impl Datelike for NaiveDateTime { /// /// The return value ranges from 1 to 31. (The last day of month differs by months.) /// - /// See also the [`NaiveDate::day`](../date/struct.NaiveDate.html#method.day) method. + /// See also the [`NaiveDate::day`](./struct.NaiveDate.html#method.day) method. /// /// # Example /// @@ -711,7 +710,7 @@ impl Datelike for NaiveDateTime { /// /// The return value ranges from 0 to 30. (The last day of month differs by months.) /// - /// See also the [`NaiveDate::day0`](../date/struct.NaiveDate.html#method.day0) method. + /// See also the [`NaiveDate::day0`](./struct.NaiveDate.html#method.day0) method. /// /// # Example /// @@ -730,7 +729,7 @@ impl Datelike for NaiveDateTime { /// /// The return value ranges from 1 to 366. (The last day of year differs by years.) /// - /// See also the [`NaiveDate::ordinal`](../date/struct.NaiveDate.html#method.ordinal) method. + /// See also the [`NaiveDate::ordinal`](./struct.NaiveDate.html#method.ordinal) method. /// /// # Example /// @@ -749,7 +748,7 @@ impl Datelike for NaiveDateTime { /// /// The return value ranges from 0 to 365. (The last day of year differs by years.) /// - /// See also the [`NaiveDate::ordinal0`](../date/struct.NaiveDate.html#method.ordinal0) method. + /// See also the [`NaiveDate::ordinal0`](./struct.NaiveDate.html#method.ordinal0) method. /// /// # Example /// @@ -766,7 +765,7 @@ impl Datelike for NaiveDateTime { /// Returns the day of week. /// - /// See also the [`NaiveDate::weekday`](../date/struct.NaiveDate.html#method.weekday) method. + /// See also the [`NaiveDate::weekday`](./struct.NaiveDate.html#method.weekday) method. /// /// # Example /// @@ -791,7 +790,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_year`](../date/struct.NaiveDate.html#method.with_year) method. + /// [`NaiveDate::with_year`](./struct.NaiveDate.html#method.with_year) method. /// /// # Example /// @@ -812,7 +811,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_month`](../date/struct.NaiveDate.html#method.with_month) method. + /// [`NaiveDate::with_month`](./struct.NaiveDate.html#method.with_month) method. /// /// # Example /// @@ -834,7 +833,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_month0`](../date/struct.NaiveDate.html#method.with_month0) method. + /// [`NaiveDate::with_month0`](./struct.NaiveDate.html#method.with_month0) method. /// /// # Example /// @@ -856,7 +855,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_day`](../date/struct.NaiveDate.html#method.with_day) method. + /// [`NaiveDate::with_day`](./struct.NaiveDate.html#method.with_day) method. /// /// # Example /// @@ -877,7 +876,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_day0`](../date/struct.NaiveDate.html#method.with_day0) method. + /// [`NaiveDate::with_day0`](./struct.NaiveDate.html#method.with_day0) method. /// /// # Example /// @@ -898,7 +897,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_ordinal`](../date/struct.NaiveDate.html#method.with_ordinal) method. + /// [`NaiveDate::with_ordinal`](./struct.NaiveDate.html#method.with_ordinal) method. /// /// # Example /// @@ -926,7 +925,7 @@ impl Datelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveDate::with_ordinal0`](../date/struct.NaiveDate.html#method.with_ordinal0) method. + /// [`NaiveDate::with_ordinal0`](./struct.NaiveDate.html#method.with_ordinal0) method. /// /// # Example /// @@ -953,7 +952,7 @@ impl Datelike for NaiveDateTime { impl Timelike for NaiveDateTime { /// Returns the hour number from 0 to 23. /// - /// See also the [`NaiveTime::hour`](../time/struct.NaiveTime.html#method.hour) method. + /// See also the [`NaiveTime::hour`](./struct.NaiveTime.html#method.hour) method. /// /// # Example /// @@ -970,7 +969,7 @@ impl Timelike for NaiveDateTime { /// Returns the minute number from 0 to 59. /// - /// See also the [`NaiveTime::minute`](../time/struct.NaiveTime.html#method.minute) method. + /// See also the [`NaiveTime::minute`](./struct.NaiveTime.html#method.minute) method. /// /// # Example /// @@ -987,7 +986,7 @@ impl Timelike for NaiveDateTime { /// Returns the second number from 0 to 59. /// - /// See also the [`NaiveTime::second`](../time/struct.NaiveTime.html#method.second) method. + /// See also the [`NaiveTime::second`](./struct.NaiveTime.html#method.second) method. /// /// # Example /// @@ -1004,10 +1003,10 @@ impl Timelike for NaiveDateTime { /// Returns the number of nanoseconds since the whole non-leap second. /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./naive/time/index.html#leap-second-handling). + /// the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// See also the - /// [`NaiveTime::nanosecond`](../time/struct.NaiveTime.html#method.nanosecond) method. + /// [`NaiveTime::nanosecond`](./struct.NaiveTime.html#method.nanosecond) method. /// /// # Example /// @@ -1027,7 +1026,7 @@ impl Timelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveTime::with_hour`](../time/struct.NaiveTime.html#method.with_hour) method. + /// [`NaiveTime::with_hour`](./struct.NaiveTime.html#method.with_hour) method. /// /// # Example /// @@ -1049,7 +1048,7 @@ impl Timelike for NaiveDateTime { /// Returns `None` when the resulting `NaiveDateTime` would be invalid. /// /// See also the - /// [`NaiveTime::with_minute`](../time/struct.NaiveTime.html#method.with_minute) method. + /// [`NaiveTime::with_minute`](./struct.NaiveTime.html#method.with_minute) method. /// /// # Example /// @@ -1073,7 +1072,7 @@ impl Timelike for NaiveDateTime { /// the input range is restricted to 0 through 59. /// /// See also the - /// [`NaiveTime::with_second`](../time/struct.NaiveTime.html#method.with_second) method. + /// [`NaiveTime::with_second`](./struct.NaiveTime.html#method.with_second) method. /// /// # Example /// @@ -1097,7 +1096,7 @@ impl Timelike for NaiveDateTime { /// the input range can exceed 1,000,000,000 for leap seconds. /// /// See also the - /// [`NaiveTime::with_nanosecond`](../time/struct.NaiveTime.html#method.with_nanosecond) + /// [`NaiveTime::with_nanosecond`](./struct.NaiveTime.html#method.with_nanosecond) /// method. /// /// # Example @@ -1131,7 +1130,7 @@ impl hash::Hash for NaiveDateTime { /// An addition of `Duration` to `NaiveDateTime` yields another `NaiveDateTime`. /// -/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), +/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), /// the addition assumes that **there is no leap second ever**, /// except when the `NaiveDateTime` itself represents a leap second /// in which case the assumption becomes that **there is exactly a single leap second ever**. @@ -1196,7 +1195,7 @@ impl Add for NaiveDateTime { /// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`. /// It is same to the addition with a negated `Duration`. /// -/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling), +/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), /// the addition assumes that **there is no leap second ever**, /// except when the `NaiveDateTime` itself represents a leap second /// in which case the assumption becomes that **there is exactly a single leap second ever**. @@ -1365,7 +1364,7 @@ impl str::FromStr for NaiveDateTime { fn test_encodable_json(to_string: F) where F: Fn(&NaiveDateTime) -> Result, E: ::std::fmt::Debug { - use naive::date; + use naive::{MIN_DATE, MAX_DATE}; assert_eq!( to_string(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), @@ -1380,10 +1379,10 @@ fn test_encodable_json(to_string: F) to_string(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), Some(r#""-0001-12-31T23:59:59.000000007""#.into())); assert_eq!( - to_string(&date::MIN.and_hms(0, 0, 0)).ok(), + to_string(&MIN_DATE.and_hms(0, 0, 0)).ok(), Some(r#""-262144-01-01T00:00:00""#.into())); assert_eq!( - to_string(&date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), + to_string(&MAX_DATE.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), Some(r#""+262143-12-31T23:59:60.999999999""#.into())); } @@ -1391,7 +1390,7 @@ fn test_encodable_json(to_string: F) fn test_decodable_json(from_str: F) where F: Fn(&str) -> Result, E: ::std::fmt::Debug { - use naive::date; + use naive::{MIN_DATE, MAX_DATE}; assert_eq!( from_str(r#""2016-07-08T09:10:48.090""#).ok(), @@ -1413,13 +1412,13 @@ fn test_decodable_json(from_str: F) Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7))); assert_eq!( from_str(r#""-262144-01-01T00:00:00""#).ok(), - Some(date::MIN.and_hms(0, 0, 0))); + Some(MIN_DATE.and_hms(0, 0, 0))); assert_eq!( from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + Some(MAX_DATE.and_hms_nano(23, 59, 59, 1_999_999_999))); assert_eq!( from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(date::MAX.and_hms_nano(23, 59, 59, 1_999_999_999))); + Some(MAX_DATE.and_hms_nano(23, 59, 59, 1_999_999_999))); // bad formats assert!(from_str(r#""""#).is_err()); @@ -1510,11 +1509,11 @@ mod rustc_serialize { pub mod serde { use std::fmt; use super::{NaiveDateTime}; - use serde::{ser, de}; + use serdelib::{ser, de}; /// Serialize a NaiveDateTime as a string /// - /// See the [`ts_seconds`](./ts_seconds/index.html) module to serialize as + /// See the [`ts_seconds`](./serde/ts_seconds/index.html) module to serialize as /// a timestamp. impl ser::Serialize for NaiveDateTime { fn serialize(&self, serializer: S) -> Result @@ -1573,7 +1572,7 @@ pub mod serde { /// # extern crate serde; /// # extern crate chrono; /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, UTC}; - /// use chrono::naive::datetime::serde::ts_seconds; + /// use chrono::naive::serde::ts_seconds; /// #[derive(Deserialize, Serialize)] /// struct S { /// #[serde(with = "ts_seconds")] @@ -1596,7 +1595,7 @@ pub mod serde { /// ``` pub mod ts_seconds { use std::fmt; - use serde::{ser, de}; + use serdelib::{ser, de}; use NaiveDateTime; @@ -1617,7 +1616,7 @@ pub mod serde { /// # extern crate chrono; /// # use chrono::{NaiveDateTime, UTC}; /// # use serde::Deserialize; - /// use chrono::naive::datetime::serde::ts_seconds::deserialize as from_ts; + /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; /// #[derive(Deserialize)] /// struct S { /// #[serde(deserialize_with = "from_ts")] @@ -1653,7 +1652,7 @@ pub mod serde { /// # extern crate chrono; /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, UTC}; /// # use serde::Serialize; - /// use chrono::naive::datetime::serde::ts_seconds::serialize as to_ts; + /// use chrono::naive::serde::ts_seconds::serialize as to_ts; /// #[derive(Serialize)] /// struct S { /// #[serde(serialize_with = "to_ts")] @@ -1720,7 +1719,7 @@ pub mod serde { fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use naive::date::NaiveDate; + use naive::NaiveDate; use self::bincode::{Infinite, serialize, deserialize}; let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90); @@ -1734,8 +1733,7 @@ pub mod serde { mod tests { use super::NaiveDateTime; use Datelike; - use naive::date as naive_date; - use naive::date::NaiveDate; + use naive::{NaiveDate, MIN_DATE, MAX_DATE}; use std::i64; use oldtime::Duration; @@ -1772,17 +1770,15 @@ mod tests { // overflow check // assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`. // (they are private constants, but the equivalence is tested in that module.) - let max_days_from_year_0 = - naive_date::MAX.signed_duration_since(NaiveDate::from_ymd(0,1,1)); - check((0,1,1, 0,0,0), max_days_from_year_0, Some((naive_date::MAX.year(),12,31, 0,0,0))); + let max_days_from_year_0 = MAX_DATE.signed_duration_since(NaiveDate::from_ymd(0,1,1)); + check((0,1,1, 0,0,0), max_days_from_year_0, Some((MAX_DATE.year(),12,31, 0,0,0))); check((0,1,1, 0,0,0), max_days_from_year_0 + Duration::seconds(86399), - Some((naive_date::MAX.year(),12,31, 23,59,59))); + Some((MAX_DATE.year(),12,31, 23,59,59))); check((0,1,1, 0,0,0), max_days_from_year_0 + Duration::seconds(86400), None); check((0,1,1, 0,0,0), Duration::max_value(), None); - let min_days_from_year_0 = - naive_date::MIN.signed_duration_since(NaiveDate::from_ymd(0,1,1)); - check((0,1,1, 0,0,0), min_days_from_year_0, Some((naive_date::MIN.year(),1,1, 0,0,0))); + let min_days_from_year_0 = MIN_DATE.signed_duration_since(NaiveDate::from_ymd(0,1,1)); + check((0,1,1, 0,0,0), min_days_from_year_0, Some((MIN_DATE.year(),1,1, 0,0,0))); check((0,1,1, 0,0,0), min_days_from_year_0 - Duration::seconds(1), None); check((0,1,1, 0,0,0), Duration::min_value(), None); } diff --git a/src/naive/time.rs b/src/naive/time.rs index 9157e9a..6c4632e 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -2,159 +2,6 @@ // See README.md and LICENSE.txt for details. //! ISO 8601 time without timezone. -//! -//! # Leap Second Handling -//! -//! Since 1960s, the manmade atomic clock has been so accurate that -//! it is much more accurate than Earth's own motion. -//! It became desirable to define the civil time in terms of the atomic clock, -//! but that risks the desynchronization of the civil time from Earth. -//! To account for this, the designers of the Coordinated Universal Time (UTC) -//! made that the UTC should be kept within 0.9 seconds of the observed Earth-bound time. -//! When the mean solar day is longer than the ideal (86,400 seconds), -//! the error slowly accumulates and it is necessary to add a **leap second** -//! to slow the UTC down a bit. -//! (We may also remove a second to speed the UTC up a bit, but it never happened.) -//! The leap second, if any, follows 23:59:59 of June 30 or December 31 in the UTC. -//! -//! Fast forward to the 21st century, -//! we have seen 26 leap seconds from January 1972 to December 2015. -//! Yes, 26 seconds. Probably you can read this paragraph within 26 seconds. -//! But those 26 seconds, and possibly more in the future, are never predictable, -//! and whether to add a leap second or not is known only before 6 months. -//! Internet-based clocks (via NTP) do account for known leap seconds, -//! but the system API normally doesn't (and often can't, with no network connection) -//! and there is no reliable way to retrieve leap second information. -//! -//! Chrono does not try to accurately implement leap seconds; it is impossible. -//! Rather, **it allows for leap seconds but behaves as if there are *no other* leap seconds.** -//! Various operations will ignore any possible leap second(s) -//! except when any of the operands were actually leap seconds. -//! -//! If you cannot tolerate this behavior, -//! you must use a separate `TimeZone` for the International Atomic Time (TAI). -//! TAI is like UTC but has no leap seconds, and thus slightly differs from UTC. -//! Chrono 0.3 does not provide such implementation, but it is planned for 0.4. -//! -//! ## Representing Leap Seconds -//! -//! The leap second is indicated via fractional seconds more than 1 second. -//! This makes possible to treat a leap second as the prior non-leap second -//! if you don't care about sub-second accuracy. -//! You should use the proper formatting to get the raw leap second. -//! -//! All methods accepting fractional seconds will accept such values. -//! -//! ~~~~ -//! use chrono::{NaiveDate, NaiveTime, UTC, TimeZone}; -//! -//! let t = NaiveTime::from_hms_milli(8, 59, 59, 1_000); -//! -//! let dt1 = NaiveDate::from_ymd(2015, 7, 1).and_hms_micro(8, 59, 59, 1_000_000); -//! -//! let dt2 = UTC.ymd(2015, 6, 30).and_hms_nano(23, 59, 59, 1_000_000_000); -//! # let _ = (t, dt1, dt2); -//! ~~~~ -//! -//! Note that the leap second can happen anytime given an appropriate time zone; -//! 2015-07-01 01:23:60 would be a proper leap second if UTC+01:24 had existed. -//! Practically speaking, though, by the time of the first leap second on 1972-06-30, -//! every time zone offset around the world has standardized to the 5-minute alignment. -//! -//! ## Date And Time Arithmetics -//! -//! As a concrete example, let's assume that `03:00:60` and `04:00:60` are leap seconds. -//! (In reality, of course, leap seconds are separated by at least 6 months.) -//! -//! `Time + Duration`: -//! -//! - `03:00:00 + 1s = 03:00:01`. -//! - `03:00:59 + 60s = 03:02:00`. -//! - `03:00:59 + 1s = 03:01:00`. -//! - `03:00:60 + 1s = 03:01:00`. -//! Note that the sum is identical to the previous. -//! - `03:00:60 + 60s = 03:01:59`. -//! - `03:00:60 + 61s = 03:02:00`. -//! - `03:00:60.1 + 0.8s = 03:00:60.9`. -//! -//! `Time - Duration`: -//! -//! - `03:00:00 - 1s = 02:59:59`. -//! - `03:01:00 - 1s = 03:00:59`. -//! - `03:01:00 - 60s = 03:00:00`. -//! - `03:00:60 - 60s = 03:00:00`. -//! Note that the result is identical to the previous. -//! - `03:00:60.7 - 0.4s = 03:00:60.3`. -//! - `03:00:60.7 - 0.9s = 03:00:59.8`. -//! -//! `Time - Time`: -//! -//! - `04:00:00 - 03:00:00 = 3600s`. -//! - `03:01:00 - 03:00:00 = 60s`. -//! - `03:00:60 - 03:00:00 = 60s`. -//! Note that the difference is identical to the previous. -//! - `03:00:60.6 - 03:00:59.4 = 1.2s`. -//! - `03:01:00 - 03:00:59.8 = 0.2s`. -//! - `03:01:00 - 03:00:60.5 = 0.5s`. -//! Note that the difference is larger than the previous, -//! even though the leap second clearly follows the previous whole second. -//! - `04:00:60.9 - 03:00:60.1 = -//! (04:00:60.9 - 04:00:00) + (04:00:00 - 03:01:00) + (03:01:00 - 03:00:60.1) = -//! 60.9s + 3540s + 0.9s = 3601.8s`. -//! -//! In general, -//! -//! - `Time + Duration` unconditionally equals to `Duration + Time`. -//! -//! - `Time - Duration` unconditionally equals to `Time + (-Duration)`. -//! -//! - `Time1 - Time2` unconditionally equals to `-(Time2 - Time1)`. -//! -//! - Associativity does not generally hold, because -//! `(Time + Duration1) - Duration2` no longer equals to `Time + (Duration1 - Duration2)` -//! for two positive durations. -//! -//! - As a special case, `(Time + Duration) - Duration` also does not equal to `Time`. -//! -//! - If you can assume that all durations have the same sign, however, -//! then the associativity holds: -//! `(Time + Duration1) + Duration2` equals to `Time + (Duration1 + Duration2)` -//! for two positive durations. -//! -//! ## Reading And Writing Leap Seconds -//! -//! The "typical" leap seconds on the minute boundary are -//! correctly handled both in the formatting and parsing. -//! The leap second in the human-readable representation -//! will be represented as the second part being 60, as required by ISO 8601. -//! -//! ~~~~ -//! use chrono::{UTC, TimeZone}; -//! -//! let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_000); -//! assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z"); -//! ~~~~ -//! -//! There are hypothetical leap seconds not on the minute boundary -//! nevertheless supported by Chrono. -//! They are allowed for the sake of completeness and consistency; -//! there were several "exotic" time zone offsets with fractional minutes prior to UTC after all. -//! For such cases the human-readable representation is ambiguous -//! and would be read back to the next non-leap second. -//! -//! ~~~~ -//! use chrono::{DateTime, UTC, TimeZone}; -//! -//! let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000); -//! assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); -//! -//! let dt = UTC.ymd(2015, 6, 30).and_hms(23, 56, 5); -//! assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); -//! assert_eq!(DateTime::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); -//! ~~~~ -//! -//! Since Chrono alone cannot determine any existence of leap seconds, -//! **there is absolutely no guarantee that the leap second read has actually happened**. use std::{str, fmt, hash}; use std::ops::{Add, Sub}; @@ -168,9 +15,158 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// ISO 8601 time without timezone. /// Allows for the nanosecond precision and optional leap second representation. /// -/// -/// Chrono has a notable policy on the [leap second handling](./index.html#leap-second-handling), -/// designed to be maximally useful for typical users. +/// # Leap Second Handling +/// +/// Since 1960s, the manmade atomic clock has been so accurate that +/// it is much more accurate than Earth's own motion. +/// It became desirable to define the civil time in terms of the atomic clock, +/// but that risks the desynchronization of the civil time from Earth. +/// To account for this, the designers of the Coordinated Universal Time (UTC) +/// made that the UTC should be kept within 0.9 seconds of the observed Earth-bound time. +/// When the mean solar day is longer than the ideal (86,400 seconds), +/// the error slowly accumulates and it is necessary to add a **leap second** +/// to slow the UTC down a bit. +/// (We may also remove a second to speed the UTC up a bit, but it never happened.) +/// The leap second, if any, follows 23:59:59 of June 30 or December 31 in the UTC. +/// +/// Fast forward to the 21st century, +/// we have seen 26 leap seconds from January 1972 to December 2015. +/// Yes, 26 seconds. Probably you can read this paragraph within 26 seconds. +/// But those 26 seconds, and possibly more in the future, are never predictable, +/// and whether to add a leap second or not is known only before 6 months. +/// Internet-based clocks (via NTP) do account for known leap seconds, +/// but the system API normally doesn't (and often can't, with no network connection) +/// and there is no reliable way to retrieve leap second information. +/// +/// Chrono does not try to accurately implement leap seconds; it is impossible. +/// Rather, **it allows for leap seconds but behaves as if there are *no other* leap seconds.** +/// Various operations will ignore any possible leap second(s) +/// except when any of the operands were actually leap seconds. +/// +/// If you cannot tolerate this behavior, +/// you must use a separate `TimeZone` for the International Atomic Time (TAI). +/// TAI is like UTC but has no leap seconds, and thus slightly differs from UTC. +/// Chrono 0.3 does not provide such implementation, but it is planned for 0.4. +/// +/// ## Representing Leap Seconds +/// +/// The leap second is indicated via fractional seconds more than 1 second. +/// This makes possible to treat a leap second as the prior non-leap second +/// if you don't care about sub-second accuracy. +/// You should use the proper formatting to get the raw leap second. +/// +/// All methods accepting fractional seconds will accept such values. +/// +/// ~~~~ +/// use chrono::{NaiveDate, NaiveTime, UTC, TimeZone}; +/// +/// let t = NaiveTime::from_hms_milli(8, 59, 59, 1_000); +/// +/// let dt1 = NaiveDate::from_ymd(2015, 7, 1).and_hms_micro(8, 59, 59, 1_000_000); +/// +/// let dt2 = UTC.ymd(2015, 6, 30).and_hms_nano(23, 59, 59, 1_000_000_000); +/// # let _ = (t, dt1, dt2); +/// ~~~~ +/// +/// Note that the leap second can happen anytime given an appropriate time zone; +/// 2015-07-01 01:23:60 would be a proper leap second if UTC+01:24 had existed. +/// Practically speaking, though, by the time of the first leap second on 1972-06-30, +/// every time zone offset around the world has standardized to the 5-minute alignment. +/// +/// ## Date And Time Arithmetics +/// +/// As a concrete example, let's assume that `03:00:60` and `04:00:60` are leap seconds. +/// (In reality, of course, leap seconds are separated by at least 6 months.) +/// +/// `Time + Duration`: +/// +/// - `03:00:00 + 1s = 03:00:01`. +/// - `03:00:59 + 60s = 03:02:00`. +/// - `03:00:59 + 1s = 03:01:00`. +/// - `03:00:60 + 1s = 03:01:00`. +/// Note that the sum is identical to the previous. +/// - `03:00:60 + 60s = 03:01:59`. +/// - `03:00:60 + 61s = 03:02:00`. +/// - `03:00:60.1 + 0.8s = 03:00:60.9`. +/// +/// `Time - Duration`: +/// +/// - `03:00:00 - 1s = 02:59:59`. +/// - `03:01:00 - 1s = 03:00:59`. +/// - `03:01:00 - 60s = 03:00:00`. +/// - `03:00:60 - 60s = 03:00:00`. +/// Note that the result is identical to the previous. +/// - `03:00:60.7 - 0.4s = 03:00:60.3`. +/// - `03:00:60.7 - 0.9s = 03:00:59.8`. +/// +/// `Time - Time`: +/// +/// - `04:00:00 - 03:00:00 = 3600s`. +/// - `03:01:00 - 03:00:00 = 60s`. +/// - `03:00:60 - 03:00:00 = 60s`. +/// Note that the difference is identical to the previous. +/// - `03:00:60.6 - 03:00:59.4 = 1.2s`. +/// - `03:01:00 - 03:00:59.8 = 0.2s`. +/// - `03:01:00 - 03:00:60.5 = 0.5s`. +/// Note that the difference is larger than the previous, +/// even though the leap second clearly follows the previous whole second. +/// - `04:00:60.9 - 03:00:60.1 = +/// (04:00:60.9 - 04:00:00) + (04:00:00 - 03:01:00) + (03:01:00 - 03:00:60.1) = +/// 60.9s + 3540s + 0.9s = 3601.8s`. +/// +/// In general, +/// +/// - `Time + Duration` unconditionally equals to `Duration + Time`. +/// +/// - `Time - Duration` unconditionally equals to `Time + (-Duration)`. +/// +/// - `Time1 - Time2` unconditionally equals to `-(Time2 - Time1)`. +/// +/// - Associativity does not generally hold, because +/// `(Time + Duration1) - Duration2` no longer equals to `Time + (Duration1 - Duration2)` +/// for two positive durations. +/// +/// - As a special case, `(Time + Duration) - Duration` also does not equal to `Time`. +/// +/// - If you can assume that all durations have the same sign, however, +/// then the associativity holds: +/// `(Time + Duration1) + Duration2` equals to `Time + (Duration1 + Duration2)` +/// for two positive durations. +/// +/// ## Reading And Writing Leap Seconds +/// +/// The "typical" leap seconds on the minute boundary are +/// correctly handled both in the formatting and parsing. +/// The leap second in the human-readable representation +/// will be represented as the second part being 60, as required by ISO 8601. +/// +/// ~~~~ +/// use chrono::{UTC, TimeZone}; +/// +/// let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_000); +/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z"); +/// ~~~~ +/// +/// There are hypothetical leap seconds not on the minute boundary +/// nevertheless supported by Chrono. +/// They are allowed for the sake of completeness and consistency; +/// there were several "exotic" time zone offsets with fractional minutes prior to UTC after all. +/// For such cases the human-readable representation is ambiguous +/// and would be read back to the next non-leap second. +/// +/// ~~~~ +/// use chrono::{DateTime, UTC, TimeZone}; +/// +/// let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000); +/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); +/// +/// let dt = UTC.ymd(2015, 6, 30).and_hms(23, 56, 5); +/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); +/// assert_eq!(DateTime::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); +/// ~~~~ +/// +/// Since Chrono alone cannot determine any existence of leap seconds, +/// **there is absolutely no guarantee that the leap second read has actually happened**. #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub struct NaiveTime { secs: u32, @@ -834,7 +830,7 @@ impl Timelike for NaiveTime { /// Returns the number of nanoseconds since the whole non-leap second. /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./naive/time/index.html#leap-second-handling). + /// the [leap second](#leap-second-handling). /// /// # Example /// @@ -1337,7 +1333,7 @@ mod rustc_serialize { mod serde { use std::fmt; use super::NaiveTime; - use serde::{ser, de}; + use serdelib::{ser, de}; // TODO not very optimized for space (binary formats would want something better) // TODO round-trip for general leap seconds (not just those with second = 60) diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 884da6c..168880c 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -9,10 +9,8 @@ use oldtime::Duration as OldDuration; use Timelike; use div::div_mod_floor; -use naive::time::NaiveTime; -use naive::date::NaiveDate; -use naive::datetime::NaiveDateTime; -use datetime::DateTime; +use naive::{NaiveTime, NaiveDate, NaiveDateTime}; +use DateTime; use super::{TimeZone, Offset, LocalResult}; /// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. diff --git a/src/offset/local.rs b/src/offset/local.rs index 800f1c9..77f1413 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -6,11 +6,8 @@ use oldtime; use {Datelike, Timelike}; -use naive::date::NaiveDate; -use naive::time::NaiveTime; -use naive::datetime::NaiveDateTime; -use date::Date; -use datetime::DateTime; +use naive::{NaiveDate, NaiveTime, NaiveDateTime}; +use {Date, DateTime}; use super::{TimeZone, LocalResult}; use super::fixed::FixedOffset; diff --git a/src/offset/mod.rs b/src/offset/mod.rs index 965497a..bc51366 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -1,35 +1,29 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -/*! - * The time zone, which calculates offsets from the local time to UTC. - * - * There are four operations provided by the `TimeZone` trait: - * - * 1. Converting the local `NaiveDateTime` to `DateTime` - * 2. Converting the UTC `NaiveDateTime` to `DateTime` - * 3. Converting `DateTime` to the local `NaiveDateTime` - * 4. Constructing `DateTime` objects from various offsets - * - * 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types. - * 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type - * which implements `Offset` (which then passed to `TimeZone` for actual implementations). - * Technically speaking `TimeZone` has a total knowledge about given timescale, - * but `Offset` is used as a cache to avoid the repeated conversion - * and provides implementations for 1 and 3. - * An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance. - */ +//! The time zone, which calculates offsets from the local time to UTC. +//! +//! There are four operations provided by the `TimeZone` trait: +//! +//! 1. Converting the local `NaiveDateTime` to `DateTime` +//! 2. Converting the UTC `NaiveDateTime` to `DateTime` +//! 3. Converting `DateTime` to the local `NaiveDateTime` +//! 4. Constructing `DateTime` objects from various offsets +//! +//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types. +//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type +//! which implements `Offset` (which then passed to `TimeZone` for actual implementations). +//! Technically speaking `TimeZone` has a total knowledge about given timescale, +//! but `Offset` is used as a cache to avoid the repeated conversion +//! and provides implementations for 1 and 3. +//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance. use std::fmt; use Weekday; -use naive::date::NaiveDate; -use naive::time::NaiveTime; -use naive::datetime::NaiveDateTime; -use date::Date; -use datetime::DateTime; +use naive::{NaiveDate, NaiveTime, NaiveDateTime}; +use {Date, DateTime}; use format::{parse, Parsed, ParseResult, StrftimeItems}; -use self::fixed::FixedOffset; /// The conversion result from the local time to the timezone-aware datetime types. #[derive(Clone, PartialEq, Debug)] @@ -165,8 +159,8 @@ pub trait Offset: Sized + Clone + fmt::Debug { /// The time zone. /// -/// The methods here are the primarily constructors for [`Date`](../date/struct.Date.html) and -/// [`DateTime`](../datetime/struct.DateTime.html) types. +/// The methods here are the primarily constructors for [`Date`](../struct.Date.html) and +/// [`DateTime`](../struct.DateTime.html) types. pub trait TimeZone: Sized + Clone { /// An associated offset type. /// This type is used to store the actual offset in date and time types. @@ -373,7 +367,11 @@ pub trait TimeZone: Sized + Clone { } } -pub mod utc; -pub mod fixed; -pub mod local; +mod utc; +mod fixed; +mod local; + +pub use self::utc::UTC; +pub use self::fixed::FixedOffset; +pub use self::local::Local; diff --git a/src/offset/utc.rs b/src/offset/utc.rs index aa028fc..bb8a029 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -6,12 +6,9 @@ use std::fmt; use oldtime; -use naive::date::NaiveDate; -use naive::datetime::NaiveDateTime; -use date::Date; -use datetime::DateTime; -use super::{TimeZone, Offset, LocalResult}; -use super::fixed::FixedOffset; +use naive::{NaiveDate, NaiveDateTime}; +use {Date, DateTime}; +use super::{TimeZone, Offset, LocalResult, FixedOffset}; /// The UTC time zone. This is the most efficient time zone when you don't need the local time. /// It is also used as an offset (which is also a dummy type). From 4f51ab0281daa434d09ddac569eb78c4670fb31f Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 21 Jun 2017 21:20:35 +0900 Subject: [PATCH 48/53] Moved `chrono::naive::date::internals` into its own file. --- src/lib.rs | 2 + src/naive/date.rs | 777 +--------------------------------------- src/naive/internals.rs | 778 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 781 insertions(+), 776 deletions(-) create mode 100644 src/naive/internals.rs diff --git a/src/lib.rs b/src/lib.rs index 0266a1a..fc60760 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,6 +392,8 @@ pub mod naive { //! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)), //! but can be also used for the simpler date and time handling. + mod internals; + // avoid using them directly even in the crate itself #[path = "date.rs"] mod date_; #[path = "time.rs"] mod time_; diff --git a/src/naive/date.rs b/src/naive/date.rs index db7d5e1..277b6c2 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -14,7 +14,7 @@ use naive::{NaiveTime, NaiveDateTime}; use format::{Item, Numeric, Pad}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; -use self::internals::{DateImpl, Of, Mdf, YearFlags}; +use super::internals::{self, DateImpl, Of, Mdf, YearFlags}; const MAX_YEAR: i32 = internals::MAX_YEAR; const MIN_YEAR: i32 = internals::MIN_YEAR; @@ -2060,778 +2060,3 @@ mod tests { } } -/// The internal implementation of the calendar and ordinal date. -/// -/// The current implementation is optimized for determining year, month, day and day of week. -/// 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar, -/// which are included in every packed `NaiveDate` instance. -/// The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is -/// based on the moderately-sized lookup table (~1.5KB) -/// and the packed representation is chosen for the efficient lookup. -/// Every internal data structure does not validate its input, -/// but the conversion keeps the valid value valid and the invalid value invalid -/// so that the user-facing `NaiveDate` can validate the input as late as possible. -#[allow(dead_code)] // some internal methods have been left for consistency -mod internals { - use std::{i32, fmt}; - use num::traits::FromPrimitive; - use Weekday; - use div::{div_rem, mod_floor}; - - /// The internal date representation. This also includes the packed `Mdf` value. - pub type DateImpl = i32; - - pub const MAX_YEAR: DateImpl = i32::MAX >> 13; - pub const MIN_YEAR: DateImpl = i32::MIN >> 13; - - /// The year flags (aka the dominical letter). - /// - /// There are 14 possible classes of year in the Gregorian calendar: - /// common and leap years starting with Monday through Sunday. - /// The `YearFlags` stores this information into 4 bits `abbb`, - /// where `a` is `1` for the common year (simplifies the `Of` validation) - /// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year - /// (simplifies the day of week calculation from the 1-based ordinal). - #[derive(PartialEq, Eq, Copy, Clone)] - pub struct YearFlags(pub u8); - - pub const A: YearFlags = YearFlags(0o15); pub const AG: YearFlags = YearFlags(0o05); - pub const B: YearFlags = YearFlags(0o14); pub const BA: YearFlags = YearFlags(0o04); - pub const C: YearFlags = YearFlags(0o13); pub const CB: YearFlags = YearFlags(0o03); - pub const D: YearFlags = YearFlags(0o12); pub const DC: YearFlags = YearFlags(0o02); - pub const E: YearFlags = YearFlags(0o11); pub const ED: YearFlags = YearFlags(0o01); - pub const F: YearFlags = YearFlags(0o17); pub const FE: YearFlags = YearFlags(0o07); - pub const G: YearFlags = YearFlags(0o16); pub const GF: YearFlags = YearFlags(0o06); - - static YEAR_TO_FLAGS: [YearFlags; 400] = [ - BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, - ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, - AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, - DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, - GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100 - C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, - GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, - CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, - FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, - BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200 - E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, - BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, - ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, - AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, - DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300 - G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, - DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, - GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, - CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, - FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400 - ]; - - static YEAR_DELTAS: [u8; 401] = [ - 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, - 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, - 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100 - 25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, - 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, - 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, - 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, - 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, // 200 - 49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, - 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, - 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, - 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, - 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, // 300 - 73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, - 77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, - 82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, - 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, - 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97 // 400+1 - ]; - - pub fn cycle_to_yo(cycle: u32) -> (u32, u32) { - let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365); - let delta = YEAR_DELTAS[year_mod_400 as usize] as u32; - if ordinal0 < delta { - year_mod_400 -= 1; - ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32; - } else { - ordinal0 -= delta; - } - (year_mod_400, ordinal0 + 1) - } - - pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { - year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1 - } - - impl YearFlags { - #[inline] - pub fn from_year(year: i32) -> YearFlags { - let year = mod_floor(year, 400); - YearFlags::from_year_mod_400(year) - } - - #[inline] - pub fn from_year_mod_400(year: i32) -> YearFlags { - YEAR_TO_FLAGS[year as usize] - } - - #[inline] - pub fn ndays(&self) -> u32 { - let YearFlags(flags) = *self; - 366 - (flags >> 3) as u32 - } - - #[inline] - pub fn isoweek_delta(&self) -> u32 { - let YearFlags(flags) = *self; - let mut delta = flags as u32 & 0b111; - if delta < 3 { delta += 7; } - delta - } - - #[inline] - pub fn nisoweeks(&self) -> u32 { - let YearFlags(flags) = *self; - 52 + ((0b00000100_00000110 >> flags as usize) & 1) - } - } - - impl fmt::Debug for YearFlags { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let YearFlags(flags) = *self; - match flags { - 0o15 => "A".fmt(f), 0o05 => "AG".fmt(f), - 0o14 => "B".fmt(f), 0o04 => "BA".fmt(f), - 0o13 => "C".fmt(f), 0o03 => "CB".fmt(f), - 0o12 => "D".fmt(f), 0o02 => "DC".fmt(f), - 0o11 => "E".fmt(f), 0o01 => "ED".fmt(f), - 0o10 => "F?".fmt(f), 0o00 => "FE?".fmt(f), // non-canonical - 0o17 => "F".fmt(f), 0o07 => "FE".fmt(f), - 0o16 => "G".fmt(f), 0o06 => "GF".fmt(f), - _ => write!(f, "YearFlags({})", flags), - } - } - } - - pub const MIN_OL: u32 = 1 << 1; - pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` - pub const MIN_MDL: u32 = (1 << 6) | (1 << 1); - pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; - - const XX: i8 = -128; - static MDL_TO_OL: [i8; (MAX_MDL as usize + 1)] = [ - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0 - XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 - XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2 - XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3 - XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4 - XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5 - XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6 - XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7 - XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8 - XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9 - XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10 - XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11 - XX, XX, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, - 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, - 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, - 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, // 12 - ]; - - static OL_TO_MDL: [u8; (MAX_OL as usize + 1)] = [ - 0, 0, // 0 - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, // 2 - 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3 - 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4 - 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5 - 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6 - 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7 - 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8 - 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9 - 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10 - 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11 - 100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, - 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, - 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, - 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98, // 12 - ]; - - /// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`. - /// - /// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag), - /// which is an index to the `OL_TO_MDL` lookup table. - #[derive(PartialEq, PartialOrd, Copy, Clone)] - pub struct Of(pub u32); - - impl Of { - #[inline] - fn clamp_ordinal(ordinal: u32) -> u32 { - if ordinal > 366 {0} else {ordinal} - } - - #[inline] - pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of { - let ordinal = Of::clamp_ordinal(ordinal); - Of((ordinal << 4) | (flags as u32)) - } - - #[inline] - pub fn from_mdf(Mdf(mdf): Mdf) -> Of { - let mdl = mdf >> 3; - match MDL_TO_OL.get(mdl as usize) { - Some(&v) => Of(mdf.wrapping_sub((v as i32 as u32 & 0x3ff) << 3)), - None => Of(0) - } - } - - #[inline] - pub fn valid(&self) -> bool { - let Of(of) = *self; - let ol = of >> 3; - MIN_OL <= ol && ol <= MAX_OL - } - - #[inline] - pub fn ordinal(&self) -> u32 { - let Of(of) = *self; - of >> 4 - } - - #[inline] - pub fn with_ordinal(&self, ordinal: u32) -> Of { - let ordinal = Of::clamp_ordinal(ordinal); - let Of(of) = *self; - Of((of & 0b1111) | (ordinal << 4)) - } - - #[inline] - pub fn flags(&self) -> YearFlags { - let Of(of) = *self; - YearFlags((of & 0b1111) as u8) - } - - #[inline] - pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of { - let Of(of) = *self; - Of((of & !0b1111) | (flags as u32)) - } - - #[inline] - pub fn weekday(&self) -> Weekday { - let Of(of) = *self; - Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap() - } - - #[inline] - pub fn isoweekdate_raw(&self) -> (u32, Weekday) { - // week ordinal = ordinal + delta - let Of(of) = *self; - let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta()); - (weekord / 7, Weekday::from_u32(weekord % 7).unwrap()) - } - - #[inline] - pub fn to_mdf(&self) -> Mdf { - Mdf::from_of(*self) - } - - #[inline] - pub fn succ(&self) -> Of { - let Of(of) = *self; - Of(of + (1 << 4)) - } - - #[inline] - pub fn pred(&self) -> Of { - let Of(of) = *self; - Of(of - (1 << 4)) - } - } - - impl fmt::Debug for Of { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let Of(of) = *self; - write!(f, "Of(({} << 4) | {:#04o} /*{:?}*/)", - of >> 4, of & 0b1111, YearFlags((of & 0b1111) as u8)) - } - } - - /// Month, day of month and year flags: `(month << 9) | (day << 4) | flags` - /// - /// The whole bits except for the least 3 bits are referred as `Mdl` - /// (month, day of month and leap flag), - /// which is an index to the `MDL_TO_OL` lookup table. - #[derive(PartialEq, PartialOrd, Copy, Clone)] - pub struct Mdf(pub u32); - - impl Mdf { - #[inline] - fn clamp_month(month: u32) -> u32 { - if month > 12 {0} else {month} - } - - #[inline] - fn clamp_day(day: u32) -> u32 { - if day > 31 {0} else {day} - } - - #[inline] - pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf { - let month = Mdf::clamp_month(month); - let day = Mdf::clamp_day(day); - Mdf((month << 9) | (day << 4) | (flags as u32)) - } - - #[inline] - pub fn from_of(Of(of): Of) -> Mdf { - let ol = of >> 3; - match OL_TO_MDL.get(ol as usize) { - Some(&v) => Mdf(of + ((v as u32) << 3)), - None => Mdf(0) - } - } - - #[inline] - pub fn valid(&self) -> bool { - let Mdf(mdf) = *self; - let mdl = mdf >> 3; - match MDL_TO_OL.get(mdl as usize) { - Some(&v) => v >= 0, - None => false - } - } - - #[inline] - pub fn month(&self) -> u32 { - let Mdf(mdf) = *self; - mdf >> 9 - } - - #[inline] - pub fn with_month(&self, month: u32) -> Mdf { - let month = Mdf::clamp_month(month); - let Mdf(mdf) = *self; - Mdf((mdf & 0b11111_1111) | (month << 9)) - } - - #[inline] - pub fn day(&self) -> u32 { - let Mdf(mdf) = *self; - (mdf >> 4) & 0b11111 - } - - #[inline] - pub fn with_day(&self, day: u32) -> Mdf { - let day = Mdf::clamp_day(day); - let Mdf(mdf) = *self; - Mdf((mdf & !0b11111_0000) | (day << 4)) - } - - #[inline] - pub fn flags(&self) -> YearFlags { - let Mdf(mdf) = *self; - YearFlags((mdf & 0b1111) as u8) - } - - #[inline] - pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { - let Mdf(mdf) = *self; - Mdf((mdf & !0b1111) | (flags as u32)) - } - - #[inline] - pub fn to_of(&self) -> Of { - Of::from_mdf(*self) - } - } - - impl fmt::Debug for Mdf { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let Mdf(mdf) = *self; - write!(f, "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)", - mdf >> 9, (mdf >> 4) & 0b11111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8)) - } - } - - #[cfg(test)] - mod tests { - #[cfg(bench)] extern crate test; - - use Weekday; - use super::{Of, Mdf}; - use super::{YearFlags, A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF}; - use num::iter::range_inclusive; - use std::u32; - - const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G]; - const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF]; - const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF]; - - #[test] - fn test_year_flags_ndays_from_year() { - assert_eq!(YearFlags::from_year(2014).ndays(), 365); - assert_eq!(YearFlags::from_year(2012).ndays(), 366); - assert_eq!(YearFlags::from_year(2000).ndays(), 366); - assert_eq!(YearFlags::from_year(1900).ndays(), 365); - assert_eq!(YearFlags::from_year(1600).ndays(), 366); - assert_eq!(YearFlags::from_year( 1).ndays(), 365); - assert_eq!(YearFlags::from_year( 0).ndays(), 366); // 1 BCE (proleptic Gregorian) - assert_eq!(YearFlags::from_year( -1).ndays(), 365); // 2 BCE - assert_eq!(YearFlags::from_year( -4).ndays(), 366); // 5 BCE - assert_eq!(YearFlags::from_year( -99).ndays(), 365); // 100 BCE - assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE - assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE - assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE - } - - #[test] - fn test_year_flags_nisoweeks() { - assert_eq!(A.nisoweeks(), 52); - assert_eq!(B.nisoweeks(), 52); - assert_eq!(C.nisoweeks(), 52); - assert_eq!(D.nisoweeks(), 53); - assert_eq!(E.nisoweeks(), 52); - assert_eq!(F.nisoweeks(), 52); - assert_eq!(G.nisoweeks(), 52); - assert_eq!(AG.nisoweeks(), 52); - assert_eq!(BA.nisoweeks(), 52); - assert_eq!(CB.nisoweeks(), 52); - assert_eq!(DC.nisoweeks(), 53); - assert_eq!(ED.nisoweeks(), 53); - assert_eq!(FE.nisoweeks(), 52); - assert_eq!(GF.nisoweeks(), 52); - } - - #[cfg(bench)] - #[bench] - fn bench_year_flags_from_year(bh: &mut test::Bencher) { - bh.iter(|| { - for year in -999i32..1000 { - YearFlags::from_year(year); - } - }); - } - - #[test] - fn test_of() { - fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) { - for ordinal in range_inclusive(ordinal1, ordinal2) { - let of = Of::new(ordinal, flags); - assert!(of.valid() == expected, - "ordinal {} = {:?} should be {} for dominical year {:?}", - ordinal, of, if expected {"valid"} else {"invalid"}, flags); - } - } - - for &flags in NONLEAP_FLAGS.iter() { - check(false, flags, 0, 0); - check(true, flags, 1, 365); - check(false, flags, 366, 1024); - check(false, flags, u32::MAX, u32::MAX); - } - - for &flags in LEAP_FLAGS.iter() { - check(false, flags, 0, 0); - check(true, flags, 1, 366); - check(false, flags, 367, 1024); - check(false, flags, u32::MAX, u32::MAX); - } - } - - #[test] - fn test_mdf_valid() { - fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, - month2: u32, day2: u32) { - for month in range_inclusive(month1, month2) { - for day in range_inclusive(day1, day2) { - let mdf = Mdf::new(month, day, flags); - assert!(mdf.valid() == expected, - "month {} day {} = {:?} should be {} for dominical year {:?}", - month, day, mdf, if expected {"valid"} else {"invalid"}, flags); - } - } - } - - for &flags in NONLEAP_FLAGS.iter() { - check(false, flags, 0, 0, 0, 1024); - check(false, flags, 0, 0, 16, 0); - check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024); - check(true, flags, 2, 1, 2, 28); check(false, flags, 2, 29, 2, 1024); - check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024); - check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024); - check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024); - check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024); - check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024); - check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024); - check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024); - check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024); - check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024); - check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024); - check(false, flags, 13, 0, 16, 1024); - check(false, flags, u32::MAX, 0, u32::MAX, 1024); - check(false, flags, 0, u32::MAX, 16, u32::MAX); - check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); - } - - for &flags in LEAP_FLAGS.iter() { - check(false, flags, 0, 0, 0, 1024); - check(false, flags, 0, 0, 16, 0); - check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024); - check(true, flags, 2, 1, 2, 29); check(false, flags, 2, 30, 2, 1024); - check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024); - check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024); - check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024); - check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024); - check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024); - check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024); - check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024); - check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024); - check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024); - check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024); - check(false, flags, 13, 0, 16, 1024); - check(false, flags, u32::MAX, 0, u32::MAX, 1024); - check(false, flags, 0, u32::MAX, 16, u32::MAX); - check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); - } - } - - #[test] - fn test_of_fields() { - for &flags in FLAGS.iter() { - for ordinal in range_inclusive(1u32, 366) { - let of = Of::new(ordinal, flags); - if of.valid() { - assert_eq!(of.ordinal(), ordinal); - } - } - } - } - - #[test] - fn test_of_with_fields() { - fn check(flags: YearFlags, ordinal: u32) { - let of = Of::new(ordinal, flags); - - for ordinal in range_inclusive(0u32, 1024) { - let of = of.with_ordinal(ordinal); - assert_eq!(of.valid(), Of::new(ordinal, flags).valid()); - if of.valid() { - assert_eq!(of.ordinal(), ordinal); - } - } - } - - for &flags in NONLEAP_FLAGS.iter() { - check(flags, 1); - check(flags, 365); - } - for &flags in LEAP_FLAGS.iter() { - check(flags, 1); - check(flags, 366); - } - } - - #[test] - fn test_of_weekday() { - assert_eq!(Of::new(1, A).weekday(), Weekday::Sun); - assert_eq!(Of::new(1, B).weekday(), Weekday::Sat); - assert_eq!(Of::new(1, C).weekday(), Weekday::Fri); - assert_eq!(Of::new(1, D).weekday(), Weekday::Thu); - assert_eq!(Of::new(1, E).weekday(), Weekday::Wed); - assert_eq!(Of::new(1, F).weekday(), Weekday::Tue); - assert_eq!(Of::new(1, G).weekday(), Weekday::Mon); - assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun); - assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat); - assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri); - assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu); - assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed); - assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue); - assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon); - - for &flags in FLAGS.iter() { - let mut prev = Of::new(1, flags).weekday(); - for ordinal in range_inclusive(2u32, flags.ndays()) { - let of = Of::new(ordinal, flags); - let expected = prev.succ(); - assert_eq!(of.weekday(), expected); - prev = expected; - } - } - } - - #[test] - fn test_mdf_fields() { - for &flags in FLAGS.iter() { - for month in range_inclusive(1u32, 12) { - for day in range_inclusive(1u32, 31) { - let mdf = Mdf::new(month, day, flags); - if mdf.valid() { - assert_eq!(mdf.month(), month); - assert_eq!(mdf.day(), day); - } - } - } - } - } - - #[test] - fn test_mdf_with_fields() { - fn check(flags: YearFlags, month: u32, day: u32) { - let mdf = Mdf::new(month, day, flags); - - for month in range_inclusive(0u32, 16) { - let mdf = mdf.with_month(month); - assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); - if mdf.valid() { - assert_eq!(mdf.month(), month); - assert_eq!(mdf.day(), day); - } - } - - for day in range_inclusive(0u32, 1024) { - let mdf = mdf.with_day(day); - assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); - if mdf.valid() { - assert_eq!(mdf.month(), month); - assert_eq!(mdf.day(), day); - } - } - } - - for &flags in NONLEAP_FLAGS.iter() { - check(flags, 1, 1); - check(flags, 1, 31); - check(flags, 2, 1); - check(flags, 2, 28); - check(flags, 2, 29); - check(flags, 12, 31); - } - for &flags in LEAP_FLAGS.iter() { - check(flags, 1, 1); - check(flags, 1, 31); - check(flags, 2, 1); - check(flags, 2, 29); - check(flags, 2, 30); - check(flags, 12, 31); - } - } - - #[test] - fn test_of_isoweekdate_raw() { - for &flags in FLAGS.iter() { - // January 4 should be in the first week - let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw(); - assert_eq!(week, 1); - } - } - - #[test] - fn test_of_to_mdf() { - for i in range_inclusive(0u32, 8192) { - let of = Of(i); - assert_eq!(of.valid(), of.to_mdf().valid()); - } - } - - #[test] - fn test_mdf_to_of() { - for i in range_inclusive(0u32, 8192) { - let mdf = Mdf(i); - assert_eq!(mdf.valid(), mdf.to_of().valid()); - } - } - - #[test] - fn test_of_to_mdf_to_of() { - for i in range_inclusive(0u32, 8192) { - let of = Of(i); - if of.valid() { - assert_eq!(of, of.to_mdf().to_of()); - } - } - } - - #[test] - fn test_mdf_to_of_to_mdf() { - for i in range_inclusive(0u32, 8192) { - let mdf = Mdf(i); - if mdf.valid() { - assert_eq!(mdf, mdf.to_of().to_mdf()); - } - } - } - } -} - diff --git a/src/naive/internals.rs b/src/naive/internals.rs new file mode 100644 index 0000000..2b83aba --- /dev/null +++ b/src/naive/internals.rs @@ -0,0 +1,778 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! The internal implementation of the calendar and ordinal date. +//! +//! The current implementation is optimized for determining year, month, day and day of week. +//! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar, +//! which are included in every packed `NaiveDate` instance. +//! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is +//! based on the moderately-sized lookup table (~1.5KB) +//! and the packed representation is chosen for the efficient lookup. +//! Every internal data structure does not validate its input, +//! but the conversion keeps the valid value valid and the invalid value invalid +//! so that the user-facing `NaiveDate` can validate the input as late as possible. + +#![allow(dead_code)] // some internal methods have been left for consistency + +use std::{i32, fmt}; +use num::traits::FromPrimitive; +use Weekday; +use div::{div_rem, mod_floor}; + +/// The internal date representation. This also includes the packed `Mdf` value. +pub type DateImpl = i32; + +pub const MAX_YEAR: DateImpl = i32::MAX >> 13; +pub const MIN_YEAR: DateImpl = i32::MIN >> 13; + +/// The year flags (aka the dominical letter). +/// +/// There are 14 possible classes of year in the Gregorian calendar: +/// common and leap years starting with Monday through Sunday. +/// The `YearFlags` stores this information into 4 bits `abbb`, +/// where `a` is `1` for the common year (simplifies the `Of` validation) +/// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year +/// (simplifies the day of week calculation from the 1-based ordinal). +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct YearFlags(pub u8); + +pub const A: YearFlags = YearFlags(0o15); pub const AG: YearFlags = YearFlags(0o05); +pub const B: YearFlags = YearFlags(0o14); pub const BA: YearFlags = YearFlags(0o04); +pub const C: YearFlags = YearFlags(0o13); pub const CB: YearFlags = YearFlags(0o03); +pub const D: YearFlags = YearFlags(0o12); pub const DC: YearFlags = YearFlags(0o02); +pub const E: YearFlags = YearFlags(0o11); pub const ED: YearFlags = YearFlags(0o01); +pub const F: YearFlags = YearFlags(0o17); pub const FE: YearFlags = YearFlags(0o07); +pub const G: YearFlags = YearFlags(0o16); pub const GF: YearFlags = YearFlags(0o06); + +static YEAR_TO_FLAGS: [YearFlags; 400] = [ + BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, + ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, + AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, + DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, + GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100 + C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, + GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, + CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, + FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, + BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200 + E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, + BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, + ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, + AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, + DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300 + G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, + DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, + GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, + CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, + FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400 +]; + +static YEAR_DELTAS: [u8; 401] = [ + 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, + 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100 + 25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, + 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, + 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, + 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, + 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, // 200 + 49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, + 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, + 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, + 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, + 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, // 300 + 73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, + 77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, + 82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, + 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, + 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97 // 400+1 +]; + +pub fn cycle_to_yo(cycle: u32) -> (u32, u32) { + let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365); + let delta = YEAR_DELTAS[year_mod_400 as usize] as u32; + if ordinal0 < delta { + year_mod_400 -= 1; + ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32; + } else { + ordinal0 -= delta; + } + (year_mod_400, ordinal0 + 1) +} + +pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { + year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1 +} + +impl YearFlags { + #[inline] + pub fn from_year(year: i32) -> YearFlags { + let year = mod_floor(year, 400); + YearFlags::from_year_mod_400(year) + } + + #[inline] + pub fn from_year_mod_400(year: i32) -> YearFlags { + YEAR_TO_FLAGS[year as usize] + } + + #[inline] + pub fn ndays(&self) -> u32 { + let YearFlags(flags) = *self; + 366 - (flags >> 3) as u32 + } + + #[inline] + pub fn isoweek_delta(&self) -> u32 { + let YearFlags(flags) = *self; + let mut delta = flags as u32 & 0b111; + if delta < 3 { delta += 7; } + delta + } + + #[inline] + pub fn nisoweeks(&self) -> u32 { + let YearFlags(flags) = *self; + 52 + ((0b00000100_00000110 >> flags as usize) & 1) + } +} + +impl fmt::Debug for YearFlags { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let YearFlags(flags) = *self; + match flags { + 0o15 => "A".fmt(f), 0o05 => "AG".fmt(f), + 0o14 => "B".fmt(f), 0o04 => "BA".fmt(f), + 0o13 => "C".fmt(f), 0o03 => "CB".fmt(f), + 0o12 => "D".fmt(f), 0o02 => "DC".fmt(f), + 0o11 => "E".fmt(f), 0o01 => "ED".fmt(f), + 0o10 => "F?".fmt(f), 0o00 => "FE?".fmt(f), // non-canonical + 0o17 => "F".fmt(f), 0o07 => "FE".fmt(f), + 0o16 => "G".fmt(f), 0o06 => "GF".fmt(f), + _ => write!(f, "YearFlags({})", flags), + } + } +} + +pub const MIN_OL: u32 = 1 << 1; +pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` +pub const MIN_MDL: u32 = (1 << 6) | (1 << 1); +pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; + +const XX: i8 = -128; +static MDL_TO_OL: [i8; (MAX_MDL as usize + 1)] = [ + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, + XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0 + XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 + XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2 + XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, + 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, + 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, + 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3 + XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, + 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, + 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, + 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4 + XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, + 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, + 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, + 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5 + XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, + 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, + 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, + 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6 + XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, + 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, + 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, + 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7 + XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, + 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, + 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, + 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8 + XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, + 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, + 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, + 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9 + XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, + 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, + 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, + 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10 + XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, + 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, + 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, + 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11 + XX, XX, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, + 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, + 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, + 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, // 12 +]; + +static OL_TO_MDL: [u8; (MAX_OL as usize + 1)] = [ + 0, 0, // 0 + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, // 2 + 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, + 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, + 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, + 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3 + 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, + 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, + 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, + 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4 + 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, + 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, + 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, + 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5 + 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, + 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, + 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, + 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6 + 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, + 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, + 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, + 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7 + 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, + 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, + 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, + 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8 + 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, + 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, + 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, + 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9 + 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, + 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, + 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, + 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10 + 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, + 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, + 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, + 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11 + 100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, + 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, + 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, + 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98, // 12 +]; + +/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`. +/// +/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag), +/// which is an index to the `OL_TO_MDL` lookup table. +#[derive(PartialEq, PartialOrd, Copy, Clone)] +pub struct Of(pub u32); + +impl Of { + #[inline] + fn clamp_ordinal(ordinal: u32) -> u32 { + if ordinal > 366 {0} else {ordinal} + } + + #[inline] + pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of { + let ordinal = Of::clamp_ordinal(ordinal); + Of((ordinal << 4) | (flags as u32)) + } + + #[inline] + pub fn from_mdf(Mdf(mdf): Mdf) -> Of { + let mdl = mdf >> 3; + match MDL_TO_OL.get(mdl as usize) { + Some(&v) => Of(mdf.wrapping_sub((v as i32 as u32 & 0x3ff) << 3)), + None => Of(0) + } + } + + #[inline] + pub fn valid(&self) -> bool { + let Of(of) = *self; + let ol = of >> 3; + MIN_OL <= ol && ol <= MAX_OL + } + + #[inline] + pub fn ordinal(&self) -> u32 { + let Of(of) = *self; + of >> 4 + } + + #[inline] + pub fn with_ordinal(&self, ordinal: u32) -> Of { + let ordinal = Of::clamp_ordinal(ordinal); + let Of(of) = *self; + Of((of & 0b1111) | (ordinal << 4)) + } + + #[inline] + pub fn flags(&self) -> YearFlags { + let Of(of) = *self; + YearFlags((of & 0b1111) as u8) + } + + #[inline] + pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of { + let Of(of) = *self; + Of((of & !0b1111) | (flags as u32)) + } + + #[inline] + pub fn weekday(&self) -> Weekday { + let Of(of) = *self; + Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap() + } + + #[inline] + pub fn isoweekdate_raw(&self) -> (u32, Weekday) { + // week ordinal = ordinal + delta + let Of(of) = *self; + let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta()); + (weekord / 7, Weekday::from_u32(weekord % 7).unwrap()) + } + + #[inline] + pub fn to_mdf(&self) -> Mdf { + Mdf::from_of(*self) + } + + #[inline] + pub fn succ(&self) -> Of { + let Of(of) = *self; + Of(of + (1 << 4)) + } + + #[inline] + pub fn pred(&self) -> Of { + let Of(of) = *self; + Of(of - (1 << 4)) + } +} + +impl fmt::Debug for Of { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Of(of) = *self; + write!(f, "Of(({} << 4) | {:#04o} /*{:?}*/)", + of >> 4, of & 0b1111, YearFlags((of & 0b1111) as u8)) + } +} + +/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags` +/// +/// The whole bits except for the least 3 bits are referred as `Mdl` +/// (month, day of month and leap flag), +/// which is an index to the `MDL_TO_OL` lookup table. +#[derive(PartialEq, PartialOrd, Copy, Clone)] +pub struct Mdf(pub u32); + +impl Mdf { + #[inline] + fn clamp_month(month: u32) -> u32 { + if month > 12 {0} else {month} + } + + #[inline] + fn clamp_day(day: u32) -> u32 { + if day > 31 {0} else {day} + } + + #[inline] + pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf { + let month = Mdf::clamp_month(month); + let day = Mdf::clamp_day(day); + Mdf((month << 9) | (day << 4) | (flags as u32)) + } + + #[inline] + pub fn from_of(Of(of): Of) -> Mdf { + let ol = of >> 3; + match OL_TO_MDL.get(ol as usize) { + Some(&v) => Mdf(of + ((v as u32) << 3)), + None => Mdf(0) + } + } + + #[inline] + pub fn valid(&self) -> bool { + let Mdf(mdf) = *self; + let mdl = mdf >> 3; + match MDL_TO_OL.get(mdl as usize) { + Some(&v) => v >= 0, + None => false + } + } + + #[inline] + pub fn month(&self) -> u32 { + let Mdf(mdf) = *self; + mdf >> 9 + } + + #[inline] + pub fn with_month(&self, month: u32) -> Mdf { + let month = Mdf::clamp_month(month); + let Mdf(mdf) = *self; + Mdf((mdf & 0b11111_1111) | (month << 9)) + } + + #[inline] + pub fn day(&self) -> u32 { + let Mdf(mdf) = *self; + (mdf >> 4) & 0b11111 + } + + #[inline] + pub fn with_day(&self, day: u32) -> Mdf { + let day = Mdf::clamp_day(day); + let Mdf(mdf) = *self; + Mdf((mdf & !0b11111_0000) | (day << 4)) + } + + #[inline] + pub fn flags(&self) -> YearFlags { + let Mdf(mdf) = *self; + YearFlags((mdf & 0b1111) as u8) + } + + #[inline] + pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { + let Mdf(mdf) = *self; + Mdf((mdf & !0b1111) | (flags as u32)) + } + + #[inline] + pub fn to_of(&self) -> Of { + Of::from_mdf(*self) + } +} + +impl fmt::Debug for Mdf { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Mdf(mdf) = *self; + write!(f, "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)", + mdf >> 9, (mdf >> 4) & 0b11111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8)) + } +} + +#[cfg(test)] +mod tests { + #[cfg(bench)] extern crate test; + + use Weekday; + use super::{Of, Mdf}; + use super::{YearFlags, A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF}; + use num::iter::range_inclusive; + use std::u32; + + const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G]; + const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF]; + const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF]; + + #[test] + fn test_year_flags_ndays_from_year() { + assert_eq!(YearFlags::from_year(2014).ndays(), 365); + assert_eq!(YearFlags::from_year(2012).ndays(), 366); + assert_eq!(YearFlags::from_year(2000).ndays(), 366); + assert_eq!(YearFlags::from_year(1900).ndays(), 365); + assert_eq!(YearFlags::from_year(1600).ndays(), 366); + assert_eq!(YearFlags::from_year( 1).ndays(), 365); + assert_eq!(YearFlags::from_year( 0).ndays(), 366); // 1 BCE (proleptic Gregorian) + assert_eq!(YearFlags::from_year( -1).ndays(), 365); // 2 BCE + assert_eq!(YearFlags::from_year( -4).ndays(), 366); // 5 BCE + assert_eq!(YearFlags::from_year( -99).ndays(), 365); // 100 BCE + assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE + assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE + assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE + } + + #[test] + fn test_year_flags_nisoweeks() { + assert_eq!(A.nisoweeks(), 52); + assert_eq!(B.nisoweeks(), 52); + assert_eq!(C.nisoweeks(), 52); + assert_eq!(D.nisoweeks(), 53); + assert_eq!(E.nisoweeks(), 52); + assert_eq!(F.nisoweeks(), 52); + assert_eq!(G.nisoweeks(), 52); + assert_eq!(AG.nisoweeks(), 52); + assert_eq!(BA.nisoweeks(), 52); + assert_eq!(CB.nisoweeks(), 52); + assert_eq!(DC.nisoweeks(), 53); + assert_eq!(ED.nisoweeks(), 53); + assert_eq!(FE.nisoweeks(), 52); + assert_eq!(GF.nisoweeks(), 52); + } + + #[cfg(bench)] + #[bench] + fn bench_year_flags_from_year(bh: &mut test::Bencher) { + bh.iter(|| { + for year in -999i32..1000 { + YearFlags::from_year(year); + } + }); + } + + #[test] + fn test_of() { + fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) { + for ordinal in range_inclusive(ordinal1, ordinal2) { + let of = Of::new(ordinal, flags); + assert!(of.valid() == expected, + "ordinal {} = {:?} should be {} for dominical year {:?}", + ordinal, of, if expected {"valid"} else {"invalid"}, flags); + } + } + + for &flags in NONLEAP_FLAGS.iter() { + check(false, flags, 0, 0); + check(true, flags, 1, 365); + check(false, flags, 366, 1024); + check(false, flags, u32::MAX, u32::MAX); + } + + for &flags in LEAP_FLAGS.iter() { + check(false, flags, 0, 0); + check(true, flags, 1, 366); + check(false, flags, 367, 1024); + check(false, flags, u32::MAX, u32::MAX); + } + } + + #[test] + fn test_mdf_valid() { + fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, + month2: u32, day2: u32) { + for month in range_inclusive(month1, month2) { + for day in range_inclusive(day1, day2) { + let mdf = Mdf::new(month, day, flags); + assert!(mdf.valid() == expected, + "month {} day {} = {:?} should be {} for dominical year {:?}", + month, day, mdf, if expected {"valid"} else {"invalid"}, flags); + } + } + } + + for &flags in NONLEAP_FLAGS.iter() { + check(false, flags, 0, 0, 0, 1024); + check(false, flags, 0, 0, 16, 0); + check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024); + check(true, flags, 2, 1, 2, 28); check(false, flags, 2, 29, 2, 1024); + check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024); + check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024); + check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024); + check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024); + check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024); + check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024); + check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024); + check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024); + check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024); + check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024); + check(false, flags, 13, 0, 16, 1024); + check(false, flags, u32::MAX, 0, u32::MAX, 1024); + check(false, flags, 0, u32::MAX, 16, u32::MAX); + check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); + } + + for &flags in LEAP_FLAGS.iter() { + check(false, flags, 0, 0, 0, 1024); + check(false, flags, 0, 0, 16, 0); + check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024); + check(true, flags, 2, 1, 2, 29); check(false, flags, 2, 30, 2, 1024); + check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024); + check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024); + check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024); + check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024); + check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024); + check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024); + check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024); + check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024); + check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024); + check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024); + check(false, flags, 13, 0, 16, 1024); + check(false, flags, u32::MAX, 0, u32::MAX, 1024); + check(false, flags, 0, u32::MAX, 16, u32::MAX); + check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); + } + } + + #[test] + fn test_of_fields() { + for &flags in FLAGS.iter() { + for ordinal in range_inclusive(1u32, 366) { + let of = Of::new(ordinal, flags); + if of.valid() { + assert_eq!(of.ordinal(), ordinal); + } + } + } + } + + #[test] + fn test_of_with_fields() { + fn check(flags: YearFlags, ordinal: u32) { + let of = Of::new(ordinal, flags); + + for ordinal in range_inclusive(0u32, 1024) { + let of = of.with_ordinal(ordinal); + assert_eq!(of.valid(), Of::new(ordinal, flags).valid()); + if of.valid() { + assert_eq!(of.ordinal(), ordinal); + } + } + } + + for &flags in NONLEAP_FLAGS.iter() { + check(flags, 1); + check(flags, 365); + } + for &flags in LEAP_FLAGS.iter() { + check(flags, 1); + check(flags, 366); + } + } + + #[test] + fn test_of_weekday() { + assert_eq!(Of::new(1, A).weekday(), Weekday::Sun); + assert_eq!(Of::new(1, B).weekday(), Weekday::Sat); + assert_eq!(Of::new(1, C).weekday(), Weekday::Fri); + assert_eq!(Of::new(1, D).weekday(), Weekday::Thu); + assert_eq!(Of::new(1, E).weekday(), Weekday::Wed); + assert_eq!(Of::new(1, F).weekday(), Weekday::Tue); + assert_eq!(Of::new(1, G).weekday(), Weekday::Mon); + assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun); + assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat); + assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri); + assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu); + assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed); + assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue); + assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon); + + for &flags in FLAGS.iter() { + let mut prev = Of::new(1, flags).weekday(); + for ordinal in range_inclusive(2u32, flags.ndays()) { + let of = Of::new(ordinal, flags); + let expected = prev.succ(); + assert_eq!(of.weekday(), expected); + prev = expected; + } + } + } + + #[test] + fn test_mdf_fields() { + for &flags in FLAGS.iter() { + for month in range_inclusive(1u32, 12) { + for day in range_inclusive(1u32, 31) { + let mdf = Mdf::new(month, day, flags); + if mdf.valid() { + assert_eq!(mdf.month(), month); + assert_eq!(mdf.day(), day); + } + } + } + } + } + + #[test] + fn test_mdf_with_fields() { + fn check(flags: YearFlags, month: u32, day: u32) { + let mdf = Mdf::new(month, day, flags); + + for month in range_inclusive(0u32, 16) { + let mdf = mdf.with_month(month); + assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); + if mdf.valid() { + assert_eq!(mdf.month(), month); + assert_eq!(mdf.day(), day); + } + } + + for day in range_inclusive(0u32, 1024) { + let mdf = mdf.with_day(day); + assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); + if mdf.valid() { + assert_eq!(mdf.month(), month); + assert_eq!(mdf.day(), day); + } + } + } + + for &flags in NONLEAP_FLAGS.iter() { + check(flags, 1, 1); + check(flags, 1, 31); + check(flags, 2, 1); + check(flags, 2, 28); + check(flags, 2, 29); + check(flags, 12, 31); + } + for &flags in LEAP_FLAGS.iter() { + check(flags, 1, 1); + check(flags, 1, 31); + check(flags, 2, 1); + check(flags, 2, 29); + check(flags, 2, 30); + check(flags, 12, 31); + } + } + + #[test] + fn test_of_isoweekdate_raw() { + for &flags in FLAGS.iter() { + // January 4 should be in the first week + let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw(); + assert_eq!(week, 1); + } + } + + #[test] + fn test_of_to_mdf() { + for i in range_inclusive(0u32, 8192) { + let of = Of(i); + assert_eq!(of.valid(), of.to_mdf().valid()); + } + } + + #[test] + fn test_mdf_to_of() { + for i in range_inclusive(0u32, 8192) { + let mdf = Mdf(i); + assert_eq!(mdf.valid(), mdf.to_of().valid()); + } + } + + #[test] + fn test_of_to_mdf_to_of() { + for i in range_inclusive(0u32, 8192) { + let of = Of(i); + if of.valid() { + assert_eq!(of, of.to_mdf().to_of()); + } + } + } + + #[test] + fn test_mdf_to_of_to_mdf() { + for i in range_inclusive(0u32, 8192) { + let mdf = Mdf(i); + if mdf.valid() { + assert_eq!(mdf, mdf.to_of().to_mdf()); + } + } + } +} + From 42a7c8e5899b1e0087294779df27c8ecd095fa8c Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 21 Jun 2017 21:58:42 +0900 Subject: [PATCH 49/53] Fixed more documentation links. Linkchecker recognizes the distinction between internal and external links (which are not checked by default), and considers URLs which does not have the starting URL base as a prefix internal... This commit has been verified against a proper set of options to Linkchecker, but there are several false positives (for our purposes) which would make the automated checking not as effective. --- src/date.rs | 2 +- src/datetime.rs | 6 +++--- src/format/mod.rs | 2 +- src/format/parsed.rs | 8 ++++---- src/naive/date.rs | 16 ++++++++-------- src/naive/datetime.rs | 16 ++++++++-------- src/naive/time.rs | 10 +++++----- src/offset/fixed.rs | 2 +- src/offset/local.rs | 2 +- src/offset/utc.rs | 2 +- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/date.rs b/src/date.rs index 05a1c32..f3cad10 100644 --- a/src/date.rs +++ b/src/date.rs @@ -261,7 +261,7 @@ impl Date where Tz::Offset: fmt::Display { } /// Formats the date with the specified format string. - /// See the [`format::strftime` module](../format/strftime/index.html) + /// See the [`format::strftime` module](./format/strftime/index.html) /// on the supported escape sequences. #[inline] pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { diff --git a/src/datetime.rs b/src/datetime.rs index ecf6e73..e478ea5 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -21,7 +21,7 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// /// There are some constructors implemented here (the `from_*` methods), but /// the general-purpose constructors are all via the methods on the -/// [`TimeZone`](../trait.TimeZone.html) implementations. +/// [`TimeZone`](./offset/trait.TimeZone.html) implementations. #[derive(Clone)] pub struct DateTime { datetime: NaiveDateTime, @@ -208,7 +208,7 @@ impl DateTime { /// Parses a string with the specified format string and /// returns a new `DateTime` with a parsed `FixedOffset`. - /// See the [`format::strftime` module](../format/strftime/index.html) + /// See the [`format::strftime` module](./format/strftime/index.html) /// on the supported escape sequences. /// /// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone. @@ -241,7 +241,7 @@ impl DateTime where Tz::Offset: fmt::Display { } /// Formats the combined date and time with the specified format string. - /// See the [`format::strftime` module](../format/strftime/index.html) + /// See the [`format::strftime` module](./format/strftime/index.html) /// on the supported escape sequences. #[inline] pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { diff --git a/src/format/mod.rs b/src/format/mod.rs index 503ef16..fc05fcc 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -558,7 +558,7 @@ impl<'a, I: Iterator> + Clone> fmt::Display for DelayedFormat { // this implementation is here only because we need some private code from `scan` -/// Parsing a `str` into a `Weekday` uses the format [`%W`](../../format/strftime/index.html). +/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html). /// /// # Example /// diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 1ab0099..00686d8 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -41,13 +41,13 @@ pub struct Parsed { /// Year modulo 100. Implies that the year is >= 1 BCE when set. pub year_mod_100: Option, - /// Year in the [ISO week date](../../naive/date/index.html#week-date). + /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date). /// /// This can be negative unlike [`isoyear_div_100`](#structfield.isoyear_div_100) and /// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields. pub isoyear: Option, - /// Year in the [ISO week date](../../naive/date/index.html#week-date), divided by 100. + /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), divided by 100. /// Implies that the year is >= 1 BCE when set. /// /// Due to the common usage, if this field is missing but @@ -55,7 +55,7 @@ pub struct Parsed { /// it is inferred to 19 when `isoyear_mod_100 >= 70` and 20 otherwise. pub isoyear_div_100: Option, - /// Year in the [ISO week date](../../naive/date/index.html#week-date), modulo 100. + /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), modulo 100. /// Implies that the year is >= 1 BCE when set. pub isoyear_mod_100: Option, @@ -70,7 +70,7 @@ pub struct Parsed { /// (0--53, 1--53 or 1--52 depending on the year). pub week_from_mon: Option, - /// [ISO week number](../../naive/date/index.html#week-date) + /// [ISO week number](../naive/struct.NaiveDate.html#week-date) /// (1--52 or 1--53 depending on the year). pub isoweek: Option, diff --git a/src/naive/date.rs b/src/naive/date.rs index 277b6c2..8acea8e 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -64,12 +64,12 @@ const MAX_BITS: usize = 44; /// /// * ISO 8601 calendars has the year 0, which is 1 BCE (a year before 1 CE). /// If you need a typical BCE/BC and CE/AD notation for year numbers, -/// use the [`Datelike::year_ce`](../../trait.Datelike.html#method.year_ce) method. +/// use the [`Datelike::year_ce`](../trait.Datelike.html#method.year_ce) method. /// /// # Week Date /// /// The ISO 8601 **week date** is a triple of year number, week number -/// and [day of the week](../../enum.Weekday.html) with the following rules: +/// and [day of the week](../enum.Weekday.html) with the following rules: /// /// * A week consists of Monday through Sunday, and is always numbered within some year. /// The week number ranges from 1 to 52 or 53 depending on the year. @@ -81,7 +81,7 @@ const MAX_BITS: usize = 44; /// For example, January 3, 2016 (Sunday) was on the last (53rd) week of 2015. /// /// Chrono's date types default to the ISO 8601 [calendar date](#calendar-date), -/// but the [`Datelike::isoweekdate`](../../trait.Datelike.html#tymethod.isoweekdate) method +/// but the [`Datelike::isoweekdate`](../trait.Datelike.html#tymethod.isoweekdate) method /// can be used to get the corresponding week date. /// /// # Ordinal Date @@ -398,7 +398,7 @@ impl NaiveDate { } /// Parses a string with the specified format string and returns a new `NaiveDate`. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// # Example @@ -913,7 +913,7 @@ impl NaiveDate { } /// Formats the date with the specified format string. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// This returns a `DelayedFormat`, @@ -1382,7 +1382,7 @@ impl Sub for NaiveDate { } /// The `Debug` output of the naive date `d` is same to -/// [`d.format("%Y-%m-%d")`](../../format/strftime/index.html). +/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html). /// /// The string printed can be readily parsed via the `parse` method on `str`. /// @@ -1417,7 +1417,7 @@ impl fmt::Debug for NaiveDate { } /// The `Display` output of the naive date `d` is same to -/// [`d.format("%Y-%m-%d")`](../../format/strftime/index.html). +/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html). /// /// The string printed can be readily parsed via the `parse` method on `str`. /// @@ -1443,7 +1443,7 @@ impl fmt::Display for NaiveDate { } /// Parsing a `str` into a `NaiveDate` uses the same format, -/// [`%Y-%m-%d`](../../format/strftime/index.html), as in `Debug` and `Display`. +/// [`%Y-%m-%d`](../format/strftime/index.html), as in `Debug` and `Display`. /// /// # Example /// diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index fbdd0eb..bd13edd 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -35,8 +35,8 @@ const MAX_SECS_BITS: usize = 44; /// # let _ = dt; /// ~~~~ /// -/// You can use typical [date-like](../../trait.Datelike.html) and -/// [time-like](../../trait.Timelike.html) methods, +/// You can use typical [date-like](../trait.Datelike.html) and +/// [time-like](../trait.Timelike.html) methods, /// provided that relevant traits are in the scope. /// /// ~~~~ @@ -99,7 +99,7 @@ impl NaiveDateTime { /// and the number of nanoseconds since the last whole non-leap second. /// /// For a non-naive version of this function see - /// [`TimeZone::timestamp`](../../offset/trait.TimeZone.html#method.timestamp). + /// [`TimeZone::timestamp`](../offset/trait.TimeZone.html#method.timestamp). /// /// The nanosecond part can exceed 1,000,000,000 in order to represent the /// [leap second](./struct.NaiveTime.html#leap-second-handling). (The true "UNIX @@ -162,7 +162,7 @@ impl NaiveDateTime { } /// Parses a string with the specified format string and returns a new `NaiveDateTime`. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// # Example @@ -594,7 +594,7 @@ impl NaiveDateTime { } /// Formats the combined date and time with the specified format string. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// This returns a `DelayedFormat`, @@ -1256,7 +1256,7 @@ impl Sub for NaiveDateTime { } /// The `Debug` output of the naive date and time `dt` is same to -/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](../../format/strftime/index.html). +/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](../format/strftime/index.html). /// /// The string printed can be readily parsed via the `parse` method on `str`. /// @@ -1289,7 +1289,7 @@ impl fmt::Debug for NaiveDateTime { } /// The `Debug` output of the naive date and time `dt` is same to -/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](../../format/strftime/index.html). +/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](../format/strftime/index.html). /// /// It should be noted that, for leap seconds not on the minute boundary, /// it may print a representation not distinguishable from non-leap seconds. @@ -1320,7 +1320,7 @@ impl fmt::Display for NaiveDateTime { } /// Parsing a `str` into a `NaiveDateTime` uses the same format, -/// [`%Y-%m-%dT%H:%M:%S%.f`](../../format/strftime/index.html), as in `Debug`. +/// [`%Y-%m-%dT%H:%M:%S%.f`](../format/strftime/index.html), as in `Debug`. /// /// # Example /// diff --git a/src/naive/time.rs b/src/naive/time.rs index 6c4632e..f51b2dc 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -426,7 +426,7 @@ impl NaiveTime { } /// Parses a string with the specified format string and returns a new `NaiveTime`. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// # Example @@ -725,7 +725,7 @@ impl NaiveTime { } /// Formats the time with the specified format string. - /// See the [`format::strftime` module](../../format/strftime/index.html) + /// See the [`format::strftime` module](../format/strftime/index.html) /// on the supported escape sequences. /// /// This returns a `DelayedFormat`, @@ -1114,7 +1114,7 @@ impl Sub for NaiveTime { } /// The `Debug` output of the naive time `t` is same to -/// [`t.format("%H:%M:%S%.f")`](../../format/strftime/index.html). +/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html). /// /// The string printed can be readily parsed via the `parse` method on `str`. /// @@ -1164,7 +1164,7 @@ impl fmt::Debug for NaiveTime { } /// The `Display` output of the naive time `t` is same to -/// [`t.format("%H:%M:%S%.f")`](../../format/strftime/index.html). +/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html). /// /// The string printed can be readily parsed via the `parse` method on `str`. /// @@ -1196,7 +1196,7 @@ impl fmt::Display for NaiveTime { } /// Parsing a `str` into a `NaiveTime` uses the same format, -/// [`%H:%M:%S%.f`](../../format/strftime/index.html), as in `Debug` and `Display`. +/// [`%H:%M:%S%.f`](../format/strftime/index.html), as in `Debug` and `Display`. /// /// # Example /// diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 168880c..ca3a23c 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -15,7 +15,7 @@ use super::{TimeZone, Offset, LocalResult}; /// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. /// -/// Using the [`TimeZone`](../../../chrono/offset/trait.TimeZone.html) methods +/// Using the [`TimeZone`](./trait.TimeZone.html) methods /// on a `FixedOffset` struct is the preferred way to construct /// `DateTime` instances. See the [`east`](#method.east) and /// [`west`](#method.west) methods for examples. diff --git a/src/offset/local.rs b/src/offset/local.rs index 77f1413..1f65f1a 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -65,7 +65,7 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec { /// The local timescale. This is implemented via the standard `time` crate. /// -/// Using the [`TimeZone`](../../../chrono/offset/trait.TimeZone.html) methods +/// Using the [`TimeZone`](./trait.TimeZone.html) methods /// on the Local struct is the preferred way to construct `DateTime` /// instances. /// diff --git a/src/offset/utc.rs b/src/offset/utc.rs index bb8a029..684be75 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -13,7 +13,7 @@ use super::{TimeZone, Offset, LocalResult, FixedOffset}; /// The UTC time zone. This is the most efficient time zone when you don't need the local time. /// It is also used as an offset (which is also a dummy type). /// -/// Using the [`TimeZone`](../../../chrono/offset/trait.TimeZone.html) methods +/// Using the [`TimeZone`](./trait.TimeZone.html) methods /// on the UTC struct is the preferred way to construct `DateTime` /// instances. /// From 9768b289f0f78dbb7d267986a350e9d6941bb58b Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 22 Jun 2017 00:21:24 +0900 Subject: [PATCH 50/53] New type: `chrono::naive::IsoWeek`. This removes `Datelike::isoweekdate` in favor of `Datelike::iso_week`. The original `isoweekdate` was not named in accordance with the style guide and also used to return the day of the week which is already provided by `Datelike::weekday`. The new design should be more reasonable. Note that we initially do not implement any public constructor for `IsoWeek`. That is, the only legitimate way to get a new `IsoWeek` is from `Datelike::iso_week`. This sidesteps the issue of boundary values (for example the year number in the maximal date will overflow in the week date) while giving the same power as the original API. Partially accounts for #139. We may add additional week types as necessary---this is the beginning. --- src/date.rs | 4 +- src/datetime.rs | 4 +- src/format/mod.rs | 8 +-- src/format/parsed.rs | 5 +- src/lib.rs | 37 +++++----- src/naive/date.rs | 53 +++++++------- src/naive/datetime.rs | 6 +- src/naive/isoweek.rs | 161 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 219 insertions(+), 59 deletions(-) create mode 100644 src/naive/isoweek.rs diff --git a/src/date.rs b/src/date.rs index f3cad10..e02380a 100644 --- a/src/date.rs +++ b/src/date.rs @@ -10,7 +10,7 @@ use oldtime::Duration as OldDuration; use {Weekday, Datelike}; use offset::{TimeZone, UTC}; -use naive::{self, NaiveDate, NaiveTime}; +use naive::{self, NaiveDate, NaiveTime, IsoWeek}; use DateTime; use format::{Item, DelayedFormat, StrftimeItems}; @@ -278,7 +278,7 @@ impl Datelike for Date { #[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() } #[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() } #[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() } - #[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() } + #[inline] fn iso_week(&self) -> IsoWeek { self.naive_local().iso_week() } #[inline] fn with_year(&self, year: i32) -> Option> { diff --git a/src/datetime.rs b/src/datetime.rs index e478ea5..319c7b8 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -12,7 +12,7 @@ use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; use offset::{TimeZone, Offset, UTC, Local, FixedOffset}; -use naive::{NaiveTime, NaiveDateTime}; +use naive::{NaiveTime, NaiveDateTime, IsoWeek}; use Date; use format::{Item, Numeric, Pad, Fixed}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; @@ -258,7 +258,7 @@ impl Datelike for DateTime { #[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() } #[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() } #[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() } - #[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() } + #[inline] fn iso_week(&self) -> IsoWeek { self.naive_local().iso_week() } #[inline] fn with_year(&self, year: i32) -> Option> { diff --git a/src/format/mod.rs b/src/format/mod.rs index fc05fcc..29ca87d 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -353,14 +353,14 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt Year => (4, date.map(|d| d.year() as i64)), YearDiv100 => (2, date.map(|d| div_floor(d.year() as i64, 100))), YearMod100 => (2, date.map(|d| mod_floor(d.year() as i64, 100))), - IsoYear => (4, date.map(|d| d.isoweekdate().0 as i64)), - IsoYearDiv100 => (2, date.map(|d| div_floor(d.isoweekdate().0 as i64, 100))), - IsoYearMod100 => (2, date.map(|d| mod_floor(d.isoweekdate().0 as i64, 100))), + IsoYear => (4, date.map(|d| d.iso_week().year() as i64)), + IsoYearDiv100 => (2, date.map(|d| div_floor(d.iso_week().year() as i64, 100))), + IsoYearMod100 => (2, date.map(|d| mod_floor(d.iso_week().year() as i64, 100))), Month => (2, date.map(|d| d.month() as i64)), Day => (2, date.map(|d| d.day() as i64)), WeekFromSun => (2, date.map(|d| week_from_sun(d) as i64)), WeekFromMon => (2, date.map(|d| week_from_mon(d) as i64)), - IsoWeek => (2, date.map(|d| d.isoweekdate().1 as i64)), + IsoWeek => (2, date.map(|d| d.iso_week().week() as i64)), NumDaysFromSun => (1, date.map(|d| d.weekday().num_days_from_sunday() as i64)), WeekdayFromMon => (1, date.map(|d| d.weekday().number_from_monday() as i64)), Ordinal => (3, date.map(|d| d.ordinal() as i64)), diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 00686d8..fd1b723 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -332,7 +332,10 @@ impl Parsed { // verify the ISO week date. let verify_isoweekdate = |date: NaiveDate| { - let (isoyear, isoweek, weekday) = date.isoweekdate(); + let week = date.iso_week(); + let isoyear = week.year(); + let isoweek = week.week(); + let weekday = date.weekday(); let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 { let (q, r) = div_rem(isoyear, 100); (Some(q), Some(r)) diff --git a/src/lib.rs b/src/lib.rs index fc60760..a4dd502 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,10 +362,10 @@ extern crate serde as serdelib; pub use oldtime::Duration; #[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, UTC, FixedOffset, Local}; -#[doc(no_inline)] pub use naive::{NaiveDate, NaiveTime, NaiveDateTime}; -pub use date_::{Date, MIN_DATE, MAX_DATE}; -pub use datetime_::DateTime; -#[cfg(feature = "rustc-serialize")] pub use datetime_::TsSeconds; +#[doc(no_inline)] pub use naive::{NaiveDate, IsoWeek, NaiveTime, NaiveDateTime}; +pub use date::{Date, MIN_DATE, MAX_DATE}; +pub use datetime::DateTime; +#[cfg(feature = "rustc-serialize")] pub use datetime::TsSeconds; pub use format::{ParseError, ParseResult}; /// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). @@ -393,24 +393,24 @@ pub mod naive { //! but can be also used for the simpler date and time handling. mod internals; + mod date; + mod isoweek; + mod time; + mod datetime; - // avoid using them directly even in the crate itself - #[path = "date.rs"] mod date_; - #[path = "time.rs"] mod time_; - #[path = "datetime.rs"] mod datetime_; - - pub use self::date_::{NaiveDate, MIN_DATE, MAX_DATE}; - pub use self::time_::NaiveTime; - pub use self::datetime_::{NaiveDateTime, TsSeconds}; + pub use self::date::{NaiveDate, MIN_DATE, MAX_DATE}; + pub use self::isoweek::IsoWeek; + pub use self::time::NaiveTime; + pub use self::datetime::{NaiveDateTime, TsSeconds}; /// Tools to help serializing/deserializing naive types. #[cfg(feature = "serde")] pub mod serde { - pub use super::datetime_::serde::*; + pub use super::datetime::serde::*; } } -#[path = "date.rs"] mod date_; -#[path = "datetime.rs"] mod datetime_; +mod date; +mod datetime; pub mod format; /// Ser/de helpers @@ -419,7 +419,7 @@ pub mod format; /// annotation](https://serde.rs/attributes.html#field-attributes). #[cfg(feature = "serde")] pub mod serde { - pub use super::datetime_::serde::*; + pub use super::datetime::serde::*; } /// The day of week. @@ -761,9 +761,8 @@ pub trait Datelike: Sized { /// Returns the day of week. fn weekday(&self) -> Weekday; - /// Returns the ISO week date: an adjusted year, week number and day of week. - /// The adjusted year may differ from that of the calendar date. - fn isoweekdate(&self) -> (i32, u32, Weekday); + /// Returns the ISO week. + fn iso_week(&self) -> IsoWeek; /// Makes a new value with the year number changed. /// diff --git a/src/naive/date.rs b/src/naive/date.rs index 8acea8e..f333413 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -10,10 +10,11 @@ use oldtime::Duration as OldDuration; use {Weekday, Datelike}; use div::div_mod_floor; -use naive::{NaiveTime, NaiveDateTime}; +use naive::{NaiveTime, NaiveDateTime, IsoWeek}; use format::{Item, Numeric, Pad}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; +use super::isoweek; use super::internals::{self, DateImpl, Of, Mdf, YearFlags}; const MAX_YEAR: i32 = internals::MAX_YEAR; @@ -81,7 +82,8 @@ const MAX_BITS: usize = 44; /// For example, January 3, 2016 (Sunday) was on the last (53rd) week of 2015. /// /// Chrono's date types default to the ISO 8601 [calendar date](#calendar-date), -/// but the [`Datelike::isoweekdate`](../trait.Datelike.html#tymethod.isoweekdate) method +/// but [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) and +/// [`Datelike::weekday`](../trait.Datelike.html#tymethod.weekday) methods /// can be used to get the corresponding week date. /// /// # Ordinal Date @@ -151,7 +153,9 @@ impl NaiveDate { /// assert_eq!(d.month(), 3); /// assert_eq!(d.day(), 14); /// assert_eq!(d.ordinal(), 73); // day of year - /// assert_eq!(d.isoweekdate(), (2015, 11, Weekday::Sat)); // ISO week and weekday + /// assert_eq!(d.iso_week().year(), 2015); + /// assert_eq!(d.iso_week().week(), 11); + /// assert_eq!(d.weekday(), Weekday::Sat); /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE /// ~~~~ pub fn from_ymd(year: i32, month: u32, day: u32) -> NaiveDate { @@ -197,7 +201,9 @@ impl NaiveDate { /// assert_eq!(d.year(), 2015); /// assert_eq!(d.month(), 3); /// assert_eq!(d.day(), 14); - /// assert_eq!(d.isoweekdate(), (2015, 11, Weekday::Sat)); // ISO week and weekday + /// assert_eq!(d.iso_week().year(), 2015); + /// assert_eq!(d.iso_week().week(), 11); + /// assert_eq!(d.weekday(), Weekday::Sat); /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE /// ~~~~ pub fn from_yo(year: i32, ordinal: u32) -> NaiveDate { @@ -241,7 +247,9 @@ impl NaiveDate { /// use chrono::{NaiveDate, Datelike, Weekday}; /// /// let d = NaiveDate::from_isoywd(2015, 11, Weekday::Sat); - /// assert_eq!(d.isoweekdate(), (2015, 11, Weekday::Sat)); + /// assert_eq!(d.iso_week().year(), 2015); + /// assert_eq!(d.iso_week().week(), 11); + /// assert_eq!(d.weekday(), Weekday::Sat); /// assert_eq!(d.year(), 2015); /// assert_eq!(d.month(), 3); /// assert_eq!(d.day(), 14); @@ -338,7 +346,9 @@ impl NaiveDate { /// assert_eq!(d.month(), 3); /// assert_eq!(d.day(), 14); /// assert_eq!(d.ordinal(), 73); // day of year - /// assert_eq!(d.isoweekdate(), (2015, 11, Weekday::Sat)); // ISO week and weekday + /// assert_eq!(d.iso_week().year(), 2015); + /// assert_eq!(d.iso_week().week(), 11); + /// assert_eq!(d.weekday(), Weekday::Sat); /// ~~~~ /// /// While not directly supported by Chrono, @@ -1128,21 +1138,9 @@ impl Datelike for NaiveDate { self.of().weekday() } - fn isoweekdate(&self) -> (i32, u32, Weekday) { - let of = self.of(); - let year = self.year(); - let (rawweek, weekday) = of.isoweekdate_raw(); - if rawweek < 1 { // previous year - let prevlastweek = YearFlags::from_year(year - 1).nisoweeks(); - (year - 1, prevlastweek, weekday) - } else { - let lastweek = of.flags().nisoweeks(); - if rawweek > lastweek { // next year - (year + 1, 1, weekday) - } else { - (year, rawweek, weekday) - } - } + #[inline] + fn iso_week(&self) -> IsoWeek { + isoweek::iso_week_from_yof(self.year(), self.of()) } /// Makes a new `NaiveDate` with the year number changed. @@ -1720,7 +1718,7 @@ mod tests { } #[test] - fn test_date_from_isoywd_and_isoweekdate() { + fn test_date_from_isoywd_and_iso_week() { for year in 2000..2401 { for week in 1..54 { for &weekday in [Weekday::Mon, Weekday::Tue, Weekday::Wed, Weekday::Thu, @@ -1729,10 +1727,9 @@ mod tests { if d.is_some() { let d = d.unwrap(); assert_eq!(d.weekday(), weekday); - let (year_, week_, weekday_) = d.isoweekdate(); - assert_eq!(year_, year); - assert_eq!(week_, week); - assert_eq!(weekday_, weekday); + let w = d.iso_week(); + assert_eq!(w.year(), year); + assert_eq!(w.week(), week); } } } @@ -1744,8 +1741,8 @@ mod tests { let d = NaiveDate::from_ymd_opt(year, month, day); if d.is_some() { let d = d.unwrap(); - let (year_, week_, weekday_) = d.isoweekdate(); - let d_ = NaiveDate::from_isoywd(year_, week_, weekday_); + let w = d.iso_week(); + let d_ = NaiveDate::from_isoywd(w.year(), w.week(), d.weekday()); assert_eq!(d, d_); } } diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index bd13edd..4fc0a4b 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -10,7 +10,7 @@ use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; use div::div_mod_floor; -use naive::{NaiveTime, NaiveDate}; +use naive::{NaiveTime, NaiveDate, IsoWeek}; use format::{Item, Numeric, Pad, Fixed}; use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems}; @@ -781,8 +781,8 @@ impl Datelike for NaiveDateTime { } #[inline] - fn isoweekdate(&self) -> (i32, u32, Weekday) { - self.date.isoweekdate() + fn iso_week(&self) -> IsoWeek { + self.date.iso_week() } /// Makes a new `NaiveDateTime` with the year number changed. diff --git a/src/naive/isoweek.rs b/src/naive/isoweek.rs new file mode 100644 index 0000000..3587bed --- /dev/null +++ b/src/naive/isoweek.rs @@ -0,0 +1,161 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 week. + +use std::fmt; + +use super::internals::{DateImpl, Of, YearFlags}; + +/// ISO 8601 week. +/// +/// This type, combined with [`Weekday`](../enum.Weekday.html), +/// constitues the ISO 8601 [week date](./struct.NaiveDate.html#week-date). +/// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types +/// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method. +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +pub struct IsoWeek { + // note that this allows for larger year range than `NaiveDate`. + // this is crucial because we have an edge case for the first and last week supported, + // which year number might not match the calendar year number. + ywf: DateImpl, // (year << 10) | (week << 4) | flag +} + +/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value. +// +// internal use only. we don't expose the public constructor for `IsoWeek` for now, +// because the year range for the week date and the calendar date do not match and +// it is confusing to have a date that is out of range in one and not in another. +// currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`. +pub fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek { + let (rawweek, _) = of.isoweekdate_raw(); + let (year, week) = if rawweek < 1 { // previous year + let prevlastweek = YearFlags::from_year(year - 1).nisoweeks(); + (year - 1, prevlastweek) + } else { + let lastweek = of.flags().nisoweeks(); + if rawweek > lastweek { // next year + (year + 1, 1) + } else { + (year, rawweek) + } + }; + IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | of.flags().0 as DateImpl } +} + +impl IsoWeek { + /// Returns the year number for this ISO week. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon); + /// assert_eq!(d.iso_week().year(), 2015); + /// ~~~~ + /// + /// This year number might not match the calendar year number. + /// Continuing the example... + /// + /// ~~~~ + /// # use chrono::{NaiveDate, Datelike, Weekday}; + /// # let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon); + /// assert_eq!(d.year(), 2014); + /// assert_eq!(d, NaiveDate::from_ymd(2014, 12, 29)); + /// ~~~~ + #[inline] + pub fn year(&self) -> i32 { + self.ywf >> 10 + } + + /// Returns the ISO week number starting from 1. + /// + /// The return value ranges from 1 to 53. (The last week of year differs by years.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon); + /// assert_eq!(d.iso_week().week(), 15); + /// ~~~~ + #[inline] + pub fn week(&self) -> u32 { + ((self.ywf >> 4) & 0x3f) as u32 + } + + /// Returns the ISO week number starting from 0. + /// + /// The return value ranges from 0 to 52. (The last week of year differs by years.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon); + /// assert_eq!(d.iso_week().week0(), 14); + /// ~~~~ + #[inline] + pub fn week0(&self) -> u32 { + ((self.ywf >> 4) & 0x3f) as u32 - 1 + } +} + +/// The `Debug` output of the ISO week `w` is same to +/// [`d.format("%G-W%V")`](../format/strftime/index.html) +/// where `d` is any `NaiveDate` value in that week. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::{NaiveDate, Datelike}; +/// +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5).iso_week()), "2015-W36"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52"); +/// ~~~~ +/// +/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. +/// +/// ~~~~ +/// # use chrono::{NaiveDate, Datelike}; +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52"); +/// ~~~~ +impl fmt::Debug for IsoWeek { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let year = self.year(); + let week = self.week(); + if 0 <= year && year <= 9999 { + write!(f, "{:04}-W{:02}", year, week) + } else { + // ISO 8601 requires the explicit sign for out-of-range years + write!(f, "{:+05}-W{:02}", year, week) + } + } +} + +#[cfg(test)] +mod tests { + use naive::{internals, MIN_DATE, MAX_DATE}; + use Datelike; + + #[test] + fn test_iso_week_extremes() { + let minweek = MIN_DATE.iso_week(); + let maxweek = MAX_DATE.iso_week(); + + assert_eq!(minweek.year(), internals::MIN_YEAR); + assert_eq!(minweek.week(), 1); + assert_eq!(minweek.week0(), 0); + assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string()); + + assert_eq!(maxweek.year(), internals::MAX_YEAR + 1); + assert_eq!(maxweek.week(), 1); + assert_eq!(maxweek.week0(), 0); + assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string()); + } +} From 7b9b0c4437548c88f2c849db65cd7064cc7bed3b Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 22 Jun 2017 00:33:17 +0900 Subject: [PATCH 51/53] Renamed `chrono::UTC` to `chrono::Utc`. Fixes #148. --- README.md | 70 ++++++++--------- src/date.rs | 6 +- src/datetime.rs | 172 +++++++++++++++++++++--------------------- src/format/parse.rs | 20 ++--- src/format/parsed.rs | 14 ++-- src/lib.rs | 74 +++++++++--------- src/naive/datetime.rs | 6 +- src/naive/time.rs | 14 ++-- src/offset/mod.rs | 24 +++--- src/offset/utc.rs | 44 +++++------ 10 files changed, 222 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 4149de7..ecf5852 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ the [**`TimeZone`**](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.h which defines how the local date is converted to and back from the UTC date. There are three well-known `TimeZone` implementations: -* [**`UTC`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.UTC.html) specifies the UTC time zone. It is most efficient. +* [**`Utc`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Utc.html) specifies the UTC time zone. It is most efficient. * [**`Local`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Local.html) specifies the system local time zone. @@ -120,14 +120,14 @@ but can be converted to each other using the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.with_timezone) method. You can get the current date and time in the UTC time zone -([`UTC::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.UTC.html#method.now)) +([`Utc::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Utc.html#method.now)) or in the local time zone ([`Local::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Local.html#method.now)). ```rust use chrono::prelude::*; -let utc: DateTime = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z` +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` ``` @@ -139,21 +139,21 @@ but in turn we get a rich combination of initialization methods. use chrono::prelude::*; use chrono::offset::LocalResult; -let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z` +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)); +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)); +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)); +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); +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); // other time zone objects can be used to construct a local datetime. // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical. @@ -187,7 +187,7 @@ assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and includ // time zone accessor and manipulation assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600)); -assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); +assert_eq!(dt.with_timezone(&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 @@ -195,14 +195,14 @@ 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 -let dt1 = UTC.ymd(2014, 11, 14).and_hms(8, 9, 10); -let dt2 = UTC.ymd(2014, 11, 14).and_hms(10, 9, 8); +let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); +let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8); assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2)); assert_eq!(dt2.signed_duration_since(dt1), 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)); +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`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.format) method, @@ -218,7 +218,7 @@ for well-known formats. ```rust use chrono::prelude::*; -let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); +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"); assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); @@ -233,7 +233,7 @@ Parsing can be done with three methods: 1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait (and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method - on a string) can be used for parsing `DateTime`, `DateTime` and + on a string) can be used for parsing `DateTime`, `DateTime` and `DateTime` values. This parses what the `{:?}` ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) format specifier prints, and requires the offset to be present. @@ -259,12 +259,12 @@ More detailed control over the parsing process is available via ```rust use chrono::prelude::*; -let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); +let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9); let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600)); // method 1 -assert_eq!("2014-11-28T12:00:09Z".parse::>(), Ok(dt.clone())); -assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(dt.clone())); +assert_eq!("2014-11-28T12:00:09Z".parse::>(), Ok(dt.clone())); +assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(dt.clone())); assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(fixed_dt.clone())); // method 2 @@ -275,15 +275,15 @@ assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"), assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone())); // method 3 -assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); -assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone())); +assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); +assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone())); // oops, the year is missing! -assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()); +assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()); // oops, the format string does not include the year at all! -assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); +assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); // oops, the weekday is incorrect! -assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); +assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); ``` ### Individual date @@ -296,12 +296,12 @@ Most operations available to `DateTime` are also available to `Date` whenever ap use chrono::prelude::*; use chrono::offset::LocalResult; -assert_eq!(UTC::today(), UTC::now().date()); +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.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), +assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri); +assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None); +assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809"); ``` @@ -347,7 +347,7 @@ if you want. Chrono inherently does not support an inaccurate or partial date and time representation. Any operation that can be ambiguous will return `None` in such cases. For example, "a month later" of 2014-01-30 is not well-defined -and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`. +and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`. Advanced time zone handling is not yet supported. For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. diff --git a/src/date.rs b/src/date.rs index e02380a..7a049d6 100644 --- a/src/date.rs +++ b/src/date.rs @@ -9,7 +9,7 @@ use std::ops::{Add, Sub}; use oldtime::Duration as OldDuration; use {Weekday, Datelike}; -use offset::{TimeZone, UTC}; +use offset::{TimeZone, Utc}; use naive::{self, NaiveDate, NaiveTime, IsoWeek}; use DateTime; use format::{Item, DelayedFormat, StrftimeItems}; @@ -45,9 +45,9 @@ pub struct Date { } /// The minimum possible `Date`. -pub const MIN_DATE: Date = Date { date: naive::MIN_DATE, offset: UTC }; +pub const MIN_DATE: Date = Date { date: naive::MIN_DATE, offset: Utc }; /// The maximum possible `Date`. -pub const MAX_DATE: Date = Date { date: naive::MAX_DATE, offset: UTC }; +pub const MAX_DATE: Date = Date { date: naive::MAX_DATE, offset: Utc }; impl Date { /// Makes a new `Date` with given *UTC* date and offset. diff --git a/src/datetime.rs b/src/datetime.rs index 319c7b8..34bbaf3 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -11,7 +11,7 @@ use std::ops::Deref; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; -use offset::{TimeZone, Offset, UTC, Local, FixedOffset}; +use offset::{TimeZone, Offset, Utc, Local, FixedOffset}; use naive::{NaiveTime, NaiveDateTime, IsoWeek}; use Date; use format::{Item, Numeric, Pad, Fixed}; @@ -59,10 +59,10 @@ impl DateTime { /// # Example /// /// ~~~~ - /// use chrono::{DateTime, TimeZone, NaiveDateTime, UTC}; + /// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc}; /// - /// let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), UTC); - /// assert_eq!(UTC.timestamp(61, 0), dt); + /// let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); + /// assert_eq!(Utc.timestamp(61, 0), dt); /// ~~~~ // // note: this constructor is purposedly not named to `new` to discourage the direct usage. @@ -405,11 +405,11 @@ impl str::FromStr for DateTime { } } -impl str::FromStr for DateTime { +impl str::FromStr for DateTime { type Err = ParseError; - fn from_str(s: &str) -> ParseResult> { - s.parse::>().map(|dt| dt.with_timezone(&UTC)) + fn from_str(s: &str) -> ParseResult> { + s.parse::>().map(|dt| dt.with_timezone(&Utc)) } } @@ -422,12 +422,12 @@ impl str::FromStr for DateTime { } #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_encodable_json(to_string_utc: FUTC, to_string_fixed: FFixed) - where FUTC: Fn(&DateTime) -> Result, +fn test_encodable_json(to_string_utc: FUtc, to_string_fixed: FFixed) + where FUtc: Fn(&DateTime) -> Result, FFixed: Fn(&DateTime) -> Result, E: ::std::fmt::Debug { - assert_eq!(to_string_utc(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), + assert_eq!(to_string_utc(&Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), Some(r#""2014-07-24T12:34:06Z""#.into())); assert_eq!(to_string_fixed(&FixedOffset::east(3660).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), @@ -437,10 +437,10 @@ fn test_encodable_json(to_string_utc: FUTC, to_string_fixed: FF } #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_decodable_json(utc_from_str: FUTC, - fixed_from_str: FFixed, - local_from_str: FLocal) - where FUTC: Fn(&str) -> Result, E>, +fn test_decodable_json(utc_from_str: FUtc, + fixed_from_str: FFixed, + local_from_str: FLocal) + where FUtc: Fn(&str) -> Result, E>, FFixed: Fn(&str) -> Result, E>, FLocal: Fn(&str) -> Result, E>, E: ::std::fmt::Debug @@ -451,9 +451,9 @@ fn test_decodable_json(utc_from_str: FUTC, } assert_eq!(norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()), - norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + norm(&Some(Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)))); assert_eq!(norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), - norm(&Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)))); + norm(&Some(Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)))); assert_eq!(norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6)))); @@ -464,20 +464,20 @@ fn test_decodable_json(utc_from_str: FUTC, // the conversion didn't change the instant itself assert_eq!(local_from_str(r#""2014-07-24T12:34:06Z""#) .expect("local shouuld parse"), - UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)); assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#) .expect("local should parse with offset"), - UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)); + Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)); assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); } #[cfg(all(test, feature = "rustc-serialize"))] -fn test_decodable_json_timestamps(utc_from_str: FUTC, +fn test_decodable_json_timestamps(utc_from_str: FUtc, fixed_from_str: FFixed, local_from_str: FLocal) - where FUTC: Fn(&str) -> Result, E>, + where FUtc: Fn(&str) -> Result, E>, FFixed: Fn(&str) -> Result, E>, FLocal: Fn(&str) -> Result, E>, E: ::std::fmt::Debug @@ -487,9 +487,9 @@ fn test_decodable_json_timestamps(utc_from_str: FUTC, } assert_eq!(norm(&utc_from_str("0").ok().map(DateTime::from)), - norm(&Some(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)))); + norm(&Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)))); assert_eq!(norm(&utc_from_str("-1").ok().map(DateTime::from)), - norm(&Some(UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)))); + norm(&Some(Utc.ymd(1969, 12, 31).and_hms(23, 59, 59)))); assert_eq!(norm(&fixed_from_str("0").ok().map(DateTime::from)), norm(&Some(FixedOffset::east(0).ymd(1970, 1, 1).and_hms(0, 0, 0)))); @@ -497,16 +497,16 @@ fn test_decodable_json_timestamps(utc_from_str: FUTC, norm(&Some(FixedOffset::east(0).ymd(1969, 12, 31).and_hms(23, 59, 59)))); assert_eq!(*fixed_from_str("0").expect("0 timestamp should parse"), - UTC.ymd(1970, 1, 1).and_hms(0, 0, 0)); + Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)); assert_eq!(*local_from_str("-1").expect("-1 timestamp should parse"), - UTC.ymd(1969, 12, 31).and_hms(23, 59, 59)); + Utc.ymd(1969, 12, 31).and_hms(23, 59, 59)); } #[cfg(feature = "rustc-serialize")] mod rustc_serialize { use std::fmt; use super::{DateTime, TsSeconds}; - use offset::{TimeZone, LocalResult, UTC, Local, FixedOffset}; + use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; impl Encodable for DateTime { @@ -543,18 +543,18 @@ mod rustc_serialize { } } - impl Decodable for DateTime { - fn decode(d: &mut D) -> Result, D::Error> { + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { d.read_str()? .parse::>() - .map(|dt| dt.with_timezone(&UTC)) + .map(|dt| dt.with_timezone(&Utc)) .map_err(|_| d.error("invalid date and time")) } } - impl Decodable for TsSeconds { - fn decode(d: &mut D) -> Result, D::Error> { - from(UTC.timestamp_opt(d.read_i64()?, 0), d) + impl Decodable for TsSeconds { + fn decode(d: &mut D) -> Result, D::Error> { + from(Utc.timestamp_opt(d.read_i64()?, 0), d) .map(|dt| TsSeconds(dt)) } } @@ -570,7 +570,7 @@ mod rustc_serialize { impl Decodable for TsSeconds { fn decode(d: &mut D) -> Result, D::Error> { - from(UTC.timestamp_opt(d.read_i64()?, 0), d) + from(Utc.timestamp_opt(d.read_i64()?, 0), d) .map(|dt| TsSeconds(dt.with_timezone(&Local))) } } @@ -602,7 +602,7 @@ mod rustc_serialize { pub mod serde { use std::fmt; use super::DateTime; - use offset::{TimeZone, LocalResult, UTC, Local, FixedOffset}; + use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset}; use serdelib::{ser, de}; /// Ser/de to/from timestamps in seconds @@ -619,16 +619,16 @@ pub mod serde { /// # #[macro_use] extern crate serde_derive; /// # #[macro_use] extern crate serde_json; /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, UTC}; + /// # use chrono::{TimeZone, DateTime, Utc}; /// use chrono::serde::ts_seconds; /// #[derive(Deserialize, Serialize)] /// struct S { /// #[serde(with = "ts_seconds")] - /// time: DateTime + /// time: DateTime /// } /// /// # fn example() -> Result { - /// let time = UTC.ymd(2015, 5, 15).and_hms(10, 0, 0); + /// let time = Utc.ymd(2015, 5, 15).and_hms(10, 0, 0); /// let my_s = S { /// time: time.clone(), /// }; @@ -645,7 +645,7 @@ pub mod serde { use std::fmt; use serdelib::{ser, de}; - use {DateTime, UTC, FixedOffset}; + use {DateTime, Utc, FixedOffset}; use offset::TimeZone; use super::from; @@ -663,12 +663,12 @@ pub mod serde { /// # #[macro_use] extern crate serde_derive; /// # #[macro_use] extern crate serde_json; /// # extern crate chrono; - /// # use chrono::{DateTime, UTC}; + /// # use chrono::{DateTime, Utc}; /// use chrono::serde::ts_seconds::deserialize as from_ts; /// #[derive(Deserialize)] /// struct S { /// #[serde(deserialize_with = "from_ts")] - /// time: DateTime + /// time: DateTime /// } /// /// # fn example() -> Result { @@ -677,10 +677,10 @@ pub mod serde { /// # } /// # fn main() { example().unwrap(); } /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> where D: de::Deserializer<'de> { - Ok(try!(d.deserialize_i64(SecondsTimestampVisitor).map(|dt| dt.with_timezone(&UTC)))) + Ok(try!(d.deserialize_i64(SecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc)))) } /// Serialize a UTC datetime into an integer number of seconds since the epoch @@ -697,17 +697,17 @@ pub mod serde { /// # #[macro_use] extern crate serde_derive; /// # #[macro_use] extern crate serde_json; /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, UTC}; + /// # use chrono::{TimeZone, DateTime, Utc}; /// use chrono::serde::ts_seconds::serialize as to_ts; /// #[derive(Serialize)] /// struct S { /// #[serde(serialize_with = "to_ts")] - /// time: DateTime + /// time: DateTime /// } /// /// # fn example() -> Result { /// let my_s = S { - /// time: UTC.ymd(2015, 5, 15).and_hms(10, 0, 0), + /// time: Utc.ymd(2015, 5, 15).and_hms(10, 0, 0), /// }; /// let as_string = serde_json::to_string(&my_s)?; /// assert_eq!(as_string, r#"{"time":1431684000}"#); @@ -715,7 +715,7 @@ pub mod serde { /// # } /// # fn main() { example().unwrap(); } /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result + pub fn serialize(dt: &DateTime, serializer: S) -> Result where S: ser::Serializer { serializer.serialize_i64(dt.timestamp()) @@ -819,11 +819,11 @@ pub mod serde { /// /// The serialized value can be either a string representation or a unix /// timestamp - impl<'de> de::Deserialize<'de> for DateTime { + impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de> { - deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&UTC)) + deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Utc)) } } @@ -860,9 +860,9 @@ pub mod serde { // it is not self-describing. use self::bincode::{Infinite, serialize, deserialize}; - let dt = UTC.ymd(2014, 7, 24).and_hms(12, 34, 6); + let dt = Utc.ymd(2014, 7, 24).and_hms(12, 34, 6); let encoded = serialize(&dt, Infinite).unwrap(); - let decoded: DateTime = deserialize(&encoded).unwrap(); + let decoded: DateTime = deserialize(&encoded).unwrap(); assert_eq!(dt, decoded); assert_eq!(dt.offset(), decoded.offset()); } @@ -873,52 +873,52 @@ mod tests { use super::DateTime; use Datelike; use naive::{NaiveTime, NaiveDate}; - use offset::{TimeZone, UTC, Local, FixedOffset}; + use offset::{TimeZone, Utc, Local, FixedOffset}; use oldtime::Duration; #[test] #[allow(non_snake_case)] fn test_datetime_offset() { - let EST = FixedOffset::west(5*60*60); - let EDT = FixedOffset::west(4*60*60); - let KST = FixedOffset::east(9*60*60); + let Est = FixedOffset::west(5*60*60); + let Edt = FixedOffset::west(4*60*60); + let Kst = FixedOffset::east(9*60*60); - assert_eq!(format!("{}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)), + assert_eq!(format!("{}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06 07:08:09 UTC"); - assert_eq!(format!("{}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)), + assert_eq!(format!("{}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06 07:08:09 -04:00"); - assert_eq!(format!("{}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)), + assert_eq!(format!("{}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06 07:08:09 +09:00"); - assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)), + assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06T07:08:09Z"); - assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)), + assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06T07:08:09-04:00"); - assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)), + assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06T07:08:09+09:00"); // edge cases - assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(0, 0, 0)), + assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(0, 0, 0)), "2014-05-06T00:00:00Z"); - assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(0, 0, 0)), + assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(0, 0, 0)), "2014-05-06T00:00:00-04:00"); - assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(0, 0, 0)), + assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(0, 0, 0)), "2014-05-06T00:00:00+09:00"); - assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(23, 59, 59)), + assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(23, 59, 59)), "2014-05-06T23:59:59Z"); - assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(23, 59, 59)), + assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(23, 59, 59)), "2014-05-06T23:59:59-04:00"); - assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(23, 59, 59)), + assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(23, 59, 59)), "2014-05-06T23:59:59+09:00"); - let dt = UTC.ymd(2014, 5, 6).and_hms(7, 8, 9); - assert_eq!(dt, EDT.ymd(2014, 5, 6).and_hms(3, 8, 9)); - assert_eq!(dt + Duration::seconds(3600 + 60 + 1), UTC.ymd(2014, 5, 6).and_hms(8, 9, 10)); - assert_eq!(dt.signed_duration_since(EDT.ymd(2014, 5, 6).and_hms(10, 11, 12)), + let dt = Utc.ymd(2014, 5, 6).and_hms(7, 8, 9); + assert_eq!(dt, Edt.ymd(2014, 5, 6).and_hms(3, 8, 9)); + assert_eq!(dt + Duration::seconds(3600 + 60 + 1), Utc.ymd(2014, 5, 6).and_hms(8, 9, 10)); + assert_eq!(dt.signed_duration_since(Edt.ymd(2014, 5, 6).and_hms(10, 11, 12)), Duration::seconds(-7*3600 - 3*60 - 3)); - assert_eq!(*UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), UTC); - assert_eq!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), EDT); - assert!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != EST); + assert_eq!(*Utc.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Utc); + assert_eq!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Edt); + assert!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != Est); } #[test] @@ -948,7 +948,7 @@ mod tests { #[test] fn test_datetime_with_timezone() { let local_now = Local::now(); - let utc_now = local_now.with_timezone(&UTC); + let utc_now = local_now.with_timezone(&Utc); let local_now2 = utc_now.with_timezone(&Local); assert_eq!(local_now, local_now2); } @@ -957,9 +957,9 @@ mod tests { #[allow(non_snake_case)] fn test_datetime_rfc2822_and_rfc3339() { let EDT = FixedOffset::east(5*60*60); - assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(), + assert_eq!(Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(), "Wed, 18 Feb 2015 23:16:09 +0000"); - assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(), + assert_eq!(Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(), "2015-02-18T23:16:09+00:00"); assert_eq!(EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc2822(), "Wed, 18 Feb 2015 23:16:09 +0500"); @@ -988,11 +988,11 @@ mod tests { Ok(FixedOffset::west(10 * 3600).ymd(2015, 2, 18).and_hms_milli(13, 16, 9, 150))); assert!("2015-2-18T23:16:9.15".parse::>().is_err()); - assert_eq!("2015-2-18T23:16:9.15Z".parse::>(), - Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150))); - assert_eq!("2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150))); - assert!("2015-2-18T23:16:9.15".parse::>().is_err()); + assert_eq!("2015-2-18T23:16:9.15Z".parse::>(), + Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150))); + assert_eq!("2015-2-18T13:16:9.15-10:00".parse::>(), + Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150))); + assert!("2015-2-18T23:16:9.15".parse::>().is_err()); // no test for `DateTime`, we cannot verify that much. } @@ -1005,22 +1005,22 @@ mod tests { assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT").is_err()); - assert_eq!(UTC.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", + assert_eq!(Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), - Ok(UTC.ymd(2013, 8, 9).and_hms(23, 54, 35))); + Ok(Utc.ymd(2013, 8, 9).and_hms(23, 54, 35))); } #[test] fn test_datetime_format_with_local() { // if we are not around the year boundary, local and UTC date should have the same year let dt = Local::now().with_month(5).unwrap(); - assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&UTC).format("%Y").to_string()); + assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string()); } #[test] fn test_datetime_is_copy() { // UTC is known to be `Copy`. - let a = UTC::now(); + let a = Utc::now(); let b = a; assert_eq!(a, b); } @@ -1030,7 +1030,7 @@ mod tests { use std::thread; // UTC is known to be `Send`. - let a = UTC::now(); + let a = Utc::now(); thread::spawn(move || { let _ = a; }).join().unwrap(); @@ -1038,7 +1038,7 @@ mod tests { #[test] fn test_subsecond_part() { - let datetime = UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 1234567); + let datetime = Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 1234567); assert_eq!(1, datetime.timestamp_subsec_millis()); assert_eq!(1234, datetime.timestamp_subsec_micros()); diff --git a/src/format/parse.rs b/src/format/parse.rs index 0c8724b..23c7596 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -655,32 +655,32 @@ fn test_rfc2822() { #[cfg(test)] #[test] fn parse_rfc850() { - use ::{UTC, TimeZone}; + use ::{Utc, TimeZone}; static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT"; let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT"; - let dt = UTC.ymd(1994, 11, 6).and_hms(8, 49, 37); + let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37); // Check that the format is what we expect assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str); // Check that it parses correctly - assert_eq!(Ok(dt), UTC.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT)); + assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT)); // Check that the rest of the weekdays parse correctly (this test originally failed because // Sunday parsed incorrectly). let testdates = [ - (UTC.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"), - (UTC.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"), - (UTC.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"), - (UTC.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"), - (UTC.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"), - (UTC.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"), + (Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"), + (Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"), + (Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"), + (Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"), + (Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"), + (Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"), ]; for val in &testdates { - assert_eq!(Ok(val.0), UTC.datetime_from_str(val.1, RFC850_FMT)); + assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT)); } } diff --git a/src/format/parsed.rs b/src/format/parsed.rs index fd1b723..3ba371d 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -642,7 +642,7 @@ mod tests { use Datelike; use Weekday::*; use naive::{MIN_DATE, MAX_DATE, NaiveDate, NaiveTime}; - use offset::{TimeZone, UTC, FixedOffset}; + use offset::{TimeZone, Utc, FixedOffset}; #[test] fn test_parsed_set_fields() { @@ -1055,11 +1055,11 @@ mod tests { } // single result from ymdhms - assert_eq!(parse!(UTC; + assert_eq!(parse!(Utc; year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), - Ok(UTC.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678))); - assert_eq!(parse!(UTC; + Ok(Utc.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678))); + assert_eq!(parse!(Utc; year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), Err(IMPOSSIBLE)); @@ -1074,9 +1074,9 @@ mod tests { .and_hms_nano(13, 26, 40, 12_345_678))); // single result from timestamp - assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 0), - Ok(UTC.ymd(2014, 12, 31).and_hms(4, 26, 40))); - assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 32400), + assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 0), + Ok(Utc.ymd(2014, 12, 31).and_hms(4, 26, 40))); + assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE)); assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0), Err(IMPOSSIBLE)); diff --git a/src/lib.rs b/src/lib.rs index a4dd502..cd68892 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,7 @@ //! which defines how the local date is converted to and back from the UTC date. //! There are three well-known `TimeZone` implementations: //! -//! * [**`UTC`**](./offset/struct.UTC.html) specifies the UTC time zone. It is most efficient. +//! * [**`Utc`**](./offset/struct.Utc.html) specifies the UTC time zone. It is most efficient. //! //! * [**`Local`**](./offset/struct.Local.html) specifies the system local time zone. //! @@ -106,14 +106,14 @@ //! the [`DateTime::with_timezone`](./struct.DateTime.html#method.with_timezone) method. //! //! You can get the current date and time in the UTC time zone -//! ([`UTC::now()`](./offset/struct.UTC.html#method.now)) +//! ([`Utc::now()`](./offset/struct.Utc.html#method.now)) //! or in the local time zone //! ([`Local::now()`](./offset/struct.Local.html#method.now)). //! //! ```rust //! use chrono::prelude::*; //! -//! let utc: DateTime = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z` +//! 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; //! ``` @@ -126,21 +126,21 @@ //! use chrono::prelude::*; //! use chrono::offset::LocalResult; //! -//! let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z` +//! 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)); +//! 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)); +//! 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)); +//! 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); +//! 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); //! //! // other time zone objects can be used to construct a local datetime. //! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical. @@ -179,7 +179,7 @@ //! // time zone accessor and manipulation //! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); //! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600)); -//! assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); +//! assert_eq!(dt.with_timezone(&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 @@ -187,14 +187,14 @@ //! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE //! //! // arithmetic operations -//! let dt1 = UTC.ymd(2014, 11, 14).and_hms(8, 9, 10); -//! let dt2 = UTC.ymd(2014, 11, 14).and_hms(10, 9, 8); +//! let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); +//! let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8); //! assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2)); //! assert_eq!(dt2.signed_duration_since(dt1), 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)); +//! 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)); //! # } //! ``` //! @@ -211,7 +211,7 @@ //! ```rust //! use chrono::prelude::*; //! -//! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); +//! 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"); //! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); //! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); @@ -226,7 +226,7 @@ //! //! 1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait //! (and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method -//! on a string) can be used for parsing `DateTime`, `DateTime` and +//! on a string) can be used for parsing `DateTime`, `DateTime` and //! `DateTime` values. This parses what the `{:?}` //! ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) //! format specifier prints, and requires the offset to be present. @@ -252,12 +252,12 @@ //! ```rust //! use chrono::prelude::*; //! -//! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); +//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9); //! let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600)); //! //! // method 1 -//! assert_eq!("2014-11-28T12:00:09Z".parse::>(), Ok(dt.clone())); -//! assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(dt.clone())); +//! assert_eq!("2014-11-28T12:00:09Z".parse::>(), Ok(dt.clone())); +//! assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(dt.clone())); //! assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(fixed_dt.clone())); //! //! // method 2 @@ -268,15 +268,15 @@ //! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone())); //! //! // method 3 -//! assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); -//! assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone())); +//! assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); +//! assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone())); //! //! // oops, the year is missing! -//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()); +//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()); //! // oops, the format string does not include the year at all! -//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); +//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); //! // oops, the weekday is incorrect! -//! assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); +//! assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); //! ``` //! //! ### Individual date @@ -290,12 +290,12 @@ //! use chrono::offset::LocalResult; //! //! # // 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!(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.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), +//! assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri); +//! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None); +//! assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), //! "070809"); //! ``` //! @@ -341,7 +341,7 @@ //! Chrono inherently does not support an inaccurate or partial date and time representation. //! Any operation that can be ambiguous will return `None` in such cases. //! For example, "a month later" of 2014-01-30 is not well-defined -//! and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`. +//! and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`. //! //! Advanced time zone handling is not yet supported. //! For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. @@ -361,7 +361,7 @@ extern crate serde as serdelib; // this reexport is to aid the transition and should not be in the prelude! pub use oldtime::Duration; -#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, UTC, FixedOffset, Local}; +#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, Utc, FixedOffset, Local}; #[doc(no_inline)] pub use naive::{NaiveDate, IsoWeek, NaiveTime, NaiveDateTime}; pub use date::{Date, MIN_DATE, MAX_DATE}; pub use datetime::DateTime; @@ -372,7 +372,7 @@ pub use format::{ParseError, ParseResult}; pub mod prelude { #[doc(no_inline)] pub use {Datelike, Timelike, Weekday}; #[doc(no_inline)] pub use {TimeZone, Offset}; - #[doc(no_inline)] pub use {UTC, FixedOffset, Local}; + #[doc(no_inline)] pub use {Utc, FixedOffset, Local}; #[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime}; #[doc(no_inline)] pub use Date; #[doc(no_inline)] pub use DateTime; diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 4fc0a4b..2f60910 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -1571,7 +1571,7 @@ pub mod serde { /// # extern crate serde_json; /// # extern crate serde; /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, UTC}; + /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; /// use chrono::naive::serde::ts_seconds; /// #[derive(Deserialize, Serialize)] /// struct S { @@ -1614,7 +1614,7 @@ pub mod serde { /// # #[macro_use] extern crate serde_json; /// # extern crate serde; /// # extern crate chrono; - /// # use chrono::{NaiveDateTime, UTC}; + /// # use chrono::{NaiveDateTime, Utc}; /// # use serde::Deserialize; /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; /// #[derive(Deserialize)] @@ -1650,7 +1650,7 @@ pub mod serde { /// # #[macro_use] extern crate serde_json; /// # #[macro_use] extern crate serde; /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, UTC}; + /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; /// # use serde::Serialize; /// use chrono::naive::serde::ts_seconds::serialize as to_ts; /// #[derive(Serialize)] diff --git a/src/naive/time.rs b/src/naive/time.rs index f51b2dc..ced33f8 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -58,13 +58,13 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// All methods accepting fractional seconds will accept such values. /// /// ~~~~ -/// use chrono::{NaiveDate, NaiveTime, UTC, TimeZone}; +/// use chrono::{NaiveDate, NaiveTime, Utc, TimeZone}; /// /// let t = NaiveTime::from_hms_milli(8, 59, 59, 1_000); /// /// let dt1 = NaiveDate::from_ymd(2015, 7, 1).and_hms_micro(8, 59, 59, 1_000_000); /// -/// let dt2 = UTC.ymd(2015, 6, 30).and_hms_nano(23, 59, 59, 1_000_000_000); +/// let dt2 = Utc.ymd(2015, 6, 30).and_hms_nano(23, 59, 59, 1_000_000_000); /// # let _ = (t, dt1, dt2); /// ~~~~ /// @@ -141,9 +141,9 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// will be represented as the second part being 60, as required by ISO 8601. /// /// ~~~~ -/// use chrono::{UTC, TimeZone}; +/// use chrono::{Utc, TimeZone}; /// -/// let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_000); +/// let dt = Utc.ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_000); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z"); /// ~~~~ /// @@ -155,12 +155,12 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// and would be read back to the next non-leap second. /// /// ~~~~ -/// use chrono::{DateTime, UTC, TimeZone}; +/// use chrono::{DateTime, Utc, TimeZone}; /// -/// let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000); +/// let dt = Utc.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); /// -/// let dt = UTC.ymd(2015, 6, 30).and_hms(23, 56, 5); +/// let dt = Utc.ymd(2015, 6, 30).and_hms(23, 56, 5); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); /// assert_eq!(DateTime::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); /// ~~~~ diff --git a/src/offset/mod.rs b/src/offset/mod.rs index bc51366..6014e60 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -178,9 +178,9 @@ pub trait TimeZone: Sized + Clone { /// # Example /// /// ~~~~ - /// use chrono::{UTC, TimeZone}; + /// use chrono::{Utc, TimeZone}; /// - /// assert_eq!(UTC.ymd(2015, 5, 15).to_string(), "2015-05-15UTC"); + /// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC"); /// ~~~~ fn ymd(&self, year: i32, month: u32, day: u32) -> Date { self.ymd_opt(year, month, day).unwrap() @@ -197,10 +197,10 @@ pub trait TimeZone: Sized + Clone { /// # Example /// /// ~~~~ - /// use chrono::{UTC, LocalResult, TimeZone}; + /// use chrono::{Utc, LocalResult, TimeZone}; /// - /// assert_eq!(UTC.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC"); - /// assert_eq!(UTC.ymd_opt(2000, 0, 0), LocalResult::None); + /// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC"); + /// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None); /// ~~~~ fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult> { match NaiveDate::from_ymd_opt(year, month, day) { @@ -220,9 +220,9 @@ pub trait TimeZone: Sized + Clone { /// # Example /// /// ~~~~ - /// use chrono::{UTC, TimeZone}; + /// use chrono::{Utc, TimeZone}; /// - /// assert_eq!(UTC.yo(2015, 135).to_string(), "2015-05-15UTC"); + /// assert_eq!(Utc.yo(2015, 135).to_string(), "2015-05-15UTC"); /// ~~~~ fn yo(&self, year: i32, ordinal: u32) -> Date { self.yo_opt(year, ordinal).unwrap() @@ -255,9 +255,9 @@ pub trait TimeZone: Sized + Clone { /// # Example /// /// ~~~~ - /// use chrono::{UTC, Weekday, TimeZone}; + /// use chrono::{Utc, Weekday, TimeZone}; /// - /// assert_eq!(UTC.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC"); + /// assert_eq!(Utc.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC"); /// ~~~~ fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date { self.isoywd_opt(year, week, weekday).unwrap() @@ -288,9 +288,9 @@ pub trait TimeZone: Sized + Clone { /// # Example /// /// ~~~~ - /// use chrono::{UTC, TimeZone}; + /// use chrono::{Utc, TimeZone}; /// - /// assert_eq!(UTC.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC"); + /// assert_eq!(Utc.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC"); /// ~~~~ fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime { self.timestamp_opt(secs, nsecs).unwrap() @@ -371,7 +371,7 @@ mod utc; mod fixed; mod local; -pub use self::utc::UTC; +pub use self::utc::Utc; pub use self::fixed::FixedOffset; pub use self::local::Local; diff --git a/src/offset/utc.rs b/src/offset/utc.rs index 684be75..ffdd53e 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -14,59 +14,59 @@ use super::{TimeZone, Offset, LocalResult, FixedOffset}; /// It is also used as an offset (which is also a dummy type). /// /// Using the [`TimeZone`](./trait.TimeZone.html) methods -/// on the UTC struct is the preferred way to construct `DateTime` +/// on the UTC struct is the preferred way to construct `DateTime` /// instances. /// /// # Example /// /// ~~~~ -/// use chrono::{DateTime, TimeZone, NaiveDateTime, UTC}; +/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc}; /// -/// let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), UTC); +/// let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); /// -/// assert_eq!(UTC.timestamp(61, 0), dt); -/// assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); +/// assert_eq!(Utc.timestamp(61, 0), dt); +/// assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); /// ~~~~ #[derive(Copy, Clone, PartialEq, Eq)] -pub struct UTC; +pub struct Utc; -impl UTC { +impl Utc { /// Returns a `Date` which corresponds to the current date. - pub fn today() -> Date { UTC::now().date() } + pub fn today() -> Date { Utc::now().date() } /// Returns a `DateTime` which corresponds to the current date. - pub fn now() -> DateTime { + pub fn now() -> DateTime { let spec = oldtime::get_time(); let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32); - DateTime::from_utc(naive, UTC) + DateTime::from_utc(naive, Utc) } } -impl TimeZone for UTC { - type Offset = UTC; +impl TimeZone for Utc { + type Offset = Utc; - fn from_offset(_state: &UTC) -> UTC { UTC } + fn from_offset(_state: &Utc) -> Utc { Utc } - fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult { - LocalResult::Single(UTC) + fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult { + LocalResult::Single(Utc) } - fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult { - LocalResult::Single(UTC) + fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult { + LocalResult::Single(Utc) } - fn offset_from_utc_date(&self, _utc: &NaiveDate) -> UTC { UTC } - fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> UTC { UTC} + fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc { Utc } + fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc { Utc } } -impl Offset for UTC { +impl Offset for Utc { fn fix(&self) -> FixedOffset { FixedOffset::east(0) } } -impl fmt::Debug for UTC { +impl fmt::Debug for Utc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") } } -impl fmt::Display for UTC { +impl fmt::Display for Utc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") } } From c3c34da141beab86f4d90fd334e2bb616d63e025 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 22 Jun 2017 00:40:48 +0900 Subject: [PATCH 52/53] Clarified that time arithmetic notations are for explanation only. Also fixes yet another remaining broken anchor links. Fixes #136. --- src/naive/datetime.rs | 2 +- src/naive/time.rs | 42 +++++++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index 2f60910..380fdde 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -187,7 +187,7 @@ impl NaiveDateTime { /// Ok(NaiveDate::from_ymd(2014, 5, 17).and_hms(12, 34, 56))); /// ~~~~ /// - /// [Leap seconds](./index.html#leap-second-handling) are correctly handled by + /// [Leap seconds](./struct.NaiveTime.html#leap-second-handling) are correctly handled by /// treating any time of the form `hh:mm:60` as a leap second. /// (This equally applies to the formatting, so the round trip is possible.) /// diff --git a/src/naive/time.rs b/src/naive/time.rs index ced33f8..52efec7 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -76,9 +76,11 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// ## Date And Time Arithmetics /// /// As a concrete example, let's assume that `03:00:60` and `04:00:60` are leap seconds. -/// (In reality, of course, leap seconds are separated by at least 6 months.) +/// In reality, of course, leap seconds are separated by at least 6 months. +/// We will also use some intuitive concise notations for the explanation. /// -/// `Time + Duration`: +/// `Time + Duration` +/// (short for [`NaiveTime::overflowing_add_signed`](#method.overflowing_add_signed)): /// /// - `03:00:00 + 1s = 03:00:01`. /// - `03:00:59 + 60s = 03:02:00`. @@ -89,7 +91,8 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// - `03:00:60 + 61s = 03:02:00`. /// - `03:00:60.1 + 0.8s = 03:00:60.9`. /// -/// `Time - Duration`: +/// `Time - Duration` +/// (short for [`NaiveTime::overflowing_sub_signed`](#method.overflowing_sub_signed)): /// /// - `03:00:00 - 1s = 02:59:59`. /// - `03:01:00 - 1s = 03:00:59`. @@ -99,7 +102,8 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem /// - `03:00:60.7 - 0.4s = 03:00:60.3`. /// - `03:00:60.7 - 0.9s = 03:00:59.8`. /// -/// `Time - Time`: +/// `Time - Time` +/// (short for [`NaiveTime::signed_duration_since`](#method.signed_duration_since)): /// /// - `04:00:00 - 03:00:00 = 3600s`. /// - `03:01:00 - 03:00:00 = 60s`. @@ -176,7 +180,7 @@ pub struct NaiveTime { impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute and second. /// - /// No [leap second](./index.html#leap-second-handling) is allowed here; + /// No [leap second](#leap-second-handling) is allowed here; /// use `NaiveTime::from_hms_*` methods with a subsecond parameter instead. /// /// Panics on invalid hour, minute and/or second. @@ -199,7 +203,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute and second. /// - /// No [leap second](./index.html#leap-second-handling) is allowed here; + /// No [leap second](#leap-second-handling) is allowed here; /// use `NaiveTime::from_hms_*_opt` methods with a subsecond parameter instead. /// /// Returns `None` on invalid hour, minute and/or second. @@ -225,7 +229,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute, second and millisecond. /// /// The millisecond part can exceed 1,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or millisecond. /// @@ -248,7 +252,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute, second and millisecond. /// /// The millisecond part can exceed 1,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Returns `None` on invalid hour, minute, second and/or millisecond. /// @@ -276,7 +280,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute, second and microsecond. /// /// The microsecond part can exceed 1,000,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or microsecond. /// @@ -299,7 +303,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute, second and microsecond. /// /// The microsecond part can exceed 1,000,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Returns `None` on invalid hour, minute, second and/or microsecond. /// @@ -327,7 +331,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute, second and nanosecond. /// /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or nanosecond. /// @@ -350,7 +354,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute, second and nanosecond. /// /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Returns `None` on invalid hour, minute, second and/or nanosecond. /// @@ -379,7 +383,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond. /// /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid number of seconds and/or nanosecond. /// @@ -402,7 +406,7 @@ impl NaiveTime { /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond. /// /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](./index.html#leap-second-handling). + /// in order to represent the [leap second](#leap-second-handling). /// /// Returns `None` on invalid number of seconds and/or nanosecond. /// @@ -451,7 +455,7 @@ impl NaiveTime { /// Ok(NaiveTime::from_hms(12, 34, 56))); /// ~~~~ /// - /// [Leap seconds](./index.html#leap-second-handling) are correctly handled by + /// [Leap seconds](#leap-second-handling) are correctly handled by /// treating any time of the form `hh:mm:60` as a leap second. /// (This equally applies to the formatting, so the round trip is possible.) /// @@ -609,7 +613,7 @@ impl NaiveTime { /// Returns a `Duration` within +/- 1 day. /// This does not overflow or underflow at all. /// - /// As a part of Chrono's [leap second handling](./index.html#leap-second-handling), + /// As a part of Chrono's [leap second handling](#leap-second-handling), /// the subtraction assumes that **there is no leap second ever**, /// except when any of the `NaiveTime`s themselves represents a leap second /// in which case the assumption becomes that @@ -814,7 +818,7 @@ impl Timelike for NaiveTime { /// ~~~~ /// /// This method never returns 60 even when it is a leap second. - /// ([Why?](./index.html#leap-second-handling)) + /// ([Why?](#leap-second-handling)) /// Use the proper [formatting method](#method.format) to get a human-readable representation. /// /// ~~~~ @@ -986,7 +990,7 @@ impl hash::Hash for NaiveTime { /// An addition of `Duration` to `NaiveTime` wraps around and never overflows or underflows. /// In particular the addition ignores integral number of days. /// -/// As a part of Chrono's [leap second handling](./index.html#leap-second-handling), +/// As a part of Chrono's [leap second handling](#leap-second-handling), /// the addition assumes that **there is no leap second ever**, /// except when the `NaiveTime` itself represents a leap second /// in which case the assumption becomes that **there is exactly a single leap second ever**. @@ -1054,7 +1058,7 @@ impl Add for NaiveTime { /// In particular the addition ignores integral number of days. /// It is same to the addition with a negated `Duration`. /// -/// As a part of Chrono's [leap second handling](./index.html#leap-second-handling), +/// As a part of Chrono's [leap second handling](#leap-second-handling), /// the addition assumes that **there is no leap second ever**, /// except when the `NaiveTime` itself represents a leap second /// in which case the assumption becomes that **there is exactly a single leap second ever**. From 72c1752068f576a4e6ffa84ad7e467e4d91f6813 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 22 Jun 2017 00:55:23 +0900 Subject: [PATCH 53/53] Much more complete explanation for the `chrono::format` module. Fixes #131, I hope! --- src/format/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 29ca87d..37bd6d5 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1,7 +1,19 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. -//! Formatting utilities for date and time. +//! Formatting (and parsing) utilities for date and time. +//! +//! This module provides the common types and routines to implement, +//! for example, [`DateTime::format`](../struct.DateTime.html#method.format) or +//! [`DateTime::parse_from_str`](../struct.DateTime.html#method.parse_from_str) methods. +//! For most cases you should use these high-level interfaces. +//! +//! Internally the formatting and parsing shares the same abstract **formatting items**, +//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of +//! the [`Item`](./enum.Item.html) type. +//! They are generated from more readable **format strings**; +//! currently Chrono supports [one built-in syntax closely resembling +//! C's `strftime` format](./strftime/index.html). use std::fmt; use std::str::FromStr;