From ed9ee01536e38edbc6fd86bbf5dc792c1de51543 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Wed, 3 Feb 2021 14:38:25 +0800 Subject: Import 'chrono' crate version 0.4.19 Change-Id: I362e8c6b674915ce7441df2d96de08d277eaadc7 --- src/date.rs | 480 +++++++++ src/datetime.rs | 2589 ++++++++++++++++++++++++++++++++++++++++++++++++ src/div.rs | 41 + src/format/locales.rs | 33 + src/format/mod.rs | 938 ++++++++++++++++++ src/format/parse.rs | 934 +++++++++++++++++ src/format/parsed.rs | 1283 ++++++++++++++++++++++++ src/format/scan.rs | 350 +++++++ src/format/strftime.rs | 649 ++++++++++++ src/lib.rs | 1535 ++++++++++++++++++++++++++++ src/naive/date.rs | 2392 ++++++++++++++++++++++++++++++++++++++++++++ src/naive/datetime.rs | 2507 ++++++++++++++++++++++++++++++++++++++++++++++ src/naive/internals.rs | 815 +++++++++++++++ src/naive/isoweek.rs | 163 +++ src/naive/time.rs | 1814 +++++++++++++++++++++++++++++++++ src/offset/fixed.rs | 244 +++++ src/offset/local.rs | 227 +++++ src/offset/mod.rs | 531 ++++++++++ src/offset/utc.rs | 100 ++ src/oldtime.rs | 684 +++++++++++++ src/round.rs | 456 +++++++++ src/sys.rs | 126 +++ src/sys/stub.rs | 80 ++ src/sys/unix.rs | 126 +++ src/sys/windows.rs | 131 +++ 25 files changed, 19228 insertions(+) create mode 100644 src/date.rs create mode 100644 src/datetime.rs create mode 100644 src/div.rs create mode 100644 src/format/locales.rs create mode 100644 src/format/mod.rs create mode 100644 src/format/parse.rs create mode 100644 src/format/parsed.rs create mode 100644 src/format/scan.rs create mode 100644 src/format/strftime.rs create mode 100644 src/lib.rs create mode 100644 src/naive/date.rs create mode 100644 src/naive/datetime.rs create mode 100644 src/naive/internals.rs create mode 100644 src/naive/isoweek.rs create mode 100644 src/naive/time.rs create mode 100644 src/offset/fixed.rs create mode 100644 src/offset/local.rs create mode 100644 src/offset/mod.rs create mode 100644 src/offset/utc.rs create mode 100644 src/oldtime.rs create mode 100644 src/round.rs create mode 100644 src/sys.rs create mode 100644 src/sys/stub.rs create mode 100644 src/sys/unix.rs create mode 100644 src/sys/windows.rs (limited to 'src') diff --git a/src/date.rs b/src/date.rs new file mode 100644 index 0000000..0012d36 --- /dev/null +++ b/src/date.rs @@ -0,0 +1,480 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 calendar date with time zone. + +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::ops::{Add, Sub}; +use core::{fmt, hash}; +use oldtime::Duration as OldDuration; + +#[cfg(feature = "unstable-locales")] +use format::Locale; +#[cfg(any(feature = "alloc", feature = "std", test))] +use format::{DelayedFormat, Item, StrftimeItems}; +use naive::{self, IsoWeek, NaiveDate, NaiveTime}; +use offset::{TimeZone, Utc}; +use DateTime; +use {Datelike, Weekday}; + +/// ISO 8601 calendar date with time zone. +/// +/// 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, +/// the corresponding local date should exist for at least a moment. +/// (It may still have a gap from the offset changes.) +/// +/// - The `TimeZone` is free to assign *any* `Offset` to the local date, +/// as long as that offset did occur in given day. +/// For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`, +/// it may produce either `2015-03-08-08:00` or `2015-03-08-07:00` +/// but *not* `2015-03-08+00:00` and others. +/// +/// - Once constructed as a full `DateTime`, +/// `DateTime::date` and other associated methods should return those for the original `Date`. +/// For example, if `dt = tz.ymd(y,m,d).hms(h,n,s)` were valid, `dt.date() == tz.ymd(y,m,d)`. +/// +/// - The date is timezone-agnostic up to one day (i.e. practically always), +/// so the local date and UTC date should be equal for most cases +/// even though the raw calculation between `NaiveDate` and `Duration` may not. +#[derive(Clone)] +pub struct Date { + date: NaiveDate, + offset: Tz::Offset, +} + +/// The minimum possible `Date`. +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 }; + +impl Date { + /// Makes a new `Date` with given *UTC* date and offset. + /// The local date should be constructed via the `TimeZone` trait. + // + // note: this constructor is purposely not named to `new` to discourage the direct usage. + #[inline] + pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date { + Date { date: date, offset: offset } + } + + /// Makes a new `DateTime` from the current date and given `NaiveTime`. + /// The offset in the current date is preserved. + /// + /// Panics on invalid datetime. + #[inline] + pub fn and_time(&self, time: NaiveTime) -> Option> { + let localdt = self.naive_local().and_time(time); + self.timezone().from_local_datetime(&localdt).single() + } + + /// Makes a new `DateTime` from the current date, hour, minute and second. + /// The offset in the current date is preserved. + /// + /// Panics on invalid hour, minute and/or second. + #[inline] + pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime { + self.and_hms_opt(hour, min, sec).expect("invalid time") + } + + /// Makes a new `DateTime` from the current date, hour, minute and second. + /// The offset in the current date is preserved. + /// + /// Returns `None` on invalid hour, minute and/or second. + #[inline] + pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option> { + NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time)) + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. + /// The millisecond part can exceed 1,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Panics on invalid hour, minute, second and/or millisecond. + #[inline] + pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime { + self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time") + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. + /// The millisecond part can exceed 1,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Returns `None` on invalid hour, minute, second and/or millisecond. + #[inline] + pub fn and_hms_milli_opt( + &self, + hour: u32, + min: u32, + sec: u32, + milli: u32, + ) -> Option> { + NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time)) + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. + /// The microsecond part can exceed 1,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Panics on invalid hour, minute, second and/or microsecond. + #[inline] + pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime { + self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time") + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. + /// The microsecond part can exceed 1,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Returns `None` on invalid hour, minute, second and/or microsecond. + #[inline] + pub fn and_hms_micro_opt( + &self, + hour: u32, + min: u32, + sec: u32, + micro: u32, + ) -> Option> { + NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time)) + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. + /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Panics on invalid hour, minute, second and/or nanosecond. + #[inline] + pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime { + self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time") + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. + /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Returns `None` on invalid hour, minute, second and/or nanosecond. + #[inline] + pub fn and_hms_nano_opt( + &self, + hour: u32, + min: u32, + sec: u32, + nano: u32, + ) -> Option> { + NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time)) + } + + /// Makes a new `Date` for the next date. + /// + /// Panics when `self` is the last representable date. + #[inline] + pub fn succ(&self) -> Date { + self.succ_opt().expect("out of bound") + } + + /// Makes a new `Date` for the next date. + /// + /// Returns `None` when `self` is the last representable date. + #[inline] + pub fn succ_opt(&self) -> Option> { + self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone())) + } + + /// Makes a new `Date` for the prior date. + /// + /// Panics when `self` is the first representable date. + #[inline] + pub fn pred(&self) -> Date { + self.pred_opt().expect("out of bound") + } + + /// Makes a new `Date` for the prior date. + /// + /// Returns `None` when `self` is the first representable date. + #[inline] + pub fn pred_opt(&self) -> Option> { + self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone())) + } + + /// Retrieves an associated offset from UTC. + #[inline] + pub fn offset(&self) -> &Tz::Offset { + &self.offset + } + + /// Retrieves an associated time zone. + #[inline] + pub fn timezone(&self) -> Tz { + TimeZone::from_offset(&self.offset) + } + + /// Changes the associated time zone. + /// This does not change the actual `Date` (but will change the string representation). + #[inline] + pub fn with_timezone(&self, tz: &Tz2) -> Date { + tz.from_utc_date(&self.date) + } + + /// Adds given `Duration` to the current date. + /// + /// Returns `None` when it will result in overflow. + #[inline] + 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 }) + } + + /// Subtracts given `Duration` from the current date. + /// + /// Returns `None` when it will result in overflow. + #[inline] + 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) + } + + /// Returns a view to the naive UTC date. + #[inline] + pub fn naive_utc(&self) -> NaiveDate { + self.date + } + + /// Returns a view to the naive local date. + /// + /// This is technically the same as [`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 + } +} + +/// Maps the local date to other date with given conversion function. +fn map_local(d: &Date, mut f: F) -> Option> +where + F: FnMut(NaiveDate) -> Option, +{ + f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single()) +} + +impl Date +where + Tz::Offset: fmt::Display, +{ + /// Formats the date with the specified formatting items. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items) + } + + /// Formats the date with the specified format string. + /// See the [`format::strftime` module](./format/strftime/index.html) + /// on the supported escape sequences. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { + self.format_with_items(StrftimeItems::new(fmt)) + } + + /// Formats the date with the specified formatting items and locale. + #[cfg(feature = "unstable-locales")] + #[inline] + pub fn format_localized_with_items<'a, I, B>( + &self, + items: I, + locale: Locale, + ) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + DelayedFormat::new_with_offset_and_locale( + Some(self.naive_local()), + None, + &self.offset, + items, + locale, + ) + } + + /// Formats the date with the specified format string and locale. + /// See the [`format::strftime` module](./format/strftime/index.html) + /// on the supported escape sequences. + #[cfg(feature = "unstable-locales")] + #[inline] + pub fn format_localized<'a>( + &self, + fmt: &'a str, + locale: Locale, + ) -> DelayedFormat> { + self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) + } +} + +impl Datelike for Date { + #[inline] + fn year(&self) -> i32 { + self.naive_local().year() + } + #[inline] + fn month(&self) -> u32 { + self.naive_local().month() + } + #[inline] + fn month0(&self) -> u32 { + self.naive_local().month0() + } + #[inline] + fn day(&self) -> u32 { + self.naive_local().day() + } + #[inline] + fn day0(&self) -> u32 { + self.naive_local().day0() + } + #[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 iso_week(&self) -> IsoWeek { + self.naive_local().iso_week() + } + + #[inline] + fn with_year(&self, year: i32) -> Option> { + map_local(self, |date| date.with_year(year)) + } + + #[inline] + fn with_month(&self, month: u32) -> Option> { + map_local(self, |date| date.with_month(month)) + } + + #[inline] + fn with_month0(&self, month0: u32) -> Option> { + map_local(self, |date| date.with_month0(month0)) + } + + #[inline] + fn with_day(&self, day: u32) -> Option> { + map_local(self, |date| date.with_day(day)) + } + + #[inline] + fn with_day0(&self, day0: u32) -> Option> { + map_local(self, |date| date.with_day0(day0)) + } + + #[inline] + fn with_ordinal(&self, ordinal: u32) -> Option> { + map_local(self, |date| date.with_ordinal(ordinal)) + } + + #[inline] + fn with_ordinal0(&self, ordinal0: u32) -> Option> { + map_local(self, |date| date.with_ordinal0(ordinal0)) + } +} + +// we need them as automatic impls cannot handle associated types +impl Copy for Date where ::Offset: Copy {} +unsafe impl Send for Date where ::Offset: Send {} + +impl PartialEq> for Date { + fn eq(&self, other: &Date) -> bool { + self.date == other.date + } +} + +impl Eq for Date {} + +impl PartialOrd for Date { + fn partial_cmp(&self, other: &Date) -> Option { + self.date.partial_cmp(&other.date) + } +} + +impl Ord for Date { + fn cmp(&self, other: &Date) -> Ordering { + self.date.cmp(&other.date) + } +} + +impl hash::Hash for Date { + fn hash(&self, state: &mut H) { + self.date.hash(state) + } +} + +impl Add for Date { + type Output = Date; + + #[inline] + fn add(self, rhs: OldDuration) -> Date { + self.checked_add_signed(rhs).expect("`Date + Duration` overflowed") + } +} + +impl Sub for Date { + type Output = Date; + + #[inline] + fn sub(self, rhs: OldDuration) -> Date { + self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed") + } +} + +impl Sub> for Date { + type Output = OldDuration; + + #[inline] + fn sub(self, rhs: Date) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +impl fmt::Debug for Date { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}{:?}", self.naive_local(), self.offset) + } +} + +impl fmt::Display for Date +where + Tz::Offset: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}{}", self.naive_local(), self.offset) + } +} diff --git a/src/datetime.rs b/src/datetime.rs new file mode 100644 index 0000000..ecd4642 --- /dev/null +++ b/src/datetime.rs @@ -0,0 +1,2589 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 date and time with time zone. + +use core::cmp::Ordering; +use core::ops::{Add, Sub}; +use core::{fmt, hash, str}; +use oldtime::Duration as OldDuration; +#[cfg(any(feature = "std", test))] +use std::time::{SystemTime, UNIX_EPOCH}; + +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::string::{String, ToString}; +#[cfg(feature = "std")] +use std::string::ToString; + +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +#[cfg(any(feature = "alloc", feature = "std", test))] +use format::DelayedFormat; +#[cfg(feature = "unstable-locales")] +use format::Locale; +use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use format::{Fixed, Item}; +use naive::{self, IsoWeek, NaiveDateTime, NaiveTime}; +#[cfg(feature = "clock")] +use offset::Local; +use offset::{FixedOffset, Offset, TimeZone, Utc}; +use Date; +use {Datelike, Timelike, Weekday}; + +/// Specific formatting options for seconds. This may be extended in the +/// future, so exhaustive matching in external code is not recommended. +/// +/// See the `TimeZone::to_rfc3339_opts` function for usage. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SecondsFormat { + /// Format whole seconds only, with no decimal point nor subseconds. + Secs, + + /// Use fixed 3 subsecond digits. This corresponds to + /// [Fixed::Nanosecond3](format/enum.Fixed.html#variant.Nanosecond3). + Millis, + + /// Use fixed 6 subsecond digits. This corresponds to + /// [Fixed::Nanosecond6](format/enum.Fixed.html#variant.Nanosecond6). + Micros, + + /// Use fixed 9 subsecond digits. This corresponds to + /// [Fixed::Nanosecond9](format/enum.Fixed.html#variant.Nanosecond9). + Nanos, + + /// Automatically select one of `Secs`, `Millis`, `Micros`, or `Nanos` to + /// display all available non-zero sub-second digits. This corresponds to + /// [Fixed::Nanosecond](format/enum.Fixed.html#variant.Nanosecond). + AutoSi, + + // Do not match against this. + #[doc(hidden)] + __NonExhaustive, +} + +/// 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`](./offset/trait.TimeZone.html) implementations. +#[derive(Clone)] +pub struct DateTime { + datetime: NaiveDateTime, + offset: Tz::Offset, +} + +/// The minimum possible `DateTime`. +pub const MIN_DATETIME: DateTime = DateTime { datetime: naive::MIN_DATETIME, offset: Utc }; +/// The maximum possible `DateTime`. +pub const MAX_DATETIME: DateTime = DateTime { datetime: naive::MAX_DATETIME, offset: Utc }; + +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 purposely not named to `new` to discourage the direct usage. + #[inline] + pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime { + DateTime { datetime: datetime, offset: offset } + } + + /// Retrieves a date component. + #[inline] + pub fn date(&self) -> Date { + Date::from_utc(self.naive_local().date(), self.offset.clone()) + } + + /// Retrieves a time component. + /// Unlike `date`, this is not associated to the time zone. + #[inline] + pub fn time(&self) -> NaiveTime { + self.datetime.time() + self.offset.fix() + } + + /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC + /// (aka "UNIX timestamp"). + #[inline] + pub fn timestamp(&self) -> i64 { + self.datetime.timestamp() + } + + /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC + /// + /// Note that this does reduce the number of years that can be represented + /// from ~584 Billion to ~584 Million. (If this is a problem, please file + /// an issue to let me know what domain needs millisecond precision over + /// billions of years, I'm curious.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::Utc; + /// use chrono::TimeZone; + /// + /// let dt = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 444); + /// assert_eq!(dt.timestamp_millis(), 1_444); + /// + /// let dt = Utc.ymd(2001, 9, 9).and_hms_milli(1, 46, 40, 555); + /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); + /// ~~~~ + #[inline] + pub fn timestamp_millis(&self) -> i64 { + self.datetime.timestamp_millis() + } + + /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC + /// + /// Note that this does reduce the number of years that can be represented + /// from ~584 Billion to ~584. (If this is a problem, please file + /// an issue to let me know what domain needs nanosecond precision over + /// millennia, I'm curious.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::Utc; + /// use chrono::TimeZone; + /// + /// let dt = Utc.ymd(1970, 1, 1).and_hms_nano(0, 0, 1, 444); + /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); + /// + /// let dt = Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 40, 555); + /// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555); + /// ~~~~ + #[inline] + pub fn timestamp_nanos(&self) -> i64 { + self.datetime.timestamp_nanos() + } + + /// Returns the number of milliseconds since the last second boundary + /// + /// warning: in event of a leap second, this may exceed 999 + /// + /// note: this is not the number of milliseconds since January 1, 1970 0:00:00 UTC + #[inline] + pub fn timestamp_subsec_millis(&self) -> u32 { + self.datetime.timestamp_subsec_millis() + } + + /// Returns the number of microseconds since the last second boundary + /// + /// warning: in event of a leap second, this may exceed 999_999 + /// + /// note: this is not the number of microseconds since January 1, 1970 0:00:00 UTC + #[inline] + pub fn timestamp_subsec_micros(&self) -> u32 { + self.datetime.timestamp_subsec_micros() + } + + /// Returns the number of nanoseconds since the last second boundary + /// + /// warning: in event of a leap second, this may exceed 999_999_999 + /// + /// note: this is not the number of nanoseconds since January 1, 1970 0:00:00 UTC + #[inline] + pub fn timestamp_subsec_nanos(&self) -> u32 { + self.datetime.timestamp_subsec_nanos() + } + + /// Retrieves an associated offset from UTC. + #[inline] + pub fn offset(&self) -> &Tz::Offset { + &self.offset + } + + /// Retrieves an associated time zone. + #[inline] + pub fn timezone(&self) -> Tz { + TimeZone::from_offset(&self.offset) + } + + /// Changes the associated time zone. + /// This does not change the actual `DateTime` (but will change the string representation). + #[inline] + pub fn with_timezone(&self, tz: &Tz2) -> DateTime { + tz.from_utc_datetime(&self.datetime) + } + + /// Adds given `Duration` to the current date and time. + /// + /// Returns `None` when it will result in overflow. + #[inline] + pub fn checked_add_signed(self, rhs: OldDuration) -> Option> { + let datetime = try_opt!(self.datetime.checked_add_signed(rhs)); + let tz = self.timezone(); + Some(tz.from_utc_datetime(&datetime)) + } + + /// Subtracts given `Duration` from the current date and time. + /// + /// Returns `None` when it will result in overflow. + #[inline] + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option> { + let datetime = try_opt!(self.datetime.checked_sub_signed(rhs)); + let tz = self.timezone(); + Some(tz.from_utc_datetime(&datetime)) + } + + /// 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 { + self.datetime + } + + /// Returns a view to the naive local datetime. + #[inline] + pub fn naive_local(&self) -> NaiveDateTime { + self.datetime + self.offset.fix() + } +} + +/// Convert a `DateTime` instance into a `DateTime` instance. +impl From> for DateTime { + /// Convert this `DateTime` instance into a `DateTime` instance. + /// + /// Conversion is done via [`DateTime::with_timezone`]. Note that the converted value returned by + /// this will be created with a fixed timezone offset of 0. + fn from(src: DateTime) -> Self { + src.with_timezone(&FixedOffset::east(0)) + } +} + +/// Convert a `DateTime` instance into a `DateTime` instance. +#[cfg(feature = "clock")] +impl From> for DateTime { + /// Convert this `DateTime` instance into a `DateTime` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in timezones. + fn from(src: DateTime) -> Self { + src.with_timezone(&Local) + } +} + +/// Convert a `DateTime` instance into a `DateTime` instance. +impl From> for DateTime { + /// Convert this `DateTime` instance into a `DateTime` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`], accounting for the timezone + /// difference. + fn from(src: DateTime) -> Self { + src.with_timezone(&Utc) + } +} + +/// Convert a `DateTime` instance into a `DateTime` instance. +#[cfg(feature = "clock")] +impl From> for DateTime { + /// Convert this `DateTime` instance into a `DateTime` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`]. Returns the equivalent value in local + /// time. + fn from(src: DateTime) -> Self { + src.with_timezone(&Local) + } +} + +/// Convert a `DateTime` instance into a `DateTime` instance. +#[cfg(feature = "clock")] +impl From> for DateTime { + /// Convert this `DateTime` instance into a `DateTime` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in + /// timezones. + fn from(src: DateTime) -> Self { + src.with_timezone(&Utc) + } +} + +/// Convert a `DateTime` instance into a `DateTime` instance. +#[cfg(feature = "clock")] +impl From> for DateTime { + /// Convert this `DateTime` instance into a `DateTime` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`]. Note that the converted value returned + /// by this will be created with a fixed timezone offset of 0. + fn from(src: DateTime) -> Self { + src.with_timezone(&FixedOffset::east(0)) + } +} + +/// Maps the local datetime to other datetime with given conversion function. +fn map_local(dt: &DateTime, mut f: F) -> Option> +where + F: FnMut(NaiveDateTime) -> Option, +{ + f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single()) +} + +impl DateTime { + /// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`, + /// then returns a new `DateTime` with a parsed `FixedOffset`. + /// + /// RFC 2822 is the internet message standard that specifices the + /// representation of times in HTTP and email headers. + /// + /// ``` + /// # use chrono::{DateTime, FixedOffset, TimeZone}; + /// assert_eq!( + /// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), + /// FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9) + /// ); + /// ``` + pub fn parse_from_rfc2822(s: &str) -> ParseResult> { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_datetime() + } + + /// Parses an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`, + /// then returns a new `DateTime` with a parsed `FixedOffset`. + /// + /// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows some freedom + /// over the syntax and RFC 3339 exercises that freedom to rigidly define a fixed format. + pub fn parse_from_rfc3339(s: &str) -> ParseResult> { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_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) + /// on the supported escape sequences. + /// + /// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone. + /// + /// Note that this method *requires a timezone* in the string. See + /// [`NaiveDateTime::parse_from_str`](./naive/struct.NaiveDateTime.html#method.parse_from_str) + /// for a version that does not require a timezone in the to-be-parsed str. + /// + /// # Example + /// + /// ```rust + /// use chrono::{DateTime, FixedOffset, TimeZone}; + /// + /// let dt = DateTime::parse_from_str( + /// "1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z"); + /// assert_eq!(dt, Ok(FixedOffset::east(0).ymd(1983, 4, 13).and_hms_milli(12, 9, 14, 274))); + /// ``` + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult> { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_datetime() + } +} + +impl DateTime +where + Tz::Offset: fmt::Display, +{ + /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. + #[cfg(any(feature = "alloc", feature = "std", test))] + pub fn to_rfc2822(&self) -> String { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; + self.format_with_items(ITEMS.iter()).to_string() + } + + /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. + #[cfg(any(feature = "alloc", feature = "std", test))] + pub fn to_rfc3339(&self) -> String { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; + self.format_with_items(ITEMS.iter()).to_string() + } + + /// Return an RFC 3339 and ISO 8601 date and time string with subseconds + /// formatted as per a `SecondsFormat`. If passed `use_z` true and the + /// timezone is UTC (offset 0), use 'Z', as per + /// [Fixed::TimezoneOffsetColonZ](format/enum.Fixed.html#variant.TimezoneOffsetColonZ). + /// If passed `use_z` false, use + /// [Fixed::TimezoneOffsetColon](format/enum.Fixed.html#variant.TimezoneOffsetColon). + /// + /// # Examples + /// + /// ```rust + /// # use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone, Utc}; + /// let dt = Utc.ymd(2018, 1, 26).and_hms_micro(18, 30, 9, 453_829); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false), + /// "2018-01-26T18:30:09.453+00:00"); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), + /// "2018-01-26T18:30:09.453Z"); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), + /// "2018-01-26T18:30:09Z"); + /// + /// let pst = FixedOffset::east(8 * 60 * 60); + /// let dt = pst.ymd(2018, 1, 26).and_hms_micro(10, 30, 9, 453_829); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), + /// "2018-01-26T10:30:09+08:00"); + /// ``` + #[cfg(any(feature = "alloc", feature = "std", test))] + pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String { + use format::Numeric::*; + use format::Pad::Zero; + use SecondsFormat::*; + + debug_assert!(secform != __NonExhaustive, "Do not use __NonExhaustive!"); + + const PREFIX: &'static [Item<'static>] = &[ + Item::Numeric(Year, Zero), + Item::Literal("-"), + Item::Numeric(Month, Zero), + Item::Literal("-"), + Item::Numeric(Day, Zero), + Item::Literal("T"), + Item::Numeric(Hour, Zero), + Item::Literal(":"), + Item::Numeric(Minute, Zero), + Item::Literal(":"), + Item::Numeric(Second, Zero), + ]; + + let ssitem = match secform { + Secs => None, + Millis => Some(Item::Fixed(Fixed::Nanosecond3)), + Micros => Some(Item::Fixed(Fixed::Nanosecond6)), + Nanos => Some(Item::Fixed(Fixed::Nanosecond9)), + AutoSi => Some(Item::Fixed(Fixed::Nanosecond)), + __NonExhaustive => unreachable!(), + }; + + let tzitem = Item::Fixed(if use_z { + Fixed::TimezoneOffsetColonZ + } else { + Fixed::TimezoneOffsetColon + }); + + match ssitem { + None => self.format_with_items(PREFIX.iter().chain([tzitem].iter())).to_string(), + Some(s) => self.format_with_items(PREFIX.iter().chain([s, tzitem].iter())).to_string(), + } + } + + /// Formats the combined date and time with the specified formatting items. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + let local = self.naive_local(); + DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items) + } + + /// Formats the combined date and time with the specified format string. + /// See the [`format::strftime` module](./format/strftime/index.html) + /// on the supported escape sequences. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { + self.format_with_items(StrftimeItems::new(fmt)) + } + + /// Formats the combined date and time with the specified formatting items and locale. + #[cfg(feature = "unstable-locales")] + #[inline] + pub fn format_localized_with_items<'a, I, B>( + &self, + items: I, + locale: Locale, + ) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + let local = self.naive_local(); + DelayedFormat::new_with_offset_and_locale( + Some(local.date()), + Some(local.time()), + &self.offset, + items, + locale, + ) + } + + /// Formats the combined date and time with the specified format string and locale. + /// See the [`format::strftime` module](./format/strftime/index.html) + /// on the supported escape sequences. + #[cfg(feature = "unstable-locales")] + #[inline] + pub fn format_localized<'a>( + &self, + fmt: &'a str, + locale: Locale, + ) -> DelayedFormat> { + self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) + } +} + +impl Datelike for DateTime { + #[inline] + fn year(&self) -> i32 { + self.naive_local().year() + } + #[inline] + fn month(&self) -> u32 { + self.naive_local().month() + } + #[inline] + fn month0(&self) -> u32 { + self.naive_local().month0() + } + #[inline] + fn day(&self) -> u32 { + self.naive_local().day() + } + #[inline] + fn day0(&self) -> u32 { + self.naive_local().day0() + } + #[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 iso_week(&self) -> IsoWeek { + self.naive_local().iso_week() + } + + #[inline] + fn with_year(&self, year: i32) -> Option> { + map_local(self, |datetime| datetime.with_year(year)) + } + + #[inline] + fn with_month(&self, month: u32) -> Option> { + map_local(self, |datetime| datetime.with_month(month)) + } + + #[inline] + fn with_month0(&self, month0: u32) -> Option> { + map_local(self, |datetime| datetime.with_month0(month0)) + } + + #[inline] + fn with_day(&self, day: u32) -> Option> { + map_local(self, |datetime| datetime.with_day(day)) + } + + #[inline] + fn with_day0(&self, day0: u32) -> Option> { + map_local(self, |datetime| datetime.with_day0(day0)) + } + + #[inline] + fn with_ordinal(&self, ordinal: u32) -> Option> { + map_local(self, |datetime| datetime.with_ordinal(ordinal)) + } + + #[inline] + fn with_ordinal0(&self, ordinal0: u32) -> Option> { + map_local(self, |datetime| datetime.with_ordinal0(ordinal0)) + } +} + +impl Timelike for DateTime { + #[inline] + fn hour(&self) -> u32 { + self.naive_local().hour() + } + #[inline] + fn minute(&self) -> u32 { + self.naive_local().minute() + } + #[inline] + fn second(&self) -> u32 { + self.naive_local().second() + } + #[inline] + fn nanosecond(&self) -> u32 { + self.naive_local().nanosecond() + } + + #[inline] + fn with_hour(&self, hour: u32) -> Option> { + map_local(self, |datetime| datetime.with_hour(hour)) + } + + #[inline] + fn with_minute(&self, min: u32) -> Option> { + map_local(self, |datetime| datetime.with_minute(min)) + } + + #[inline] + fn with_second(&self, sec: u32) -> Option> { + map_local(self, |datetime| datetime.with_second(sec)) + } + + #[inline] + fn with_nanosecond(&self, nano: u32) -> Option> { + map_local(self, |datetime| datetime.with_nanosecond(nano)) + } +} + +// we need them as automatic impls cannot handle associated types +impl Copy for DateTime where ::Offset: Copy {} +unsafe impl Send for DateTime where ::Offset: Send {} + +impl PartialEq> for DateTime { + fn eq(&self, other: &DateTime) -> bool { + self.datetime == other.datetime + } +} + +impl Eq for DateTime {} + +impl PartialOrd> for DateTime { + /// Compare two DateTimes based on their true time, ignoring time zones + /// + /// # Example + /// + /// ``` + /// use chrono::prelude::*; + /// + /// let earlier = Utc.ymd(2015, 5, 15).and_hms(2, 0, 0).with_timezone(&FixedOffset::west(1 * 3600)); + /// let later = Utc.ymd(2015, 5, 15).and_hms(3, 0, 0).with_timezone(&FixedOffset::west(5 * 3600)); + /// + /// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00"); + /// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00"); + /// + /// assert!(later > earlier); + /// ``` + fn partial_cmp(&self, other: &DateTime) -> Option { + self.datetime.partial_cmp(&other.datetime) + } +} + +impl Ord for DateTime { + fn cmp(&self, other: &DateTime) -> Ordering { + self.datetime.cmp(&other.datetime) + } +} + +impl hash::Hash for DateTime { + fn hash(&self, state: &mut H) { + self.datetime.hash(state) + } +} + +impl Add for DateTime { + type Output = DateTime; + + #[inline] + fn add(self, rhs: OldDuration) -> DateTime { + self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed") + } +} + +impl Sub for DateTime { + type Output = DateTime; + + #[inline] + fn sub(self, rhs: OldDuration) -> DateTime { + self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed") + } +} + +impl Sub> for DateTime { + type Output = OldDuration; + + #[inline] + fn sub(self, rhs: DateTime) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +impl fmt::Debug for DateTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}{:?}", self.naive_local(), self.offset) + } +} + +impl fmt::Display for DateTime +where + Tz::Offset: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.naive_local(), self.offset) + } +} + +impl str::FromStr for DateTime { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult> { + s.parse::>().map(|dt| dt.with_timezone(&Utc)) + } +} + +#[cfg(feature = "clock")] +impl str::FromStr for DateTime { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult> { + s.parse::>().map(|dt| dt.with_timezone(&Local)) + } +} + +#[cfg(any(feature = "std", test))] +impl From for DateTime { + fn from(t: SystemTime) -> DateTime { + let (sec, nsec) = match t.duration_since(UNIX_EPOCH) { + Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()), + Err(e) => { + // unlikely but should be handled + let dur = e.duration(); + let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos()); + if nsec == 0 { + (-sec, 0) + } else { + (-sec - 1, 1_000_000_000 - nsec) + } + } + }; + Utc.timestamp(sec, nsec) + } +} + +#[cfg(feature = "clock")] +impl From for DateTime { + fn from(t: SystemTime) -> DateTime { + DateTime::::from(t).with_timezone(&Local) + } +} + +#[cfg(any(feature = "std", test))] +impl From> for SystemTime { + fn from(dt: DateTime) -> SystemTime { + use std::time::Duration; + + let sec = dt.timestamp(); + let nsec = dt.timestamp_subsec_nanos(); + if sec < 0 { + // unlikely but should be handled + UNIX_EPOCH - Duration::new(-sec as u64, 0) + Duration::new(0, nsec) + } else { + UNIX_EPOCH + Duration::new(sec as u64, nsec) + } + } +} + +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] +impl From for DateTime { + fn from(date: js_sys::Date) -> DateTime { + DateTime::::from(&date) + } +} + +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] +impl From<&js_sys::Date> for DateTime { + fn from(date: &js_sys::Date) -> DateTime { + let millisecs_since_unix_epoch: u64 = date.get_time() as u64; + let secs = millisecs_since_unix_epoch / 1000; + let nanos = 1_000_000 * (millisecs_since_unix_epoch % 1000); + let naive = NaiveDateTime::from_timestamp(secs as i64, nanos as u32); + DateTime::from_utc(naive, Utc) + } +} + +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] +impl From> for js_sys::Date { + fn from(date: DateTime) -> js_sys::Date { + let js_date = js_sys::Date::new_0(); + + js_date.set_utc_full_year_with_month_date( + date.year() as u32, + date.month0() as i32, + date.day() as i32, + ); + + js_date.set_utc_hours(date.hour()); + js_date.set_utc_minutes(date.minute()); + js_date.set_utc_seconds(date.second()); + + js_date + } +} + +#[test] +fn test_auto_conversion() { + let utc_dt = Utc.ymd(2018, 9, 5).and_hms(23, 58, 0); + let cdt_dt = FixedOffset::west(5 * 60 * 60).ymd(2018, 9, 5).and_hms(18, 58, 0); + let utc_dt2: DateTime = cdt_dt.into(); + assert_eq!(utc_dt, utc_dt2); +} + +#[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: ::core::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, feature = "clock", 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: ::core::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""#).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""#).expect("local should parse with offset"), + 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 = "clock", 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: ::core::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")] +pub mod rustc_serialize { + use super::DateTime; + use core::fmt; + use core::ops::Deref; + #[cfg(feature = "clock")] + use offset::Local; + use offset::{FixedOffset, LocalResult, TimeZone, Utc}; + use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + + impl Encodable for DateTime { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } + } + + // lik? 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> { + d.read_str()? + .parse::>() + .map_err(|_| d.error("invalid date and time")) + } + } + + #[allow(deprecated)] + impl Decodable for TsSeconds { + #[allow(deprecated)] + fn decode(d: &mut D) -> Result, D::Error> { + from(FixedOffset::east(0).timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) + } + } + + impl Decodable for DateTime { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_str()? + .parse::>() + .map(|dt| dt.with_timezone(&Utc)) + .map_err(|_| d.error("invalid date and time")) + } + } + + /// A `DateTime` that can be deserialized from a timestamp + /// + /// A timestamp here is seconds since the epoch + #[derive(Debug)] + pub struct TsSeconds(DateTime); + + #[allow(deprecated)] + impl From> for DateTime { + /// Pull the inner DateTime out + #[allow(deprecated)] + fn from(obj: TsSeconds) -> DateTime { + obj.0 + } + } + + #[allow(deprecated)] + impl Deref for TsSeconds { + type Target = DateTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + #[allow(deprecated)] + impl Decodable for TsSeconds { + fn decode(d: &mut D) -> Result, D::Error> { + from(Utc.timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) + } + } + + #[cfg(feature = "clock")] + 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(feature = "clock")] + #[allow(deprecated)] + impl Decodable for TsSeconds { + #[allow(deprecated)] + 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] + fn test_encodable() { + super::test_encodable_json(json::encode, json::encode); + } + + #[cfg(feature = "clock")] + #[test] + fn test_decodable() { + super::test_decodable_json(json::decode, json::decode, json::decode); + } + + #[cfg(feature = "clock")] + #[test] + fn test_decodable_timestamps() { + super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); + } +} + +/// documented at re-export site +#[cfg(feature = "serde")] +pub mod serde { + use super::DateTime; + use core::fmt; + #[cfg(feature = "clock")] + use offset::Local; + use offset::{FixedOffset, LocalResult, TimeZone, Utc}; + use serdelib::{de, ser}; + use {ne_timestamp, SerdeError}; + + #[doc(hidden)] + #[derive(Debug)] + pub struct SecondsTimestampVisitor; + + #[doc(hidden)] + #[derive(Debug)] + pub struct NanoSecondsTimestampVisitor; + + #[doc(hidden)] + #[derive(Debug)] + pub struct MilliSecondsTimestampVisitor; + + // lik? function to convert a LocalResult into a serde-ish Result + fn serde_from(me: LocalResult, ts: &V) -> Result + where + E: de::Error, + V: fmt::Display, + T: fmt::Display, + { + match me { + LocalResult::None => Err(E::custom(ne_timestamp(ts))), + LocalResult::Ambiguous(min, max) => { + Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min: min, max: max })) + } + LocalResult::Single(val) => Ok(val), + } + } + + /// Ser/de to/from timestamps in nanoseconds + /// + /// 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::serde::ts_nanoseconds; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_nanoseconds")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let time = Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// 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_nanoseconds { + use core::fmt; + use serdelib::{de, ser}; + + use offset::TimeZone; + use {DateTime, Utc}; + + use super::{serde_from, NanoSecondsTimestampVisitor}; + + /// Serialize a UTC datetime into an integer number of nanoseconds 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::serde::ts_nanoseconds::serialize as to_nano_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_ts")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(dt: &DateTime, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_nanos()) + } + + /// Deserialize a `DateTime` from a nanosecond 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::serde::ts_nanoseconds::deserialize as from_nano_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_ts")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + Ok(d.deserialize_i64(NanoSecondsTimestampVisitor)?) + } + + impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a unix timestamp in nanoseconds") + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_i64(self, value: i64) -> Result, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt(value / 1_000_000_000, (value % 1_000_000_000) as u32), + &value, + ) + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_u64(self, value: u64) -> Result, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt( + (value / 1_000_000_000) as i64, + (value % 1_000_000_000) as u32, + ), + &value, + ) + } + } + } + + /// Ser/de to/from optional timestamps in nanoseconds + /// + /// 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::serde::ts_nanoseconds_option; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_nanoseconds_option")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let time = Some(Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733)); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// 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_nanoseconds_option { + use core::fmt; + use serdelib::{de, ser}; + + use {DateTime, Utc}; + + use super::NanoSecondsTimestampVisitor; + + /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch or none + /// + /// 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::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_tsopt")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: Some(Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733)), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(opt: &Option>, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a nanosecond timestamp or none + /// + /// 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::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_tsopt")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> + where + D: de::Deserializer<'de>, + { + Ok(d.deserialize_option(OptionNanoSecondsTimestampVisitor)?) + } + + struct OptionNanoSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { + type Value = Option>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in nanoseconds or none") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_some(self, d: D) -> Result>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_none(self) -> Result>, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_unit(self) -> Result>, E> + where + E: de::Error, + { + Ok(None) + } + } + } + + /// Ser/de to/from timestamps in milliseconds + /// + /// 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::serde::ts_milliseconds; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_milliseconds")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let time = Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// 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_milliseconds { + use core::fmt; + use serdelib::{de, ser}; + + use offset::TimeZone; + use {DateTime, Utc}; + + use super::{serde_from, MilliSecondsTimestampVisitor}; + + /// Serialize a UTC datetime into an integer number of milliseconds 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::serde::ts_milliseconds::serialize as to_milli_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_ts")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(dt: &DateTime, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_millis()) + } + + /// Deserialize a `DateTime` from a millisecond 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::serde::ts_milliseconds::deserialize as from_milli_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_ts")] + /// time: DateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + Ok(d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))?) + } + + impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in milliseconds") + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_i64(self, value: i64) -> Result, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt(value / 1000, ((value % 1000) * 1_000_000) as u32), + &value, + ) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_u64(self, value: u64) -> Result, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32), + &value, + ) + } + } + } + + /// Ser/de to/from optional timestamps in milliseconds + /// + /// 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::serde::ts_milliseconds_option; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_milliseconds_option")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let time = Some(Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918)); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// 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_milliseconds_option { + use core::fmt; + use serdelib::{de, ser}; + + use {DateTime, Utc}; + + use super::MilliSecondsTimestampVisitor; + + /// Serialize a UTC datetime into an integer number of milliseconds since the epoch or none + /// + /// 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::serde::ts_milliseconds_option::serialize as to_milli_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_tsopt")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: Some(Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918)), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(opt: &Option>, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a millisecond timestamp or none + /// + /// 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::prelude::*; + /// use chrono::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; + /// + /// #[derive(Deserialize, PartialEq, Debug)] + /// #[serde(untagged)] + /// enum E { + /// V(T), + /// } + /// + /// #[derive(Deserialize, PartialEq, Debug)] + /// struct S { + /// #[serde(default, deserialize_with = "from_milli_tsopt")] + /// time: Option> + /// } + /// + /// # fn example() -> Result<(), serde_json::Error> { + /// let my_s: E = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; + /// assert_eq!(my_s, E::V(S { time: Some(Utc.timestamp(1526522699, 918000000)) })); + /// let s: E = serde_json::from_str(r#"{ "time": null }"#)?; + /// assert_eq!(s, E::V(S { time: None })); + /// let t: E = serde_json::from_str(r#"{}"#)?; + /// assert_eq!(t, E::V(S { time: None })); + /// # Ok(()) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> + where + D: de::Deserializer<'de>, + { + Ok(d.deserialize_option(OptionMilliSecondsTimestampVisitor) + .map(|opt| opt.map(|dt| dt.with_timezone(&Utc)))?) + } + + struct OptionMilliSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { + type Value = Option>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in milliseconds or none") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_some(self, d: D) -> Result>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_none(self) -> Result>, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_unit(self) -> Result>, E> + where + E: de::Error, + { + Ok(None) + } + } + } + + /// 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::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 core::fmt; + use serdelib::{de, ser}; + + use offset::TimeZone; + use {DateTime, Utc}; + + use super::{serde_from, SecondsTimestampVisitor}; + + /// 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::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()) + } + + /// 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::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(d.deserialize_i64(SecondsTimestampVisitor)?) + } + + impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("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, + { + serde_from(Utc.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, + { + serde_from(Utc.timestamp_opt(value as i64, 0), &value) + } + } + } + + /// Ser/de to/from optional 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::serde::ts_seconds_option; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_seconds_option")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let time = Some(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_option { + use core::fmt; + use serdelib::{de, ser}; + + use {DateTime, Utc}; + + use super::SecondsTimestampVisitor; + + /// Serialize a UTC datetime into an integer number of seconds since the epoch or none + /// + /// 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::serde::ts_seconds_option::serialize as to_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_tsopt")] + /// time: Option> + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: Some(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(opt: &Option>, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a seconds timestamp or none + /// + /// 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::serde::ts_seconds_option::deserialize as from_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_tsopt")] + /// time: Option> + /// } + /// + /// # 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(d.deserialize_option(OptionSecondsTimestampVisitor)?) + } + + struct OptionSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { + type Value = Option>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in seconds or none") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_some(self, d: D) -> Result>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_none(self) -> Result>, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_unit(self) -> Result>, E> + where + E: de::Error, + { + Ok(None) + } + } + } + + impl ser::Serialize for DateTime { + /// Serialize into a rfc3339 time string + /// + /// See [the `serde` module](./serde/index.html) for alternate + /// serializations. + 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.collect_str(&FormatWrapped { inner: &self }) + } + } + + struct DateTimeVisitor; + + impl<'de> de::Visitor<'de> for DateTimeVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a formatted date and time string or a unix timestamp") + } + + fn visit_str(self, value: &str) -> Result, E> + where + E: de::Error, + { + value.parse().map_err(|err: ::format::ParseError| E::custom(err)) + } + } + + /// Deserialize a value that optionally includes a timezone offset in its + /// string representation + /// + /// The value to be deserialized must be an rfc3339 string. + /// + /// See [the `serde` module](./serde/index.html) for alternate + /// deserialization formats. + impl<'de> de::Deserialize<'de> for DateTime { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(DateTimeVisitor) + } + } + + /// Deserialize into a UTC value + /// + /// The value to be deserialized must be an rfc3339 string. + /// + /// See [the `serde` module](./serde/index.html) for alternate + /// deserialization formats. + 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)) + } + } + + /// Deserialize a value that includes no timezone in its string + /// representation + /// + /// The value to be deserialized must be an rfc3339 string. + /// + /// See [the `serde` module](./serde/index.html) for alternate + /// serialization formats. + #[cfg(feature = "clock")] + 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(&Local)) + } + } + + #[cfg(test)] + extern crate bincode; + #[cfg(test)] + extern crate serde_json; + + #[test] + fn test_serde_serialize() { + super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string); + } + + #[cfg(feature = "clock")] + #[test] + fn test_serde_deserialize() { + 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] + fn test_serde_bincode() { + // Bincode is relevant to test separately from JSON because + // it is not self-describing. + use self::bincode::{deserialize, serialize, Infinite}; + + let dt = Utc.ymd(2014, 7, 24).and_hms(12, 34, 6); + let encoded = serialize(&dt, Infinite).unwrap(); + let decoded: DateTime = deserialize(&encoded).unwrap(); + assert_eq!(dt, decoded); + assert_eq!(dt.offset(), decoded.offset()); + } +} + +#[cfg(test)] +mod tests { + use super::DateTime; + use naive::{NaiveDate, NaiveTime}; + #[cfg(feature = "clock")] + use offset::Local; + use offset::{FixedOffset, TimeZone, Utc}; + use oldtime::Duration; + use std::time::{SystemTime, UNIX_EPOCH}; + #[cfg(feature = "clock")] + use Datelike; + + #[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); + + 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)), + "2014-05-06 07:08:09 -04:00" + ); + 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)), "2014-05-06T07:08:09Z"); + 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)), + "2014-05-06T07:08:09+09:00" + ); + + // edge cases + 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)), + "2014-05-06T00:00:00-04:00" + ); + 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)), + "2014-05-06T23:59:59Z" + ); + 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)), + "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)), + 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); + } + + #[test] + fn test_datetime_date_and_time() { + let tz = FixedOffset::east(5 * 60 * 60); + let d = tz.ymd(2014, 5, 6).and_hms(7, 8, 9); + assert_eq!(d.time(), NaiveTime::from_hms(7, 8, 9)); + assert_eq!(d.date(), tz.ymd(2014, 5, 6)); + assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2014, 5, 6)); + assert_eq!(d.date().and_time(d.time()), Some(d)); + + let tz = FixedOffset::east(4 * 60 * 60); + let d = tz.ymd(2016, 5, 4).and_hms(3, 2, 1); + assert_eq!(d.time(), NaiveTime::from_hms(3, 2, 1)); + assert_eq!(d.date(), tz.ymd(2016, 5, 4)); + assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2016, 5, 4)); + assert_eq!(d.date().and_time(d.time()), Some(d)); + + let tz = FixedOffset::west(13 * 60 * 60); + let d = tz.ymd(2017, 8, 9).and_hms(12, 34, 56); + assert_eq!(d.time(), NaiveTime::from_hms(12, 34, 56)); + assert_eq!(d.date(), tz.ymd(2017, 8, 9)); + assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2017, 8, 9)); + assert_eq!(d.date().and_time(d.time()), Some(d)); + + let utc_d = Utc.ymd(2017, 8, 9).and_hms(12, 34, 56); + assert!(utc_d < d); + } + + #[test] + #[cfg(feature = "clock")] + fn test_datetime_with_timezone() { + let local_now = Local::now(); + let utc_now = local_now.with_timezone(&Utc); + let local_now2 = utc_now.with_timezone(&Local); + assert_eq!(local_now, local_now2); + } + + #[test] + #[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(), + "Wed, 18 Feb 2015 23:16:09 +0000" + ); + 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" + ); + assert_eq!( + EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc3339(), + "2015-02-18T23:16:09.150+05:00" + ); + assert_eq!( + EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567).to_rfc2822(), + "Wed, 18 Feb 2015 23:59:60 +0500" + ); + assert_eq!( + EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567).to_rfc3339(), + "2015-02-18T23:59:60.234567+05:00" + ); + + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)) + ); + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)) + ); + assert_eq!( + DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)) + ); + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), + Ok(EDT.ymd(2015, 2, 18).and_hms_milli(23, 59, 59, 1_000)) + ); + assert_eq!( + DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + Ok(EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567)) + ); + } + + #[test] + fn test_rfc3339_opts() { + use SecondsFormat::*; + let pst = FixedOffset::east(8 * 60 * 60); + let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000); + assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00"); + assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00"); + assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00"); + assert_eq!(dt.to_rfc3339_opts(Micros, false), "2018-01-11T10:05:13.084660+08:00"); + assert_eq!(dt.to_rfc3339_opts(Nanos, false), "2018-01-11T10:05:13.084660000+08:00"); + assert_eq!(dt.to_rfc3339_opts(AutoSi, false), "2018-01-11T10:05:13.084660+08:00"); + + let ut = DateTime::::from_utc(dt.naive_utc(), Utc); + assert_eq!(ut.to_rfc3339_opts(Secs, false), "2018-01-11T02:05:13+00:00"); + assert_eq!(ut.to_rfc3339_opts(Secs, true), "2018-01-11T02:05:13Z"); + assert_eq!(ut.to_rfc3339_opts(Millis, false), "2018-01-11T02:05:13.084+00:00"); + assert_eq!(ut.to_rfc3339_opts(Millis, true), "2018-01-11T02:05:13.084Z"); + assert_eq!(ut.to_rfc3339_opts(Micros, true), "2018-01-11T02:05:13.084660Z"); + assert_eq!(ut.to_rfc3339_opts(Nanos, true), "2018-01-11T02:05:13.084660000Z"); + assert_eq!(ut.to_rfc3339_opts(AutoSi, true), "2018-01-11T02:05:13.084660Z"); + } + + #[test] + #[should_panic] + fn test_rfc3339_opts_nonexhaustive() { + use SecondsFormat; + let dt = Utc.ymd(1999, 10, 9).and_hms(1, 2, 3); + dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true); + } + + #[test] + fn test_datetime_from_str() { + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::>(), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) + ); + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::>(), + Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) + ); + assert_eq!( + "2015-02-18T23:16:9.15 UTC".parse::>(), + Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) + ); + assert_eq!( + "2015-02-18T23:16:9.15UTC".parse::>(), + Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) + ); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::>(), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::>(), + 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()); + + // no test for `DateTime`, we cannot verify that much. + } + + #[test] + fn test_datetime_parse_from_str() { + let ymdhms = |y, m, d, h, n, s, off| FixedOffset::east(off).ymd(y, m, d).and_hms(h, n, s); + assert_eq!( + DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(ymdhms(2014, 5, 7, 12, 34, 56, 570 * 60)) + ); // ignore offset + 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", "%a, %d %b %Y %H:%M:%S GMT"), + Ok(Utc.ymd(2013, 8, 9).and_hms(23, 54, 35)) + ); + } + + #[test] + fn test_to_string_round_trip() { + let dt = Utc.ymd(2000, 1, 1).and_hms(0, 0, 0); + let _dt: DateTime = dt.to_string().parse().unwrap(); + + let ndt_fixed = dt.with_timezone(&FixedOffset::east(3600)); + let _dt: DateTime = ndt_fixed.to_string().parse().unwrap(); + + let ndt_fixed = dt.with_timezone(&FixedOffset::east(0)); + let _dt: DateTime = ndt_fixed.to_string().parse().unwrap(); + } + + #[test] + #[cfg(feature = "clock")] + fn test_to_string_round_trip_with_local() { + let ndt = Local::now(); + let _dt: DateTime = ndt.to_string().parse().unwrap(); + } + + #[test] + #[cfg(feature = "clock")] + 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()); + } + + #[test] + #[cfg(feature = "clock")] + fn test_datetime_is_copy() { + // UTC is known to be `Copy`. + let a = Utc::now(); + let b = a; + assert_eq!(a, b); + } + + #[test] + #[cfg(feature = "clock")] + fn test_datetime_is_send() { + use std::thread; + + // UTC is known to be `Send`. + let a = Utc::now(); + thread::spawn(move || { + let _ = a; + }) + .join() + .unwrap(); + } + + #[test] + fn test_subsecond_part() { + 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()); + assert_eq!(1234567, datetime.timestamp_subsec_nanos()); + } + + #[test] + #[cfg(not(target_os = "windows"))] + fn test_from_system_time() { + use std::time::Duration; + + let epoch = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let nanos = 999_999_999; + + // SystemTime -> DateTime + assert_eq!(DateTime::::from(UNIX_EPOCH), epoch); + assert_eq!( + DateTime::::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), + Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos) + ); + assert_eq!( + DateTime::::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), + Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1) + ); + + // DateTime -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!( + SystemTime::from(Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos)), + UNIX_EPOCH + Duration::new(999_999_999, nanos) + ); + assert_eq!( + SystemTime::from(Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1)), + UNIX_EPOCH - Duration::new(999_999_999, 999_999_999) + ); + + // DateTime -> SystemTime (via `with_timezone`) + #[cfg(feature = "clock")] + { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } + assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH); + assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH); + } + + #[test] + #[cfg(target_os = "windows")] + fn test_from_system_time() { + use std::time::Duration; + + let nanos = 999_999_000; + + let epoch = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + + // SystemTime -> DateTime + assert_eq!(DateTime::::from(UNIX_EPOCH), epoch); + assert_eq!( + DateTime::::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), + Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos) + ); + assert_eq!( + DateTime::::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), + Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1_000) + ); + + // DateTime -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!( + SystemTime::from(Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos)), + UNIX_EPOCH + Duration::new(999_999_999, nanos) + ); + assert_eq!( + SystemTime::from(Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1_000)), + UNIX_EPOCH - Duration::new(999_999_999, nanos) + ); + + // DateTime -> SystemTime (via `with_timezone`) + #[cfg(feature = "clock")] + { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } + assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH); + assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH); + } + + #[test] + fn test_datetime_format_alignment() { + let datetime = Utc.ymd(2007, 01, 02); + + // Item::Literal + let percent = datetime.format("%%"); + assert_eq!(" %", format!("{:>3}", percent)); + assert_eq!("% ", format!("{:<3}", percent)); + assert_eq!(" % ", format!("{:^3}", percent)); + + // Item::Numeric + let year = datetime.format("%Y"); + assert_eq!(" 2007", format!("{:>6}", year)); + assert_eq!("2007 ", format!("{:<6}", year)); + assert_eq!(" 2007 ", format!("{:^6}", year)); + + // Item::Fixed + let tz = datetime.format("%Z"); + assert_eq!(" UTC", format!("{:>5}", tz)); + assert_eq!("UTC ", format!("{:<5}", tz)); + assert_eq!(" UTC ", format!("{:^5}", tz)); + + // [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric] + let ymd = datetime.format("%Y %B %d"); + let ymd_formatted = "2007 January 02"; + assert_eq!(format!(" {}", ymd_formatted), format!("{:>17}", ymd)); + assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd)); + assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd)); + } +} diff --git a/src/div.rs b/src/div.rs new file mode 100644 index 0000000..64b8e4b --- /dev/null +++ b/src/div.rs @@ -0,0 +1,41 @@ +// 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/)) + +// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, +// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) + +pub use num_integer::{div_floor, div_mod_floor, div_rem, mod_floor}; + +#[cfg(test)] +mod tests { + use super::{div_mod_floor, mod_floor}; + + #[test] + fn test_mod_floor() { + assert_eq!(mod_floor(8, 3), 2); + assert_eq!(mod_floor(8, -3), -1); + assert_eq!(mod_floor(-8, 3), 1); + assert_eq!(mod_floor(-8, -3), -2); + + assert_eq!(mod_floor(1, 2), 1); + assert_eq!(mod_floor(1, -2), -1); + assert_eq!(mod_floor(-1, 2), 1); + assert_eq!(mod_floor(-1, -2), -1); + } + + #[test] + fn test_div_mod_floor() { + assert_eq!(div_mod_floor(8, 3), (2, 2)); + assert_eq!(div_mod_floor(8, -3), (-3, -1)); + assert_eq!(div_mod_floor(-8, 3), (-3, 1)); + assert_eq!(div_mod_floor(-8, -3), (2, -2)); + + assert_eq!(div_mod_floor(1, 2), (0, 1)); + assert_eq!(div_mod_floor(1, -2), (-1, -1)); + assert_eq!(div_mod_floor(-1, 2), (-1, 1)); + assert_eq!(div_mod_floor(-1, -2), (0, -1)); + } +} diff --git a/src/format/locales.rs b/src/format/locales.rs new file mode 100644 index 0000000..f7b4bbd --- /dev/null +++ b/src/format/locales.rs @@ -0,0 +1,33 @@ +use pure_rust_locales::{locale_match, Locale}; + +pub(crate) fn short_months(locale: Locale) -> &'static [&'static str] { + locale_match!(locale => LC_TIME::ABMON) +} + +pub(crate) fn long_months(locale: Locale) -> &'static [&'static str] { + locale_match!(locale => LC_TIME::MON) +} + +pub(crate) fn short_weekdays(locale: Locale) -> &'static [&'static str] { + locale_match!(locale => LC_TIME::ABDAY) +} + +pub(crate) fn long_weekdays(locale: Locale) -> &'static [&'static str] { + locale_match!(locale => LC_TIME::DAY) +} + +pub(crate) fn am_pm(locale: Locale) -> &'static [&'static str] { + locale_match!(locale => LC_TIME::AM_PM) +} + +pub(crate) fn d_fmt(locale: Locale) -> &'static str { + locale_match!(locale => LC_TIME::D_FMT) +} + +pub(crate) fn d_t_fmt(locale: Locale) -> &'static str { + locale_match!(locale => LC_TIME::D_T_FMT) +} + +pub(crate) fn t_fmt(locale: Locale) -> &'static str { + locale_match!(locale => LC_TIME::T_FMT) +} diff --git a/src/format/mod.rs b/src/format/mod.rs new file mode 100644 index 0000000..a641f19 --- /dev/null +++ b/src/format/mod.rs @@ -0,0 +1,938 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! 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). + +#![allow(ellipsis_inclusive_range_patterns)] + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +#[cfg(feature = "alloc")] +use alloc::string::{String, ToString}; +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::fmt; +use core::str::FromStr; +#[cfg(any(feature = "std", test))] +use std::error::Error; + +#[cfg(any(feature = "alloc", feature = "std", test))] +use naive::{NaiveDate, NaiveTime}; +#[cfg(any(feature = "alloc", feature = "std", test))] +use offset::{FixedOffset, Offset}; +#[cfg(any(feature = "alloc", feature = "std", test))] +use {Datelike, Timelike}; +use {Month, ParseMonthError, ParseWeekdayError, Weekday}; + +#[cfg(feature = "unstable-locales")] +pub(crate) mod locales; + +pub use self::parse::parse; +pub use self::parsed::Parsed; +pub use self::strftime::StrftimeItems; +/// L10n locales. +#[cfg(feature = "unstable-locales")] +pub use pure_rust_locales::Locale; + +#[cfg(not(feature = "unstable-locales"))] +#[derive(Debug)] +struct Locale; + +/// An uninhabited 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 { + /// No padding. + None, + /// Zero (`0`) padding. + Zero, + /// Space padding. + Space, +} + +/// Numeric item types. +/// They have associated formatting width (FW) and parsing width (PW). +/// +/// The **formatting width** is the minimal width to be formatted. +/// If the number is too short, and the padding is not [`Pad::None`](./enum.Pad.html#variant.None), +/// then it is left-padded. +/// If the number is too long or (in some cases) negative, it is printed as is. +/// +/// The **parsing width** is the maximal width to be scanned. +/// The parser only tries to consume from one to given number of digits (greedily). +/// It also trims the preceding whitespace if any. +/// It cannot parse the negative number, so some date and time cannot be formatted then +/// parsed with the same formatting items. +#[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. + Year, + /// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year. + YearDiv100, + /// Gregorian year modulo 100 (FW=PW=2). Cannot be negative. + YearMod100, + /// Year in the ISO week date (FW=4, PW=∞). + /// May accept years before 1 BCE or after 9999 CE, given an initial sign. + IsoYear, + /// Year in the ISO week date, divided by 100 (FW=PW=2). Implies the non-negative year. + IsoYearDiv100, + /// Year in the ISO week date, modulo 100 (FW=PW=2). Cannot be negative. + IsoYearMod100, + /// Month (FW=PW=2). + Month, + /// Day of the month (FW=PW=2). + Day, + /// Week number, where the week 1 starts at the first Sunday of January (FW=PW=2). + WeekFromSun, + /// Week number, where the week 1 starts at the first Monday of January (FW=PW=2). + WeekFromMon, + /// Week number in the ISO week date (FW=PW=2). + IsoWeek, + /// Day of the week, where Sunday = 0 and Saturday = 6 (FW=PW=1). + NumDaysFromSun, + /// Day of the week, where Monday = 1 and Sunday = 7 (FW=PW=1). + WeekdayFromMon, + /// Day of the year (FW=PW=3). + Ordinal, + /// Hour number in the 24-hour clocks (FW=PW=2). + Hour, + /// Hour number in the 12-hour clocks (FW=PW=2). + Hour12, + /// The number of minutes since the last whole hour (FW=PW=2). + Minute, + /// The number of seconds since the last whole minute (FW=PW=2). + Second, + /// The number of nanoseconds since the last whole second (FW=PW=9). + /// Note that this is *not* left-aligned; + /// see also [`Fixed::Nanosecond`](./enum.Fixed.html#variant.Nanosecond). + Nanosecond, + /// 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. +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, "") + } +} + +/// 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(Clone, PartialEq, Eq, Debug)] +pub enum Fixed { + /// Abbreviated month names. + /// + /// Prints a three-letter-long name in the title case, reads the same name in any case. + ShortMonthName, + /// Full month names. + /// + /// Prints a full name in the title case, reads either a short or full name in any case. + LongMonthName, + /// Abbreviated day of the week names. + /// + /// Prints a three-letter-long name in the title case, reads the same name in any case. + ShortWeekdayName, + /// Full day of the week names. + /// + /// Prints a full name in the title case, reads either a short or full name in any case. + LongWeekdayName, + /// AM/PM. + /// + /// Prints in lower case, reads in any case. + LowerAmPm, + /// AM/PM. + /// + /// Prints in upper case, reads in any case. + UpperAmPm, + /// An optional dot plus one or more digits for left-aligned nanoseconds. + /// May print nothing, 3, 6 or 9 digits according to the available accuracy. + /// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.Nanosecond). + Nanosecond, + /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3. + Nanosecond3, + /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6. + Nanosecond6, + /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9. + Nanosecond9, + /// Timezone name. + /// + /// It does not support parsing, its use in the parser is an immediate failure. + TimezoneName, + /// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`). + /// + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. + /// The offset is limited from `-24:00` to `+24:00`, + /// which is the same as [`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 whitespace, + /// and `Z` can be either in upper case or in lower case. + /// The offset is limited from `-24:00` to `+24:00`, + /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. + TimezoneOffsetColonZ, + /// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon. + /// Parsing allows an optional colon. + TimezoneOffset, + /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon. + /// Parsing allows an optional colon. + TimezoneOffsetZ, + /// RFC 2822 date and time syntax. Commonly used for email and MIME date and time. + 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(Debug, Clone, PartialEq, Eq)] +pub struct InternalFixed { + val: InternalInternal, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum InternalInternal { + /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but + /// allows missing minutes (per [ISO 8601][iso8601]). + /// + /// # Panics + /// + /// If you try to use this for printing. + /// + /// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC + TimezoneOffsetPermissive, + /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot. + Nanosecond3NoDot, + /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot. + Nanosecond6NoDot, + /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot. + Nanosecond9NoDot, +} + +/// A single formatting item. This is used for both formatting and parsing. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Item<'a> { + /// A literally printed and parsed text. + Literal(&'a str), + /// Same as `Literal` but with the string owned by the item. + #[cfg(any(feature = "alloc", feature = "std", test))] + OwnedLiteral(Box), + /// Whitespace. Prints literally but reads zero or more whitespace. + Space(&'a str), + /// Same as `Space` but with the string owned by the item. + #[cfg(any(feature = "alloc", feature = "std", test))] + 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), + /// Fixed-format item. + Fixed(Fixed), + /// Issues a formatting error. Used to signal an invalid format string. + Error, +} + +macro_rules! lit { + ($x:expr) => { + Item::Literal($x) + }; +} +macro_rules! sp { + ($x:expr) => { + Item::Space($x) + }; +} +macro_rules! num { + ($x:ident) => { + Item::Numeric(Numeric::$x, Pad::None) + }; +} +macro_rules! num0 { + ($x:ident) => { + Item::Numeric(Numeric::$x, Pad::Zero) + }; +} +macro_rules! nums { + ($x:ident) => { + Item::Numeric(Numeric::$x, Pad::Space) + }; +} +macro_rules! fix { + ($x:ident) => { + Item::Fixed(Fixed::$x) + }; +} +macro_rules! internal_fix { + ($x:ident) => { + Item::Fixed(Fixed::Internal(InternalFixed { val: InternalInternal::$x })) + }; +} + +/// An error from the `parse` function. +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +pub struct ParseError(ParseErrorKind); + +/// The category of parse error +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +enum ParseErrorKind { + /// Given field is out of permitted range. + OutOfRange, + + /// There is no possible date and time value with given set of fields. + /// + /// This does not include the out-of-range conditions, which are trivially invalid. + /// It includes the case that there are one or more fields that are inconsistent to each other. + Impossible, + + /// Given set of fields is not enough to make a requested date and time value. + /// + /// Note that there *may* be a case that given fields constrain the possible values so much + /// that there is a unique possible value. Chrono only tries to be correct for + /// most useful sets of fields however, as such constraint solving can be expensive. + NotEnough, + + /// The input string has some invalid character sequence for given formatting items. + Invalid, + + /// The input string has been prematurely ended. + TooShort, + + /// All formatting items have been read but there is a remaining input. + TooLong, + + /// There was an error on the formatting string, or there were non-supported formating items. + BadFormat, +} + +/// Same as `Result`. +pub type ParseResult = Result; + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + ParseErrorKind::OutOfRange => write!(f, "input is out of range"), + ParseErrorKind::Impossible => write!(f, "no possible date and time matching input"), + ParseErrorKind::NotEnough => write!(f, "input is not enough for unique date and time"), + ParseErrorKind::Invalid => write!(f, "input contains invalid characters"), + ParseErrorKind::TooShort => write!(f, "premature end of input"), + ParseErrorKind::TooLong => write!(f, "trailing input"), + ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"), + } + } +} + +#[cfg(any(feature = "std", test))] +impl Error for ParseError { + #[allow(deprecated)] + fn description(&self) -> &str { + "parser error, see to_string() for details" + } +} + +// to be used in this module and submodules +const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange); +const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible); +const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough); +const INVALID: ParseError = ParseError(ParseErrorKind::Invalid); +const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort); +const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong); +const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); + +/// Formats single formatting item +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn format_item<'a>( + w: &mut fmt::Formatter, + date: Option<&NaiveDate>, + time: Option<&NaiveTime>, + off: Option<&(String, FixedOffset)>, + item: &Item<'a>, +) -> fmt::Result { + let mut result = String::new(); + format_inner(&mut result, date, time, off, item, None)?; + w.pad(&result) +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +fn format_inner<'a>( + result: &mut String, + date: Option<&NaiveDate>, + time: Option<&NaiveTime>, + off: Option<&(String, FixedOffset)>, + item: &Item<'a>, + _locale: Option, +) -> fmt::Result { + #[cfg(feature = "unstable-locales")] + let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = { + let locale = _locale.unwrap_or(Locale::POSIX); + let am_pm = locales::am_pm(locale); + ( + locales::short_months(locale), + locales::long_months(locale), + locales::short_weekdays(locale), + locales::long_weekdays(locale), + am_pm, + &[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()], + ) + }; + #[cfg(not(feature = "unstable-locales"))] + let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = { + ( + &["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + &[ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ], + &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + &["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + &["AM", "PM"], + &["am", "pm"], + ) + }; + + use core::fmt::Write; + use div::{div_floor, mod_floor}; + + match *item { + Item::Literal(s) | Item::Space(s) => result.push_str(s), + #[cfg(any(feature = "alloc", feature = "std", test))] + Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s), + + Item::Numeric(ref spec, ref pad) => { + use self::Numeric::*; + + let week_from_sun = |d: &NaiveDate| { + (d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 7) / 7 + }; + let week_from_mon = |d: &NaiveDate| { + (d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7 + }; + + let (width, v) = match *spec { + Year => (4, date.map(|d| i64::from(d.year()))), + YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))), + YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))), + IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))), + IsoYearDiv100 => (2, date.map(|d| div_floor(i64::from(d.iso_week().year()), 100))), + IsoYearMod100 => (2, date.map(|d| mod_floor(i64::from(d.iso_week().year()), 100))), + Month => (2, date.map(|d| i64::from(d.month()))), + Day => (2, date.map(|d| i64::from(d.day()))), + WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))), + WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))), + IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))), + NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))), + WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))), + Ordinal => (3, date.map(|d| i64::from(d.ordinal()))), + Hour => (2, time.map(|t| i64::from(t.hour()))), + Hour12 => (2, time.map(|t| i64::from(t.hour12().1))), + Minute => (2, time.map(|t| i64::from(t.minute()))), + Second => (2, time.map(|t| i64::from(t.second() + t.nanosecond() / 1_000_000_000))), + Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))), + Timestamp => ( + 1, + match (date, time, off) { + (Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()), + (Some(d), Some(t), Some(&(_, off))) => { + Some((d.and_time(*t) - off).timestamp()) + } + (_, _, _) => None, + }, + ), + + // for the future expansion + Internal(ref int) => match int._dummy {}, + }; + + if let Some(v) = v { + if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) { + // non-four-digit years require an explicit sign as per ISO 8601 + match *pad { + Pad::None => write!(result, "{:+}", v), + Pad::Zero => write!(result, "{:+01$}", v, width + 1), + Pad::Space => write!(result, "{:+1$}", v, width + 1), + } + } else { + match *pad { + Pad::None => write!(result, "{}", v), + Pad::Zero => write!(result, "{:01$}", v, width), + Pad::Space => write!(result, "{:1$}", v, width), + } + }? + } else { + return Err(fmt::Error); // insufficient arguments for given format + } + } + + Item::Fixed(ref spec) => { + use self::Fixed::*; + + /// 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( + result: &mut String, + off: FixedOffset, + allow_zulu: bool, + use_colon: bool, + ) -> fmt::Result { + let off = off.local_minus_utc(); + if !allow_zulu || off != 0 { + let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; + if use_colon { + write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60) + } else { + write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60) + } + } else { + result.push_str("Z"); + Ok(()) + } + } + + let ret = + match *spec { + ShortMonthName => date.map(|d| { + result.push_str(short_months[d.month0() as usize]); + Ok(()) + }), + LongMonthName => date.map(|d| { + result.push_str(long_months[d.month0() as usize]); + Ok(()) + }), + ShortWeekdayName => date.map(|d| { + result + .push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]); + Ok(()) + }), + LongWeekdayName => date.map(|d| { + result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]); + Ok(()) + }), + LowerAmPm => time.map(|t| { + #[cfg_attr(feature = "cargo-clippy", allow(useless_asref))] + { + result.push_str(if t.hour12().0 { + am_pm_lowercase[1].as_ref() + } else { + am_pm_lowercase[0].as_ref() + }); + } + Ok(()) + }), + UpperAmPm => time.map(|t| { + result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] }); + Ok(()) + }), + Nanosecond => time.map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + if nano == 0 { + Ok(()) + } else if nano % 1_000_000 == 0 { + write!(result, ".{:03}", nano / 1_000_000) + } else if nano % 1_000 == 0 { + write!(result, ".{:06}", nano / 1_000) + } else { + write!(result, ".{:09}", nano) + } + }), + Nanosecond3 => time.map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + write!(result, ".{:03}", nano / 1_000_000) + }), + Nanosecond6 => time.map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + write!(result, ".{:06}", nano / 1_000) + }), + Nanosecond9 => time.map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + write!(result, ".{:09}", nano) + }), + Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => time + .map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + write!(result, "{:03}", nano / 1_000_000) + }), + Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => time + .map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + write!(result, "{:06}", nano / 1_000) + }), + Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => time + .map(|t| { + let nano = t.nanosecond() % 1_000_000_000; + write!(result, "{:09}", nano) + }), + TimezoneName => off.map(|&(ref name, _)| { + result.push_str(name); + Ok(()) + }), + TimezoneOffsetColon => { + off.map(|&(_, off)| write_local_minus_utc(result, off, false, true)) + } + TimezoneOffsetColonZ => { + off.map(|&(_, off)| write_local_minus_utc(result, off, true, true)) + } + TimezoneOffset => { + off.map(|&(_, off)| write_local_minus_utc(result, off, false, false)) + } + TimezoneOffsetZ => { + off.map(|&(_, off)| write_local_minus_utc(result, off, true, false)) + } + Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => { + panic!("Do not try to write %#z it is undefined") + } + RFC2822 => + // same as `%a, %d %b %Y %H:%M:%S %z` + { + if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { + let sec = t.second() + t.nanosecond() / 1_000_000_000; + write!( + result, + "{}, {:02} {} {:04} {:02}:{:02}:{:02} ", + short_weekdays[d.weekday().num_days_from_sunday() as usize], + d.day(), + short_months[d.month0() as usize], + d.year(), + t.hour(), + t.minute(), + sec + )?; + Some(write_local_minus_utc(result, off, false, false)) + } else { + None + } + } + RFC3339 => + // same as `%Y-%m-%dT%H:%M:%S%.f%:z` + { + if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { + // reuse `Debug` impls which already print ISO 8601 format. + // this is faster in this way. + write!(result, "{:?}T{:?}", d, t)?; + Some(write_local_minus_utc(result, off, false, true)) + } else { + None + } + } + }; + + match ret { + Some(ret) => ret?, + None => return Err(fmt::Error), // insufficient arguments for given format + } + } + + Item::Error => return Err(fmt::Error), + } + Ok(()) +} + +/// Tries to format given arguments with given formatting items. +/// Internally used by `DelayedFormat`. +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn format<'a, I, B>( + w: &mut fmt::Formatter, + date: Option<&NaiveDate>, + time: Option<&NaiveTime>, + off: Option<&(String, FixedOffset)>, + items: I, +) -> fmt::Result +where + I: Iterator + Clone, + B: Borrow>, +{ + let mut result = String::new(); + for item in items { + format_inner(&mut result, date, time, off, item.borrow(), None)?; + } + w.pad(&result) +} + +mod parsed; + +// due to the size of parsing routines, they are in separate modules. +mod parse; +mod scan; + +pub mod strftime; + +/// A *temporary* object which can be used as an argument to `format!` or others. +/// This is normally constructed via `format` methods of each date and time type. +#[cfg(any(feature = "alloc", feature = "std", test))] +#[derive(Debug)] +pub struct DelayedFormat { + /// The date view, if any. + date: Option, + /// The time view, if any. + time: Option, + /// The name and local-to-UTC difference for the offset (timezone), if any. + off: Option<(String, FixedOffset)>, + /// An iterator returning formatting items. + items: I, + /// Locale used for text. + locale: Option, +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +impl<'a, I: Iterator + Clone, B: Borrow>> DelayedFormat { + /// Makes a new `DelayedFormat` value out of local date and time. + pub fn new(date: Option, time: Option, items: I) -> DelayedFormat { + DelayedFormat { date: date, time: time, off: None, items: items, locale: None } + } + + /// Makes a new `DelayedFormat` value out of local date and time and UTC offset. + 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.fix()); + DelayedFormat { + date: date, + time: time, + off: Some(name_and_diff), + items: items, + locale: None, + } + } + + /// Makes a new `DelayedFormat` value out of local date and time and locale. + #[cfg(feature = "unstable-locales")] + pub fn new_with_locale( + date: Option, + time: Option, + items: I, + locale: Locale, + ) -> DelayedFormat { + DelayedFormat { date: date, time: time, off: None, items: items, locale: Some(locale) } + } + + /// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale. + #[cfg(feature = "unstable-locales")] + pub fn new_with_offset_and_locale( + date: Option, + time: Option, + offset: &Off, + items: I, + locale: Locale, + ) -> DelayedFormat + where + Off: Offset + fmt::Display, + { + let name_and_diff = (offset.to_string(), offset.fix()); + DelayedFormat { + date: date, + time: time, + off: Some(name_and_diff), + items: items, + locale: Some(locale), + } + } +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +impl<'a, I: Iterator + Clone, B: Borrow>> fmt::Display for DelayedFormat { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(feature = "unstable-locales")] + { + if let Some(locale) = self.locale { + return format_localized( + f, + self.date.as_ref(), + self.time.as_ref(), + self.off.as_ref(), + self.items.clone(), + locale, + ); + } + } + + format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone()) + } +} + +// 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: () }) + } + } +} + +/// Formats single formatting item +#[cfg(feature = "unstable-locales")] +pub fn format_item_localized<'a>( + w: &mut fmt::Formatter, + date: Option<&NaiveDate>, + time: Option<&NaiveTime>, + off: Option<&(String, FixedOffset)>, + item: &Item<'a>, + locale: Locale, +) -> fmt::Result { + let mut result = String::new(); + format_inner(&mut result, date, time, off, item, Some(locale))?; + w.pad(&result) +} + +/// Tries to format given arguments with given formatting items. +/// Internally used by `DelayedFormat`. +#[cfg(feature = "unstable-locales")] +pub fn format_localized<'a, I, B>( + w: &mut fmt::Formatter, + date: Option<&NaiveDate>, + time: Option<&NaiveTime>, + off: Option<&(String, FixedOffset)>, + items: I, + locale: Locale, +) -> fmt::Result +where + I: Iterator + Clone, + B: Borrow>, +{ + let mut result = String::new(); + for item in items { + format_inner(&mut result, date, time, off, item.borrow(), Some(locale))?; + } + w.pad(&result) +} + +/// Parsing a `str` into a `Month` uses the format [`%W`](./format/strftime/index.html). +/// +/// # Example +/// +/// ~~~~ +/// use chrono::Month; +/// +/// assert_eq!("January".parse::(), Ok(Month::January)); +/// assert!("any day".parse::().is_err()); +/// ~~~~ +/// +/// The parsing is case-insensitive. +/// +/// ~~~~ +/// # use chrono::Month; +/// assert_eq!("fEbruARy".parse::(), Ok(Month::February)); +/// ~~~~ +/// +/// Only the shortest form (e.g. `jan`) and the longest form (e.g. `january`) is accepted. +/// +/// ~~~~ +/// # use chrono::Month; +/// assert!("septem".parse::().is_err()); +/// assert!("Augustin".parse::().is_err()); +/// ~~~~ +impl FromStr for Month { + type Err = ParseMonthError; + + fn from_str(s: &str) -> Result { + if let Ok(("", w)) = scan::short_or_long_month0(s) { + match w { + 0 => Ok(Month::January), + 1 => Ok(Month::February), + 2 => Ok(Month::March), + 3 => Ok(Month::April), + 4 => Ok(Month::May), + 5 => Ok(Month::June), + 6 => Ok(Month::July), + 7 => Ok(Month::August), + 8 => Ok(Month::September), + 9 => Ok(Month::October), + 10 => Ok(Month::November), + 11 => Ok(Month::December), + _ => Err(ParseMonthError { _dummy: () }), + } + } else { + Err(ParseMonthError { _dummy: () }) + } + } +} diff --git a/src/format/parse.rs b/src/format/parse.rs new file mode 100644 index 0000000..2fce827 --- /dev/null +++ b/src/format/parse.rs @@ -0,0 +1,934 @@ +// This is a part of Chrono. +// Portions copyright (c) 2015, John Nagle. +// See README.md and LICENSE.txt for details. + +//! Date and time parsing routines. + +#![allow(deprecated)] + +use core::borrow::Borrow; +use core::str; +use core::usize; + +use super::scan; +use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed}; +use super::{ParseError, ParseErrorKind, ParseResult}; +use super::{BAD_FORMAT, INVALID, NOT_ENOUGH, OUT_OF_RANGE, TOO_LONG, TOO_SHORT}; +use {DateTime, FixedOffset, Weekday}; + +fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> { + p.set_weekday(match v { + 0 => Weekday::Sun, + 1 => Weekday::Mon, + 2 => Weekday::Tue, + 3 => Weekday::Wed, + 4 => Weekday::Thu, + 5 => Weekday::Fri, + 6 => Weekday::Sat, + _ => return Err(OUT_OF_RANGE), + }) +} + +fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()> { + p.set_weekday(match v { + 1 => Weekday::Mon, + 2 => Weekday::Tue, + 3 => Weekday::Wed, + 4 => Weekday::Thu, + 5 => Weekday::Fri, + 6 => Weekday::Sat, + 7 => Weekday::Sun, + _ => return Err(OUT_OF_RANGE), + }) +} + +fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> { + macro_rules! try_consume { + ($e:expr) => {{ + let (s_, v) = $e?; + s = s_; + v + }}; + } + + // an adapted RFC 2822 syntax from Section 3.3 and 4.3: + // + // date-time = [ day-of-week "," ] date 1*S time *S + // day-of-week = *S day-name *S + // day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" + // date = day month year + // day = *S 1*2DIGIT *S + // month = 1*S month-name 1*S + // month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / + // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" + // year = *S 2*DIGIT *S + // time = time-of-day 1*S zone + // time-of-day = hour ":" minute [ ":" second ] + // hour = *S 2DIGIT *S + // minute = *S 2DIGIT *S + // second = *S 2DIGIT *S + // zone = ( "+" / "-" ) 4DIGIT / + // "UT" / "GMT" / ; same as +0000 + // "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800 + // "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700 + // 1*(%d65-90 / %d97-122) ; same as -0000 + // + // some notes: + // + // - quoted characters can be in any mixture of lower and upper cases. + // + // - we do not recognize a folding white space (FWS) or comment (CFWS). + // for our purposes, instead, we accept any sequence of Unicode + // white space characters (denoted here to `S`). any actual RFC 2822 + // parser is expected to parse FWS and/or CFWS themselves and replace + // it with a single SP (`%x20`); this is legitimate. + // + // - two-digit year < 50 should be interpreted by adding 2000. + // two-digit year >= 50 or three-digit year should be interpreted + // by adding 1900. note that four-or-more-digit years less than 1000 + // are *never* affected by this rule. + // + // - mismatching day-of-week is always an error, which is consistent to + // Chrono's own rules. + // + // - zones can range from `-9959` to `+9959`, but `FixedOffset` does not + // support offsets larger than 24 hours. this is not *that* problematic + // since we do not directly go to a `DateTime` so one can recover + // the offset information from `Parsed` anyway. + + s = s.trim_left(); + + if let Ok((s_, weekday)) = scan::short_weekday(s) { + if !s_.starts_with(',') { + return Err(INVALID); + } + s = &s_[1..]; + parsed.set_weekday(weekday)?; + } + + s = s.trim_left(); + parsed.set_day(try_consume!(scan::number(s, 1, 2)))?; + s = scan::space(s)?; // mandatory + parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?; + s = scan::space(s)?; // mandatory + + // distinguish two- and three-digit years from four-digit years + let prevlen = s.len(); + let mut year = try_consume!(scan::number(s, 2, usize::MAX)); + let yearlen = prevlen - s.len(); + match (yearlen, year) { + (2, 0...49) => { + year += 2000; + } // 47 -> 2047, 05 -> 2005 + (2, 50...99) => { + year += 1900; + } // 79 -> 1979 + (3, _) => { + year += 1900; + } // 112 -> 2012, 009 -> 1909 + (_, _) => {} // 1987 -> 1987, 0654 -> 0654 + } + parsed.set_year(year)?; + + s = scan::space(s)?; // mandatory + parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?; + s = scan::char(s.trim_left(), b':')?.trim_left(); // *S ":" *S + parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?; + if let Ok(s_) = scan::char(s.trim_left(), b':') { + // [ ":" *S 2DIGIT ] + parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?; + } + + s = scan::space(s)?; // mandatory + if let Some(offset) = try_consume!(scan::timezone_offset_2822(s)) { + // only set the offset when it is definitely known (i.e. not `-0000`) + parsed.set_offset(i64::from(offset))?; + } + + Ok((s, ())) +} + +fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> { + macro_rules! try_consume { + ($e:expr) => {{ + let (s_, v) = $e?; + s = s_; + v + }}; + } + + // an adapted RFC 3339 syntax from Section 5.6: + // + // date-fullyear = 4DIGIT + // date-month = 2DIGIT ; 01-12 + // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year + // time-hour = 2DIGIT ; 00-23 + // time-minute = 2DIGIT ; 00-59 + // time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules + // time-secfrac = "." 1*DIGIT + // time-numoffset = ("+" / "-") time-hour ":" time-minute + // time-offset = "Z" / time-numoffset + // partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] + // full-date = date-fullyear "-" date-month "-" date-mday + // full-time = partial-time time-offset + // date-time = full-date "T" full-time + // + // some notes: + // + // - quoted characters can be in any mixture of lower and upper cases. + // + // - it may accept any number of fractional digits for seconds. + // for Chrono, this means that we should skip digits past first 9 digits. + // + // - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59. + // note that this restriction is unique to RFC 3339 and not ISO 8601. + // since this is not a typical Chrono behavior, we check it earlier. + + parsed.set_year(try_consume!(scan::number(s, 4, 4)))?; + s = scan::char(s, b'-')?; + parsed.set_month(try_consume!(scan::number(s, 2, 2)))?; + s = scan::char(s, b'-')?; + parsed.set_day(try_consume!(scan::number(s, 2, 2)))?; + + s = match s.as_bytes().first() { + Some(&b't') | Some(&b'T') => &s[1..], + Some(_) => return Err(INVALID), + None => return Err(TOO_SHORT), + }; + + parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?; + s = scan::char(s, b':')?; + parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?; + s = scan::char(s, b':')?; + parsed.set_second(try_consume!(scan::number(s, 2, 2)))?; + if s.starts_with('.') { + let nanosecond = try_consume!(scan::nanosecond(&s[1..])); + parsed.set_nanosecond(nanosecond)?; + } + + let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':'))); + if offset <= -86_400 || offset >= 86_400 { + return Err(OUT_OF_RANGE); + } + parsed.set_offset(i64::from(offset))?; + + Ok((s, ())) +} + +/// Tries to parse given string into `parsed` with given formatting items. +/// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used). +/// There should be no trailing string after parsing; +/// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces. +/// +/// This particular date and time parser is: +/// +/// - Greedy. It will consume the longest possible prefix. +/// For example, `April` is always consumed entirely when the long month name is requested; +/// it equally accepts `Apr`, but prefers the longer prefix in this case. +/// +/// - Padding-agnostic (for numeric items). +/// The [`Pad`](./enum.Pad.html) field is completely ignored, +/// so one can prepend any number of whitespace then any number of zeroes before numbers. +/// +/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. +pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()> +where + I: Iterator, + B: Borrow>, +{ + parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e) +} + +fn parse_internal<'a, 'b, I, B>( + parsed: &mut Parsed, + mut s: &'b str, + items: I, +) -> Result<&'b str, (&'b str, ParseError)> +where + I: Iterator, + B: Borrow>, +{ + macro_rules! try_consume { + ($e:expr) => {{ + match $e { + Ok((s_, v)) => { + s = s_; + v + } + Err(e) => return Err((s, e)), + } + }}; + } + + for item in items { + match *item.borrow() { + Item::Literal(prefix) => { + if s.len() < prefix.len() { + return Err((s, TOO_SHORT)); + } + if !s.starts_with(prefix) { + return Err((s, INVALID)); + } + s = &s[prefix.len()..]; + } + + #[cfg(any(feature = "alloc", feature = "std", test))] + Item::OwnedLiteral(ref prefix) => { + if s.len() < prefix.len() { + return Err((s, TOO_SHORT)); + } + if !s.starts_with(&prefix[..]) { + return Err((s, INVALID)); + } + s = &s[prefix.len()..]; + } + + Item::Space(_) => { + s = s.trim_left(); + } + + #[cfg(any(feature = "alloc", feature = "std", test))] + Item::OwnedSpace(_) => { + s = s.trim_left(); + } + + Item::Numeric(ref spec, ref _pad) => { + use super::Numeric::*; + type Setter = fn(&mut Parsed, i64) -> ParseResult<()>; + + let (width, signed, set): (usize, bool, Setter) = match *spec { + Year => (4, true, Parsed::set_year), + YearDiv100 => (2, false, Parsed::set_year_div_100), + YearMod100 => (2, false, Parsed::set_year_mod_100), + IsoYear => (4, true, Parsed::set_isoyear), + IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100), + IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100), + Month => (2, false, Parsed::set_month), + Day => (2, false, Parsed::set_day), + WeekFromSun => (2, false, Parsed::set_week_from_sun), + WeekFromMon => (2, false, Parsed::set_week_from_mon), + IsoWeek => (2, false, Parsed::set_isoweek), + NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday), + WeekdayFromMon => (1, false, set_weekday_with_number_from_monday), + Ordinal => (3, false, Parsed::set_ordinal), + Hour => (2, false, Parsed::set_hour), + Hour12 => (2, false, Parsed::set_hour12), + Minute => (2, false, Parsed::set_minute), + 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(); + let v = if signed { + if s.starts_with('-') { + let v = try_consume!(scan::number(&s[1..], 1, usize::MAX)); + 0i64.checked_sub(v).ok_or((s, OUT_OF_RANGE))? + } else if s.starts_with('+') { + try_consume!(scan::number(&s[1..], 1, usize::MAX)) + } else { + // if there is no explicit sign, we respect the original `width` + try_consume!(scan::number(s, 1, width)) + } + } else { + try_consume!(scan::number(s, 1, width)) + }; + set(parsed, v).map_err(|e| (s, e))?; + } + + Item::Fixed(ref spec) => { + use super::Fixed::*; + + match spec { + &ShortMonthName => { + let month0 = try_consume!(scan::short_month0(s)); + parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?; + } + + &LongMonthName => { + let month0 = try_consume!(scan::short_or_long_month0(s)); + parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?; + } + + &ShortWeekdayName => { + let weekday = try_consume!(scan::short_weekday(s)); + parsed.set_weekday(weekday).map_err(|e| (s, e))?; + } + + &LongWeekdayName => { + let weekday = try_consume!(scan::short_or_long_weekday(s)); + parsed.set_weekday(weekday).map_err(|e| (s, e))?; + } + + &LowerAmPm | &UpperAmPm => { + if s.len() < 2 { + return Err((s, TOO_SHORT)); + } + let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) { + (b'a', b'm') => false, + (b'p', b'm') => true, + _ => return Err((s, INVALID)), + }; + parsed.set_ampm(ampm).map_err(|e| (s, e))?; + s = &s[2..]; + } + + &Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => { + if s.starts_with('.') { + let nano = try_consume!(scan::nanosecond(&s[1..])); + parsed.set_nanosecond(nano).map_err(|e| (s, e))?; + } + } + + &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => { + if s.len() < 3 { + return Err((s, TOO_SHORT)); + } + let nano = try_consume!(scan::nanosecond_fixed(s, 3)); + parsed.set_nanosecond(nano).map_err(|e| (s, e))?; + } + + &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => { + if s.len() < 6 { + return Err((s, TOO_SHORT)); + } + let nano = try_consume!(scan::nanosecond_fixed(s, 6)); + parsed.set_nanosecond(nano).map_err(|e| (s, e))?; + } + + &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => { + if s.len() < 9 { + return Err((s, TOO_SHORT)); + } + let nano = try_consume!(scan::nanosecond_fixed(s, 9)); + parsed.set_nanosecond(nano).map_err(|e| (s, e))?; + } + + &TimezoneName => { + try_consume!(scan::timezone_name_skip(s)); + } + + &TimezoneOffsetColon | &TimezoneOffset => { + let offset = try_consume!(scan::timezone_offset( + s.trim_left(), + scan::colon_or_space + )); + parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?; + } + + &TimezoneOffsetColonZ | &TimezoneOffsetZ => { + let offset = try_consume!(scan::timezone_offset_zulu( + s.trim_left(), + scan::colon_or_space + )); + parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?; + } + &Internal(InternalFixed { + val: InternalInternal::TimezoneOffsetPermissive, + }) => { + let offset = try_consume!(scan::timezone_offset_permissive( + s.trim_left(), + scan::colon_or_space + )); + parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?; + } + + &RFC2822 => try_consume!(parse_rfc2822(parsed, s)), + &RFC3339 => try_consume!(parse_rfc3339(parsed, s)), + } + } + + Item::Error => { + return Err((s, BAD_FORMAT)); + } + } + } + + // if there are trailling chars, it is an error + if !s.is_empty() { + Err((s, TOO_LONG)) + } else { + Ok(s) + } +} + +impl str::FromStr for DateTime { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult> { + const DATE_ITEMS: &'static [Item<'static>] = &[ + Item::Numeric(Numeric::Year, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Month, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Day, Pad::Zero), + ]; + const TIME_ITEMS: &'static [Item<'static>] = &[ + Item::Numeric(Numeric::Hour, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Minute, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Second, Pad::Zero), + Item::Fixed(Fixed::Nanosecond), + Item::Space(""), + Item::Fixed(Fixed::TimezoneOffsetZ), + Item::Space(""), + ]; + + let mut parsed = Parsed::new(); + match parse_internal(&mut parsed, s, DATE_ITEMS.iter()) { + Err((remainder, e)) if e.0 == ParseErrorKind::TooLong => { + if remainder.starts_with('T') || remainder.starts_with(' ') { + parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?; + } else { + Err(INVALID)?; + } + } + Err((_s, e)) => Err(e)?, + Ok(_) => Err(NOT_ENOUGH)?, + }; + parsed.to_datetime() + } +} + +#[cfg(test)] +#[test] +fn test_parse() { + use super::IMPOSSIBLE; + use super::*; + + // workaround for Rust issue #22255 + fn parse_all(s: &str, items: &[Item]) -> ParseResult { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, items.iter())?; + Ok(parsed) + } + + macro_rules! check { + ($fmt:expr, $items:expr; $err:tt) => ( + assert_eq!(parse_all($fmt, &$items), Err($err)) + ); + ($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 + check!("", []; ); + check!(" ", []; TOO_LONG); + check!("a", []; TOO_LONG); + + // whitespaces + check!("", [sp!("")]; ); + check!(" ", [sp!("")]; ); + check!("\t", [sp!("")]; ); + check!(" \n\r \n", [sp!("")]; ); + check!("a", [sp!("")]; TOO_LONG); + + // literal + check!("", [lit!("a")]; TOO_SHORT); + check!(" ", [lit!("a")]; INVALID); + check!("a", [lit!("a")]; ); + check!("aa", [lit!("a")]; TOO_LONG); + check!("A", [lit!("a")]; INVALID); + check!("xy", [lit!("xy")]; ); + check!("xy", [lit!("x"), lit!("y")]; ); + check!("x y", [lit!("x"), lit!("y")]; INVALID); + check!("xy", [lit!("x"), sp!(""), lit!("y")]; ); + check!("x y", [lit!("x"), sp!(""), lit!("y")]; ); + + // numeric + check!("1987", [num!(Year)]; year: 1987); + check!("1987 ", [num!(Year)]; TOO_LONG); + check!("0x12", [num!(Year)]; TOO_LONG); // `0` is parsed + check!("x123", [num!(Year)]; INVALID); + check!("2015", [num!(Year)]; year: 2015); + check!("0000", [num!(Year)]; year: 0); + check!("9999", [num!(Year)]; year: 9999); + check!(" \t987", [num!(Year)]; year: 987); + check!("5", [num!(Year)]; year: 5); + check!("5\0", [num!(Year)]; TOO_LONG); + check!("\05", [num!(Year)]; INVALID); + check!("", [num!(Year)]; TOO_SHORT); + check!("12345", [num!(Year), lit!("5")]; year: 1234); + check!("12345", [nums!(Year), lit!("5")]; year: 1234); + check!("12345", [num0!(Year), lit!("5")]; year: 1234); + check!("12341234", [num!(Year), num!(Year)]; year: 1234); + check!("1234 1234", [num!(Year), num!(Year)]; year: 1234); + check!("1234 1235", [num!(Year), num!(Year)]; IMPOSSIBLE); + check!("1234 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); + check!("1234x1234", [num!(Year), lit!("x"), num!(Year)]; year: 1234); + check!("1234xx1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); + check!("1234 x 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID); + + // signed numeric + check!("-42", [num!(Year)]; year: -42); + check!("+42", [num!(Year)]; year: 42); + check!("-0042", [num!(Year)]; year: -42); + check!("+0042", [num!(Year)]; year: 42); + check!("-42195", [num!(Year)]; year: -42195); + check!("+42195", [num!(Year)]; year: 42195); + check!(" -42195", [num!(Year)]; year: -42195); + check!(" +42195", [num!(Year)]; year: 42195); + check!(" - 42", [num!(Year)]; INVALID); + check!(" + 42", [num!(Year)]; INVALID); + check!("-", [num!(Year)]; TOO_SHORT); + check!("+", [num!(Year)]; TOO_SHORT); + + // unsigned numeric + check!("345", [num!(Ordinal)]; ordinal: 345); + check!("+345", [num!(Ordinal)]; INVALID); + check!("-345", [num!(Ordinal)]; INVALID); + check!(" 345", [num!(Ordinal)]; ordinal: 345); + check!(" +345", [num!(Ordinal)]; INVALID); + check!(" -345", [num!(Ordinal)]; INVALID); + + // various numeric fields + check!("1234 5678", + [num!(Year), num!(IsoYear)]; + year: 1234, isoyear: 5678); + check!("12 34 56 78", + [num!(YearDiv100), num!(YearMod100), num!(IsoYearDiv100), num!(IsoYearMod100)]; + year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78); + check!("1 2 3 4 5 6", + [num!(Month), num!(Day), num!(WeekFromSun), num!(WeekFromMon), num!(IsoWeek), + num!(NumDaysFromSun)]; + month: 1, day: 2, week_from_sun: 3, week_from_mon: 4, isoweek: 5, weekday: Weekday::Sat); + check!("7 89 01", + [num!(WeekdayFromMon), num!(Ordinal), num!(Hour12)]; + weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1); + check!("23 45 6 78901234 567890123", + [num!(Hour), num!(Minute), num!(Second), num!(Nanosecond), num!(Timestamp)]; + hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234, + timestamp: 567_890_123); + + // fixed: month and weekday names + check!("apr", [fix!(ShortMonthName)]; month: 4); + check!("Apr", [fix!(ShortMonthName)]; month: 4); + check!("APR", [fix!(ShortMonthName)]; month: 4); + check!("ApR", [fix!(ShortMonthName)]; month: 4); + check!("April", [fix!(ShortMonthName)]; TOO_LONG); // `Apr` is parsed + check!("A", [fix!(ShortMonthName)]; TOO_SHORT); + check!("Sol", [fix!(ShortMonthName)]; INVALID); + check!("Apr", [fix!(LongMonthName)]; month: 4); + check!("Apri", [fix!(LongMonthName)]; TOO_LONG); // `Apr` is parsed + check!("April", [fix!(LongMonthName)]; month: 4); + check!("Aprill", [fix!(LongMonthName)]; TOO_LONG); + check!("Aprill", [fix!(LongMonthName), lit!("l")]; month: 4); + check!("Aprl", [fix!(LongMonthName), lit!("l")]; month: 4); + check!("April", [fix!(LongMonthName), lit!("il")]; TOO_SHORT); // do not backtrack + check!("thu", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu); + check!("Thu", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu); + check!("THU", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu); + check!("tHu", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu); + check!("Thursday", [fix!(ShortWeekdayName)]; TOO_LONG); // `Thu` is parsed + check!("T", [fix!(ShortWeekdayName)]; TOO_SHORT); + check!("The", [fix!(ShortWeekdayName)]; INVALID); + check!("Nop", [fix!(ShortWeekdayName)]; INVALID); + check!("Thu", [fix!(LongWeekdayName)]; weekday: Weekday::Thu); + check!("Thur", [fix!(LongWeekdayName)]; TOO_LONG); // `Thu` is parsed + check!("Thurs", [fix!(LongWeekdayName)]; TOO_LONG); // ditto + check!("Thursday", [fix!(LongWeekdayName)]; weekday: Weekday::Thu); + check!("Thursdays", [fix!(LongWeekdayName)]; TOO_LONG); + check!("Thursdays", [fix!(LongWeekdayName), lit!("s")]; weekday: Weekday::Thu); + check!("Thus", [fix!(LongWeekdayName), lit!("s")]; weekday: Weekday::Thu); + check!("Thursday", [fix!(LongWeekdayName), lit!("rsday")]; TOO_SHORT); // do not backtrack + + // fixed: am/pm + check!("am", [fix!(LowerAmPm)]; hour_div_12: 0); + check!("pm", [fix!(LowerAmPm)]; hour_div_12: 1); + check!("AM", [fix!(LowerAmPm)]; hour_div_12: 0); + check!("PM", [fix!(LowerAmPm)]; hour_div_12: 1); + check!("am", [fix!(UpperAmPm)]; hour_div_12: 0); + check!("pm", [fix!(UpperAmPm)]; hour_div_12: 1); + check!("AM", [fix!(UpperAmPm)]; hour_div_12: 0); + check!("PM", [fix!(UpperAmPm)]; hour_div_12: 1); + check!("Am", [fix!(LowerAmPm)]; hour_div_12: 0); + check!(" Am", [fix!(LowerAmPm)]; INVALID); + check!("ame", [fix!(LowerAmPm)]; TOO_LONG); // `am` is parsed + check!("a", [fix!(LowerAmPm)]; TOO_SHORT); + check!("p", [fix!(LowerAmPm)]; TOO_SHORT); + check!("x", [fix!(LowerAmPm)]; TOO_SHORT); + check!("xx", [fix!(LowerAmPm)]; INVALID); + check!("", [fix!(LowerAmPm)]; TOO_SHORT); + + // fixed: dot plus nanoseconds + check!("", [fix!(Nanosecond)]; ); // no field set, but not an error + check!("4", [fix!(Nanosecond)]; TOO_LONG); // never consumes `4` + check!("4", [fix!(Nanosecond), num!(Second)]; second: 4); + check!(".0", [fix!(Nanosecond)]; nanosecond: 0); + check!(".4", [fix!(Nanosecond)]; nanosecond: 400_000_000); + check!(".42", [fix!(Nanosecond)]; nanosecond: 420_000_000); + check!(".421", [fix!(Nanosecond)]; nanosecond: 421_000_000); + check!(".42195", [fix!(Nanosecond)]; nanosecond: 421_950_000); + check!(".421950803", [fix!(Nanosecond)]; nanosecond: 421_950_803); + check!(".421950803547", [fix!(Nanosecond)]; nanosecond: 421_950_803); + check!(".000000003547", [fix!(Nanosecond)]; nanosecond: 3); + check!(".000000000547", [fix!(Nanosecond)]; nanosecond: 0); + check!(".", [fix!(Nanosecond)]; TOO_SHORT); + check!(".4x", [fix!(Nanosecond)]; TOO_LONG); + check!(". 4", [fix!(Nanosecond)]; INVALID); + check!(" .4", [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming + + // fixed: nanoseconds without the dot + check!("", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("0", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("4", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("42", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("421", [internal_fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000); + check!("42143", [internal_fix!(Nanosecond3NoDot), num!(Second)]; nanosecond: 421_000_000, second: 43); + check!("42195", [internal_fix!(Nanosecond3NoDot)]; TOO_LONG); + check!("4x", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!(" 4", [internal_fix!(Nanosecond3NoDot)]; INVALID); + check!(".421", [internal_fix!(Nanosecond3NoDot)]; INVALID); + + check!("", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("0", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("42195", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("421950", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000); + check!("000003", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 3000); + check!("000000", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 0); + check!("4x", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!(" 4", [internal_fix!(Nanosecond6NoDot)]; INVALID); + check!(".42100", [internal_fix!(Nanosecond6NoDot)]; INVALID); + + check!("", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT); + check!("42195", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT); + check!("421950803", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803); + check!("000000003", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 3); + check!("42195080354", [internal_fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9 + check!("421950803547", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG); + check!("000000000", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 0); + check!("00000000x", [internal_fix!(Nanosecond9NoDot)]; INVALID); + check!(" 4", [internal_fix!(Nanosecond9NoDot)]; INVALID); + check!(".42100000", [internal_fix!(Nanosecond9NoDot)]; INVALID); + + // fixed: timezone offsets + check!("+00:00", [fix!(TimezoneOffset)]; offset: 0); + check!("-00:00", [fix!(TimezoneOffset)]; offset: 0); + check!("+00:01", [fix!(TimezoneOffset)]; offset: 60); + check!("-00:01", [fix!(TimezoneOffset)]; offset: -60); + check!("+00:30", [fix!(TimezoneOffset)]; offset: 30 * 60); + check!("-00:30", [fix!(TimezoneOffset)]; offset: -30 * 60); + check!("+04:56", [fix!(TimezoneOffset)]; offset: 296 * 60); + check!("-04:56", [fix!(TimezoneOffset)]; offset: -296 * 60); + check!("+24:00", [fix!(TimezoneOffset)]; offset: 24 * 60 * 60); + check!("-24:00", [fix!(TimezoneOffset)]; offset: -24 * 60 * 60); + check!("+99:59", [fix!(TimezoneOffset)]; offset: (100 * 60 - 1) * 60); + check!("-99:59", [fix!(TimezoneOffset)]; offset: -(100 * 60 - 1) * 60); + check!("+00:59", [fix!(TimezoneOffset)]; offset: 59 * 60); + check!("+00:60", [fix!(TimezoneOffset)]; OUT_OF_RANGE); + check!("+00:99", [fix!(TimezoneOffset)]; OUT_OF_RANGE); + check!("#12:34", [fix!(TimezoneOffset)]; INVALID); + check!("12:34", [fix!(TimezoneOffset)]; INVALID); + check!("+12:34 ", [fix!(TimezoneOffset)]; TOO_LONG); + check!(" +12:34", [fix!(TimezoneOffset)]; offset: 754 * 60); + check!("\t -12:34", [fix!(TimezoneOffset)]; offset: -754 * 60); + check!("", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+1", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+12", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+123", [fix!(TimezoneOffset)]; TOO_SHORT); + check!("+1234", [fix!(TimezoneOffset)]; offset: 754 * 60); + check!("+12345", [fix!(TimezoneOffset)]; TOO_LONG); + check!("+12345", [fix!(TimezoneOffset), num!(Day)]; offset: 754 * 60, day: 5); + check!("Z", [fix!(TimezoneOffset)]; INVALID); + check!("z", [fix!(TimezoneOffset)]; INVALID); + check!("Z", [fix!(TimezoneOffsetZ)]; offset: 0); + check!("z", [fix!(TimezoneOffsetZ)]; offset: 0); + check!("Y", [fix!(TimezoneOffsetZ)]; INVALID); + check!("Zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0); + check!("zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0); + check!("+1234ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60); + check!("+12:34ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60); + check!("Z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); + check!("z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0); + check!("+12:00", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60); + check!("+12", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60); + check!("CEST 5", [fix!(TimezoneName), lit!(" "), num!(Day)]; day: 5); + + // some practical examples + check!("2015-02-04T14:37:05+09:00", + [num!(Year), lit!("-"), num!(Month), lit!("-"), num!(Day), lit!("T"), + num!(Hour), lit!(":"), num!(Minute), lit!(":"), num!(Second), fix!(TimezoneOffset)]; + year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, + minute: 37, second: 5, offset: 32400); + check!("20150204143705567", + [num!(Year), num!(Month), num!(Day), + num!(Hour), num!(Minute), num!(Second), internal_fix!(Nanosecond3NoDot)]; + year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, + minute: 37, second: 5, nanosecond: 567000000); + check!("Mon, 10 Jun 2013 09:32:37 GMT", + [fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "), + fix!(ShortMonthName), sp!(" "), num!(Year), sp!(" "), num!(Hour), lit!(":"), + num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")]; + year: 2013, month: 6, day: 10, weekday: Weekday::Mon, + hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37); + check!("Sun Aug 02 13:39:15 CEST 2020", + [fix!(ShortWeekdayName), sp!(" "), fix!(ShortMonthName), sp!(" "), + num!(Day), sp!(" "), num!(Hour), lit!(":"), num!(Minute), lit!(":"), + num!(Second), sp!(" "), fix!(TimezoneName), sp!(" "), num!(Year)]; + year: 2020, month: 8, day: 2, weekday: Weekday::Sun, + hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15); + check!("20060102150405", + [num!(Year), num!(Month), num!(Day), num!(Hour), num!(Minute), num!(Second)]; + year: 2006, month: 1, day: 2, hour_div_12: 1, hour_mod_12: 3, minute: 4, second: 5); + check!("3:14PM", + [num!(Hour12), lit!(":"), num!(Minute), fix!(LowerAmPm)]; + hour_div_12: 1, hour_mod_12: 3, minute: 14); + check!("12345678901234.56789", + [num!(Timestamp), lit!("."), num!(Nanosecond)]; + nanosecond: 56_789, timestamp: 12_345_678_901_234); + check!("12345678901234.56789", + [num!(Timestamp), fix!(Nanosecond)]; + nanosecond: 567_890_000, timestamp: 12_345_678_901_234); +} + +#[cfg(test)] +#[test] +fn test_rfc2822() { + use super::NOT_ENOUGH; + use super::*; + use offset::FixedOffset; + use DateTime; + + // Test data - (input, Ok(expected result after parse and format) or Err(error code)) + let testdates = [ + ("Tue, 20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // normal case + ("Fri, 2 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // folding whitespace + ("Fri, 02 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // leading zero + ("20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // no day of week + ("20 JAN 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // upper case month + ("Tue, 20 Jan 2015 17:35 -0800", Ok("Tue, 20 Jan 2015 17:35:00 -0800")), // no second + ("11 Sep 2001 09:45:00 EST", Ok("Tue, 11 Sep 2001 09:45:00 -0500")), + ("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month + ("Tue, 20 Jan 2015", Err(TOO_SHORT)), // omitted fields + ("Tue, 20 Avr 2015 17:35:20 -0800", Err(INVALID)), // bad month name + ("Tue, 20 Jan 2015 25:35:20 -0800", Err(OUT_OF_RANGE)), // bad hour + ("Tue, 20 Jan 2015 7:35:20 -0800", Err(INVALID)), // bad # of digits in hour + ("Tue, 20 Jan 2015 17:65:20 -0800", Err(OUT_OF_RANGE)), // bad minute + ("Tue, 20 Jan 2015 17:35:90 -0800", Err(OUT_OF_RANGE)), // bad second + ("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset + ("6 Jun 1944 04:00:00Z", Err(INVALID)), // bad offset (zulu not allowed) + ("Tue, 20 Jan 2015 17:35:20 HAS", Err(NOT_ENOUGH)), // bad named time zone + ]; + + fn rfc2822_to_datetime(date: &str) -> ParseResult> { + let mut parsed = Parsed::new(); + parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?; + parsed.to_datetime() + } + + fn fmt_rfc2822_datetime(dt: DateTime) -> String { + dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter()).to_string() + } + + // Test against test data above + for &(date, checkdate) in testdates.iter() { + let d = rfc2822_to_datetime(date); // parse a date + let dt = match d { + // did we get a value? + Ok(dt) => Ok(fmt_rfc2822_datetime(dt)), // yes, go on + Err(e) => Err(e), // otherwise keep an error for the comparison + }; + if dt != checkdate.map(|s| s.to_string()) { + // check for expected result + panic!( + "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}", + date, dt, checkdate + ); + } + } +} + +#[cfg(test)] +#[test] +fn parse_rfc850() { + use {TimeZone, Utc}; + + 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); + + // 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)); + + // 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"), + ]; + + for val in &testdates { + assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT)); + } +} + +#[cfg(test)] +#[test] +fn test_rfc3339() { + use super::*; + use offset::FixedOffset; + use DateTime; + + // Test data - (input, Ok(expected result after parse and format) or Err(error code)) + let testdates = [ + ("2015-01-20T17:35:20-08:00", Ok("2015-01-20T17:35:20-08:00")), // normal case + ("1944-06-06T04:04:00Z", Ok("1944-06-06T04:04:00+00:00")), // D-day + ("2001-09-11T09:45:00-08:00", Ok("2001-09-11T09:45:00-08:00")), + ("2015-01-20T17:35:20.001-08:00", Ok("2015-01-20T17:35:20.001-08:00")), + ("2015-01-20T17:35:20.000031-08:00", Ok("2015-01-20T17:35:20.000031-08:00")), + ("2015-01-20T17:35:20.000000004-08:00", Ok("2015-01-20T17:35:20.000000004-08:00")), + ("2015-01-20T17:35:20.000000000452-08:00", Ok("2015-01-20T17:35:20-08:00")), // too small + ("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month + ("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour + ("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute + ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second + ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset + ]; + + fn rfc3339_to_datetime(date: &str) -> ParseResult> { + let mut parsed = Parsed::new(); + parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter())?; + parsed.to_datetime() + } + + fn fmt_rfc3339_datetime(dt: DateTime) -> String { + dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter()).to_string() + } + + // Test against test data above + for &(date, checkdate) in testdates.iter() { + let d = rfc3339_to_datetime(date); // parse a date + let dt = match d { + // did we get a value? + Ok(dt) => Ok(fmt_rfc3339_datetime(dt)), // yes, go on + Err(e) => Err(e), // otherwise keep an error for the comparison + }; + if dt != checkdate.map(|s| s.to_string()) { + // check for expected result + panic!( + "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}", + date, dt, checkdate + ); + } + } +} diff --git a/src/format/parsed.rs b/src/format/parsed.rs new file mode 100644 index 0000000..b8ed2d9 --- /dev/null +++ b/src/format/parsed.rs @@ -0,0 +1,1283 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! A collection of parsed date and time items. +//! They can be constructed incrementally while being checked for consistency. + +use num_traits::ToPrimitive; +use oldtime::Duration as OldDuration; + +use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; +use div::div_rem; +use naive::{NaiveDate, NaiveDateTime, NaiveTime}; +use offset::{FixedOffset, LocalResult, Offset, TimeZone}; +use DateTime; +use Weekday; +use {Datelike, Timelike}; + +/// Parsed parts of date and time. There are two classes of methods: +/// +/// - `set_*` methods try to set given field(s) while checking for the consistency. +/// It may or may not check for the range constraint immediately (for efficiency reasons). +/// +/// - `to_*` methods try to make a concrete date and time value out of set fields. +/// It fully checks any remaining out-of-range conditions and inconsistent/impossible fields. +#[allow(missing_copy_implementations)] +#[derive(Clone, PartialEq, Debug)] +pub struct Parsed { + /// Year. + /// + /// This can be negative unlike [`year_div_100`](#structfield.year_div_100) + /// and [`year_mod_100`](#structfield.year_mod_100) fields. + pub year: Option, + + /// Year divided by 100. Implies that the year is >= 1 BCE when set. + /// + /// Due to the common usage, if this field is missing but + /// [`year_mod_100`](#structfield.year_mod_100) is present, + /// it is inferred to 19 when `year_mod_100 >= 70` and 20 otherwise. + pub year_div_100: Option, + + /// Year modulo 100. Implies that the year is >= 1 BCE when set. + pub year_mod_100: Option, + + /// 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/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 + /// [`isoyear_mod_100`](#structfield.isoyear_mod_100) is present, + /// 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/struct.NaiveDate.html#week-date), modulo 100. + /// Implies that the year is >= 1 BCE when set. + pub isoyear_mod_100: Option, + + /// Month (1--12). + pub month: Option, + + /// Week number, where the week 1 starts at the first Sunday of January + /// (0--53, 1--53 or 1--52 depending on the year). + pub week_from_sun: Option, + + /// Week number, where the week 1 starts at the first Monday of January + /// (0--53, 1--53 or 1--52 depending on the year). + pub week_from_mon: Option, + + /// [ISO week number](../naive/struct.NaiveDate.html#week-date) + /// (1--52 or 1--53 depending on the year). + pub isoweek: Option, + + /// Day of the week. + pub weekday: Option, + + /// Day of the year (1--365 or 1--366 depending on the year). + pub ordinal: Option, + + /// Day of the month (1--28, 1--29, 1--30 or 1--31 depending on the month). + pub day: Option, + + /// Hour number divided by 12 (0--1). 0 indicates AM and 1 indicates PM. + pub hour_div_12: Option, + + /// Hour number modulo 12 (0--11). + pub hour_mod_12: Option, + + /// Minute number (0--59). + pub minute: Option, + + /// Second number (0--60, accounting for leap seconds). + pub second: Option, + + /// The number of nanoseconds since the whole second (0--999,999,999). + pub nanosecond: Option, + + /// The number of non-leap seconds since the midnight UTC on January 1, 1970. + /// + /// This can be off by one if [`second`](#structfield.second) is 60 (a leap second). + pub timestamp: Option, + + /// 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 as `new` (i.e. "consistent"), +/// and if it is empty, set `old` to `new` as well. +#[inline] +fn set_if_consistent(old: &mut Option, new: T) -> ParseResult<()> { + if let Some(ref old) = *old { + if *old == new { + Ok(()) + } else { + Err(IMPOSSIBLE) + } + } else { + *old = Some(new); + Ok(()) + } +} + +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::default() + } + + /// Tries to set the [`year`](#structfield.year) field from given value. + #[inline] + pub fn set_year(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.year, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value. + #[inline] + pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> { + if value < 0 { + return Err(OUT_OF_RANGE); + } + set_if_consistent(&mut self.year_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value. + #[inline] + pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> { + if value < 0 { + return Err(OUT_OF_RANGE); + } + set_if_consistent(&mut self.year_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`isoyear`](#structfield.isoyear) field from given value. + #[inline] + pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.isoyear, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value. + #[inline] + pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> { + if value < 0 { + return Err(OUT_OF_RANGE); + } + set_if_consistent(&mut self.isoyear_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value. + #[inline] + pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> { + if value < 0 { + return Err(OUT_OF_RANGE); + } + set_if_consistent(&mut self.isoyear_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`month`](#structfield.month) field from given value. + #[inline] + pub fn set_month(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.month, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value. + #[inline] + pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.week_from_sun, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value. + #[inline] + pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.week_from_mon, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`isoweek`](#structfield.isoweek) field from given value. + #[inline] + pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.isoweek, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`weekday`](#structfield.weekday) field from given value. + #[inline] + pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> { + set_if_consistent(&mut self.weekday, value) + } + + /// Tries to set the [`ordinal`](#structfield.ordinal) field from given value. + #[inline] + pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.ordinal, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`day`](#structfield.day) field from given value. + #[inline] + pub fn set_day(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.day, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value. + /// (`false` for AM, `true` for PM) + #[inline] + pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { + set_if_consistent(&mut self.hour_div_12, if value { 1 } else { 0 }) + } + + /// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from + /// given hour number in 12-hour clocks. + #[inline] + pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> { + if value < 1 || value > 12 { + return Err(OUT_OF_RANGE); + } + set_if_consistent(&mut self.hour_mod_12, value as u32 % 12) + } + + /// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and + /// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value. + #[inline] + pub fn set_hour(&mut self, value: i64) -> ParseResult<()> { + let v = value.to_u32().ok_or(OUT_OF_RANGE)?; + set_if_consistent(&mut self.hour_div_12, v / 12)?; + set_if_consistent(&mut self.hour_mod_12, v % 12)?; + Ok(()) + } + + /// Tries to set the [`minute`](#structfield.minute) field from given value. + #[inline] + pub fn set_minute(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.minute, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`second`](#structfield.second) field from given value. + #[inline] + pub fn set_second(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.second, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value. + #[inline] + pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.nanosecond, value.to_u32().ok_or(OUT_OF_RANGE)?) + } + + /// Tries to set the [`timestamp`](#structfield.timestamp) field from given value. + #[inline] + pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.timestamp, value) + } + + /// Tries to set the [`offset`](#structfield.offset) field from given value. + #[inline] + pub fn set_offset(&mut self, value: i64) -> ParseResult<()> { + set_if_consistent(&mut self.offset, value.to_i32().ok_or(OUT_OF_RANGE)?) + } + + /// Returns a parsed naive date out of given fields. + /// + /// This method is able to determine the date from given subset of fields: + /// + /// - Year, month, day. + /// - Year, day of the year (ordinal). + /// - Year, week number counted from Sunday or Monday, day of the week. + /// - ISO week date. + /// + /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted, + /// the two-digit year is used to guess the century number then. + pub fn to_naive_date(&self) -> ParseResult { + fn resolve_year( + y: Option, + q: Option, + r: Option, + ) -> ParseResult> { + match (y, q, r) { + // if there is no further information, simply return the given full year. + // this is a common case, so let's avoid division here. + (y, None, None) => Ok(y), + + // if there is a full year *and* also quotient and/or modulo, + // check if present quotient and/or modulo is consistent to the full year. + // since the presence of those fields means a positive full year, + // we should filter a negative full year first. + (Some(y), q, r @ Some(0...99)) | (Some(y), q, r @ None) => { + if y < 0 { + return Err(OUT_OF_RANGE); + } + let (q_, r_) = div_rem(y, 100); + if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ { + Ok(Some(y)) + } else { + Err(IMPOSSIBLE) + } + } + + // the full year is missing but we have quotient and modulo. + // reconstruct the full year. make sure that the result is always positive. + (None, Some(q), Some(r @ 0...99)) => { + if q < 0 { + return Err(OUT_OF_RANGE); + } + let y = q.checked_mul(100).and_then(|v| v.checked_add(r)); + Ok(Some(y.ok_or(OUT_OF_RANGE)?)) + } + + // we only have modulo. try to interpret a modulo as a conventional two-digit year. + // note: we are affected by Rust issue #18060. avoid multiple range patterns. + (None, None, Some(r @ 0...99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })), + + // otherwise it is an out-of-bound or insufficient condition. + (None, Some(_), None) => Err(NOT_ENOUGH), + (_, _, Some(_)) => Err(OUT_OF_RANGE), + } + } + + let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?; + let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?; + + // verify the normal year-month-day date. + let verify_ymd = |date: NaiveDate| { + let year = date.year(); + let (year_div_100, year_mod_100) = if year >= 0 { + let (q, r) = div_rem(year, 100); + (Some(q), Some(r)) + } else { + (None, None) // they should be empty to be consistent + }; + let month = date.month(); + let day = date.day(); + self.year.unwrap_or(year) == year + && self.year_div_100.or(year_div_100) == year_div_100 + && self.year_mod_100.or(year_mod_100) == year_mod_100 + && self.month.unwrap_or(month) == month + && self.day.unwrap_or(day) == day + }; + + // verify the ISO week date. + let verify_isoweekdate = |date: NaiveDate| { + 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)) + } else { + (None, None) // they should be empty to be consistent + }; + self.isoyear.unwrap_or(isoyear) == isoyear + && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100 + && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100 + && self.isoweek.unwrap_or(isoweek) == isoweek + && self.weekday.unwrap_or(weekday) == weekday + }; + + // verify the ordinal and other (non-ISO) week dates. + let verify_ordinal = |date: NaiveDate| { + let ordinal = date.ordinal(); + let weekday = date.weekday(); + let week_from_sun = (ordinal as i32 - weekday.num_days_from_sunday() as i32 + 7) / 7; + let week_from_mon = (ordinal as i32 - weekday.num_days_from_monday() as i32 + 7) / 7; + self.ordinal.unwrap_or(ordinal) == ordinal + && self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun + && self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon + }; + + // test several possibilities. + // tries to construct a full `NaiveDate` as much as possible, then verifies that + // it is consistent with other given fields. + let (verified, parsed_date) = match (given_year, given_isoyear, self) { + (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => { + // year, month, day + let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?; + (verify_isoweekdate(date) && verify_ordinal(date), date) + } + + (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => { + // year, day of the year + let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?; + (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) + } + + ( + Some(year), + _, + &Parsed { week_from_sun: Some(week_from_sun), weekday: Some(weekday), .. }, + ) => { + // year, week (starting at 1st Sunday), day of the week + let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; + let firstweek = match newyear.weekday() { + Weekday::Sun => 0, + Weekday::Mon => 6, + Weekday::Tue => 5, + Weekday::Wed => 4, + Weekday::Thu => 3, + Weekday::Fri => 2, + Weekday::Sat => 1, + }; + + // `firstweek+1`-th day of January is the beginning of the week 1. + 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 = newyear + .checked_add_signed(OldDuration::days(i64::from(ndays))) + .ok_or(OUT_OF_RANGE)?; + if date.year() != year { + return Err(OUT_OF_RANGE); + } // early exit for correct error + + (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) + } + + ( + Some(year), + _, + &Parsed { week_from_mon: Some(week_from_mon), weekday: Some(weekday), .. }, + ) => { + // year, week (starting at 1st Monday), day of the week + let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; + let firstweek = match newyear.weekday() { + Weekday::Sun => 1, + Weekday::Mon => 0, + Weekday::Tue => 6, + Weekday::Wed => 5, + Weekday::Thu => 4, + Weekday::Fri => 3, + Weekday::Sat => 2, + }; + + // `firstweek+1`-th day of January is the beginning of the week 1. + 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 = newyear + .checked_add_signed(OldDuration::days(i64::from(ndays))) + .ok_or(OUT_OF_RANGE)?; + if date.year() != year { + return Err(OUT_OF_RANGE); + } // early exit for correct error + + (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) + } + + (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => { + // ISO year, week, day of the week + let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday); + let date = date.ok_or(OUT_OF_RANGE)?; + (verify_ymd(date) && verify_ordinal(date), date) + } + + (_, _, _) => return Err(NOT_ENOUGH), + }; + + if verified { + Ok(parsed_date) + } else { + Err(IMPOSSIBLE) + } + } + + /// Returns a parsed naive time out of given fields. + /// + /// This method is able to determine the time from given subset of fields: + /// + /// - Hour, minute. (second and nanosecond assumed to be 0) + /// - Hour, minute, second. (nanosecond assumed to be 0) + /// - Hour, minute, second, nanosecond. + /// + /// It is able to handle leap seconds when given second is 60. + pub fn to_naive_time(&self) -> ParseResult { + let hour_div_12 = match self.hour_div_12 { + Some(v @ 0...1) => v, + Some(_) => return Err(OUT_OF_RANGE), + None => return Err(NOT_ENOUGH), + }; + let hour_mod_12 = match self.hour_mod_12 { + Some(v @ 0...11) => v, + Some(_) => return Err(OUT_OF_RANGE), + None => return Err(NOT_ENOUGH), + }; + let hour = hour_div_12 * 12 + hour_mod_12; + + let minute = match self.minute { + Some(v @ 0...59) => v, + Some(_) => return Err(OUT_OF_RANGE), + None => return Err(NOT_ENOUGH), + }; + + // we allow omitting seconds or nanoseconds, but they should be in the range. + let (second, mut nano) = match self.second.unwrap_or(0) { + v @ 0...59 => (v, 0), + 60 => (59, 1_000_000_000), + _ => return Err(OUT_OF_RANGE), + }; + nano += match self.nanosecond { + Some(v @ 0...999_999_999) if self.second.is_some() => v, + Some(0...999_999_999) => return Err(NOT_ENOUGH), // second is missing + Some(_) => return Err(OUT_OF_RANGE), + None => 0, + }; + + NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE) + } + + /// Returns a parsed naive date and time out of given fields, + /// except for the [`offset`](#structfield.offset) field (assumed to have a given value). + /// This is required for parsing a local time or other known-timezone inputs. + /// + /// This method is able to determine the combined date and time + /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field. + /// Either way those fields have to be consistent to each other. + pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult { + let date = self.to_naive_date(); + let time = self.to_naive_time(); + if let (Ok(date), Ok(time)) = (date, time) { + let datetime = date.and_time(time); + + // verify the timestamp field if any + // the following is safe, `timestamp` is very limited in range + let timestamp = datetime.timestamp() - i64::from(offset); + if let Some(given_timestamp) = self.timestamp { + // if `datetime` represents a leap second, it might be off by one second. + if given_timestamp != timestamp + && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1) + { + return Err(IMPOSSIBLE); + } + } + + Ok(datetime) + } else if let Some(timestamp) = self.timestamp { + use super::ParseError as PE; + use super::ParseErrorKind::{Impossible, OutOfRange}; + + // if date and time is problematic already, there is no point proceeding. + // we at least try to give a correct error though. + match (date, time) { + (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE), + (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE), + (_, _) => {} // one of them is insufficient + } + + // reconstruct date and time fields from timestamp + let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?; + let datetime = NaiveDateTime::from_timestamp_opt(ts, 0); + let mut datetime = datetime.ok_or(OUT_OF_RANGE)?; + + // fill year, ordinal, hour, minute and second fields from timestamp. + // if existing fields are consistent, this will allow the full date/time reconstruction. + let mut parsed = self.clone(); + if parsed.second == Some(60) { + // `datetime.second()` cannot be 60, so this is the only case for a leap second. + match datetime.second() { + // it's okay, just do not try to overwrite the existing field. + 59 => {} + // `datetime` is known to be off by one second. + 0 => { + datetime -= OldDuration::seconds(1); + } + // otherwise it is impossible. + _ => return Err(IMPOSSIBLE), + } + // ...and we have the correct candidates for other fields. + } else { + parsed.set_second(i64::from(datetime.second()))?; + } + parsed.set_year(i64::from(datetime.year()))?; + parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd + parsed.set_hour(i64::from(datetime.hour()))?; + parsed.set_minute(i64::from(datetime.minute()))?; + + // validate other fields (e.g. week) and return + let date = parsed.to_naive_date()?; + let time = parsed.to_naive_time()?; + Ok(date.and_time(time)) + } else { + // reproduce the previous error(s) + date?; + time?; + unreachable!() + } + } + + /// Returns a parsed fixed time zone offset out of given fields. + pub fn to_fixed_offset(&self) -> ParseResult { + self.offset.and_then(FixedOffset::east_opt).ok_or(OUT_OF_RANGE) + } + + /// Returns a parsed timezone-aware date and time out of given fields. + /// + /// This method is able to determine the combined date and time + /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field, + /// plus a time zone offset. + /// Either way those fields have to be consistent to each other. + pub fn to_datetime(&self) -> ParseResult> { + let offset = self.offset.ok_or(NOT_ENOUGH)?; + let datetime = self.to_naive_datetime_with_offset(offset)?; + let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; + match offset.from_local_datetime(&datetime) { + LocalResult::None => Err(IMPOSSIBLE), + LocalResult::Single(t) => Ok(t), + LocalResult::Ambiguous(..) => Err(NOT_ENOUGH), + } + } + + /// Returns a parsed timezone-aware date and time out of given fields, + /// with an additional `TimeZone` used to interpret and validate the local date. + /// + /// This method is able to determine the combined date and time + /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field, + /// plus a time zone offset. + /// Either way those fields have to be consistent to each other. + /// If parsed fields include an UTC offset, it also has to be consistent to + /// [`offset`](#structfield.offset). + pub fn to_datetime_with_timezone(&self, tz: &Tz) -> ParseResult> { + // if we have `timestamp` specified, guess an offset from that. + let mut guessed_offset = 0; + if let Some(timestamp) = self.timestamp { + // make a naive `DateTime` from given timestamp and (if any) nanosecond. + // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine. + let nanosecond = self.nanosecond.unwrap_or(0); + let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond); + let dt = dt.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 { + dt.offset().fix().local_minus_utc() == offset + } else { + true + } + }; + + // `guessed_offset` should be correct when `self.timestamp` is given. + // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case. + let datetime = self.to_naive_datetime_with_offset(guessed_offset)?; + match tz.from_local_datetime(&datetime) { + LocalResult::None => Err(IMPOSSIBLE), + LocalResult::Single(t) => { + if check_offset(&t) { + Ok(t) + } else { + Err(IMPOSSIBLE) + } + } + LocalResult::Ambiguous(min, max) => { + // try to disambiguate two possible local dates by offset. + match (check_offset(&min), check_offset(&max)) { + (false, false) => Err(IMPOSSIBLE), + (false, true) => Ok(max), + (true, false) => Ok(min), + (true, true) => Err(NOT_ENOUGH), + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; + use super::Parsed; + use naive::{NaiveDate, NaiveTime, MAX_DATE, MIN_DATE}; + use offset::{FixedOffset, TimeZone, Utc}; + use Datelike; + use Weekday::*; + + #[test] + fn test_parsed_set_fields() { + // year*, isoyear* + let mut p = Parsed::new(); + assert_eq!(p.set_year(1987), Ok(())); + assert_eq!(p.set_year(1986), Err(IMPOSSIBLE)); + assert_eq!(p.set_year(1988), Err(IMPOSSIBLE)); + assert_eq!(p.set_year(1987), Ok(())); + assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year` + assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE)); + assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE)); + assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto + assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE)); + assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE)); + + let mut p = Parsed::new(); + assert_eq!(p.set_year(0), Ok(())); + assert_eq!(p.set_year_div_100(0), Ok(())); + assert_eq!(p.set_year_mod_100(0), Ok(())); + + let mut p = Parsed::new(); + assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE)); + assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE)); + assert_eq!(p.set_year(-1), Ok(())); + assert_eq!(p.set_year(-2), Err(IMPOSSIBLE)); + assert_eq!(p.set_year(0), Err(IMPOSSIBLE)); + + let mut p = Parsed::new(); + assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); + assert_eq!(p.set_year_div_100(8), Ok(())); + assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); + + // month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset + let mut p = Parsed::new(); + assert_eq!(p.set_month(7), Ok(())); + assert_eq!(p.set_month(1), Err(IMPOSSIBLE)); + assert_eq!(p.set_month(6), Err(IMPOSSIBLE)); + assert_eq!(p.set_month(8), Err(IMPOSSIBLE)); + assert_eq!(p.set_month(12), Err(IMPOSSIBLE)); + + let mut p = Parsed::new(); + assert_eq!(p.set_month(8), Ok(())); + assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE)); + + // hour + let mut p = Parsed::new(); + assert_eq!(p.set_hour(12), Ok(())); + assert_eq!(p.set_hour(11), Err(IMPOSSIBLE)); + assert_eq!(p.set_hour(13), Err(IMPOSSIBLE)); + assert_eq!(p.set_hour(12), Ok(())); + assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE)); + assert_eq!(p.set_ampm(true), Ok(())); + assert_eq!(p.set_hour12(12), Ok(())); + assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation + assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE)); + assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE)); + + let mut p = Parsed::new(); + assert_eq!(p.set_ampm(true), Ok(())); + assert_eq!(p.set_hour12(7), Ok(())); + assert_eq!(p.set_hour(7), Err(IMPOSSIBLE)); + assert_eq!(p.set_hour(18), Err(IMPOSSIBLE)); + assert_eq!(p.set_hour(19), Ok(())); + + // timestamp + let mut p = Parsed::new(); + assert_eq!(p.set_timestamp(1_234_567_890), Ok(())); + assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE)); + assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE)); + } + + #[test] + fn test_parsed_to_naive_date() { + macro_rules! parse { + ($($k:ident: $v:expr),*) => ( + Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date() + ) + } + + let ymd = |y, m, d| Ok(NaiveDate::from_ymd(y, m, d)); + + // ymd: omission of fields + assert_eq!(parse!(), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2)); + assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH)); + assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH)); + assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH)); + assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH)); + assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2)); + assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH)); + assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH)); + assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2)); + assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2)); + + // ymd: out-of-range conditions + assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29)); + assert_eq!( + parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31), + ymd(1983, 12, 31) + ); + assert_eq!( + parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1), + Err(OUT_OF_RANGE) + ); + assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), 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 = 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) + ); + assert_eq!( + parse!(year_div_100: (max_year + 1) / 100, + year_mod_100: (max_year + 1) % 100, month: 1, day: 1), + Err(OUT_OF_RANGE) + ); + + // ymd: conflicting inputs + assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1)); + assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE)); + assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1)); + assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE)); + assert_eq!( + parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1), + ymd(1984, 1, 1) + ); + assert_eq!( + parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1), + Err(OUT_OF_RANGE) + ); + assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(OUT_OF_RANGE)); + + // weekdates + assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1)); + assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1)); + assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2)); + assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2)); + assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3)); + assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3)); + assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8)); + assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8)); + assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9)); + assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9)); + assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10)); + assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30)); + assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31)); + assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1)); + + // weekdates: conflicting inputs + assert_eq!( + parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat), + ymd(2000, 1, 8) + ); + assert_eq!( + parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun), + ymd(2000, 1, 9) + ); + assert_eq!( + parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun), + Err(IMPOSSIBLE) + ); + + // ISO weekdates + assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH)); + assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31)); + assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1)); + assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE)); + assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE)); + assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3)); + assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH)); + + // year and ordinal + assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH)); + assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1)); + assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29)); + assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1)); + assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31)); + assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1)); + assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28)); + assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1)); + assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31)); + assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE)); + assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); + + // more complex cases + assert_eq!( + parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1, + week_from_sun: 52, week_from_mon: 52, weekday: Wed), + ymd(2014, 12, 31) + ); + assert_eq!( + parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1, + week_from_sun: 52, week_from_mon: 52), + ymd(2014, 12, 31) + ); + assert_eq!( + parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53, + week_from_sun: 52, week_from_mon: 52, weekday: Wed), + Err(IMPOSSIBLE) + ); // no ISO week date 2014-W53-3 + assert_eq!( + parse!(year: 2012, isoyear: 2015, isoweek: 1, + week_from_sun: 52, week_from_mon: 52), + Err(NOT_ENOUGH) + ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31) + assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH)); + // technically unique (2014-12-31) but Chrono gives up + } + + #[test] + fn test_parsed_to_naive_time() { + macro_rules! parse { + ($($k:ident: $v:expr),*) => ( + Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time() + ) + } + + let hms = |h, m, s| Ok(NaiveTime::from_hms(h, m, s)); + let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano(h, m, s, n)); + + // omission of fields + assert_eq!(parse!(), Err(NOT_ENOUGH)); + assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH)); + assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH)); + assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0)); + assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45)); + assert_eq!( + parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45, + nanosecond: 678_901_234), + hmsn(1, 23, 45, 678_901_234) + ); + assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6)); + assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH)); + assert_eq!( + parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012), + Err(NOT_ENOUGH) + ); + + // out-of-range conditions + assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE)); + assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE)); + assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE)); + assert_eq!( + parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61), + Err(OUT_OF_RANGE) + ); + assert_eq!( + parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34, + nanosecond: 1_000_000_000), + Err(OUT_OF_RANGE) + ); + + // leap seconds + assert_eq!( + parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60), + hmsn(1, 23, 59, 1_000_000_000) + ); + assert_eq!( + parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60, + nanosecond: 999_999_999), + hmsn(1, 23, 59, 1_999_999_999) + ); + } + + #[test] + fn test_parsed_to_naive_datetime_with_offset() { + macro_rules! parse { + (offset = $offset:expr; $($k:ident: $v:expr),*) => ( + Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset) + ); + ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*)) + } + + let ymdhms = |y, m, d, h, n, s| Ok(NaiveDate::from_ymd(y, m, d).and_hms(h, n, s)); + let ymdhmsn = + |y, m, d, h, n, s, nano| Ok(NaiveDate::from_ymd(y, m, d).and_hms_nano(h, n, s, nano)); + + // omission of fields + assert_eq!(parse!(), Err(NOT_ENOUGH)); + assert_eq!( + parse!(year: 2015, month: 1, day: 30, + hour_div_12: 1, hour_mod_12: 2, minute: 38), + ymdhms(2015, 1, 30, 14, 38, 0) + ); + assert_eq!( + parse!(year: 1997, month: 1, day: 30, + hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5), + ymdhms(1997, 1, 30, 14, 38, 5) + ); + assert_eq!( + parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5, + minute: 6, second: 7, nanosecond: 890_123_456), + ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456) + ); + assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0)); + assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1)); + assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1)); + assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40)); + assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44)); + + // full fields + assert_eq!( + parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, + ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, + isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, + hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, + nanosecond: 12_345_678, timestamp: 1_420_000_000), + ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) + ); + assert_eq!( + parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, + ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, + isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, + hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, + nanosecond: 12_345_678, timestamp: 1_419_999_999), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(offset = 32400; + year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, + ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, + isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, + hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, + nanosecond: 12_345_678, timestamp: 1_419_967_600), + ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) + ); + + // more timestamps + let max_days_from_year_1970 = + 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 = + MIN_DATE.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); + assert_eq!( + parse!(timestamp: min_days_from_year_1970.num_seconds()), + 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(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)); + assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59)); + assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); + assert_eq!( + parse!(second: 60, timestamp: 1_341_100_799), + ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) + ); + assert_eq!( + parse!(second: 60, timestamp: 1_341_100_800), + ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) + ); + assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0)); + assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); + assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE)); + + // leap seconds #2: full fields + // we need to have separate tests for them since it uses another control flow. + assert_eq!( + parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, + minute: 59, second: 59, timestamp: 1_341_100_798), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, + minute: 59, second: 59, timestamp: 1_341_100_799), + ymdhms(2012, 6, 30, 23, 59, 59) + ); + assert_eq!( + parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, + minute: 59, second: 59, timestamp: 1_341_100_800), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, + minute: 59, second: 60, timestamp: 1_341_100_799), + ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) + ); + assert_eq!( + parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, + minute: 59, second: 60, timestamp: 1_341_100_800), + ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) + ); + assert_eq!( + parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, + minute: 0, second: 0, timestamp: 1_341_100_800), + ymdhms(2012, 7, 1, 0, 0, 0) + ); + assert_eq!( + parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, + minute: 0, second: 1, timestamp: 1_341_100_800), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, + minute: 59, second: 60, timestamp: 1_341_100_801), + Err(IMPOSSIBLE) + ); + + // error codes + assert_eq!( + parse!(year: 2015, month: 1, day: 20, weekday: Tue, + hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20), + Err(OUT_OF_RANGE) + ); // `hour_div_12` is out of range + } + + #[test] + fn test_parsed_to_datetime() { + macro_rules! parse { + ($($k:ident: $v:expr),*) => ( + Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime() + ) + } + + let ymdhmsn = |y, m, d, h, n, s, nano, off| { + Ok(FixedOffset::east(off).ymd(y, m, d).and_hms_nano(h, n, s, nano)) + }; + + assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH)); + assert_eq!( + parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, + minute: 26, second: 40, nanosecond: 12_345_678), + Err(NOT_ENOUGH) + ); + assert_eq!( + parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, + minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), + ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0) + ); + assert_eq!( + parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, + minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), + ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400) + ); + assert_eq!( + parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1, + minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876), + ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876) + ); + assert_eq!( + parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4, + minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400), + Err(OUT_OF_RANGE) + ); // `FixedOffset` does not support such huge offset + } + + #[test] + fn test_parsed_to_datetime_with_timezone() { + macro_rules! parse { + ($tz:expr; $($k:ident: $v:expr),*) => ( + Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz) + ) + } + + // single result from ymdhms + 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; + year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, + minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(FixedOffset::east(32400); + year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, + minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(FixedOffset::east(32400); + year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, + minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), + Ok(FixedOffset::east(32400).ymd(2014, 12, 31).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), Err(IMPOSSIBLE)); + assert_eq!( + parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0), + Err(IMPOSSIBLE) + ); + assert_eq!( + parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 32400), + Ok(FixedOffset::east(32400).ymd(2014, 12, 31).and_hms(13, 26, 40)) + ); + + // TODO test with a variable time zone (for None and Ambiguous cases) + } +} diff --git a/src/format/scan.rs b/src/format/scan.rs new file mode 100644 index 0000000..0efb1ee --- /dev/null +++ b/src/format/scan.rs @@ -0,0 +1,350 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +/*! + * Various scanning routines for the parser. + */ + +#![allow(deprecated)] + +use super::{ParseResult, INVALID, OUT_OF_RANGE, TOO_SHORT}; +use Weekday; + +/// Returns true when two slices are equal case-insensitively (in ASCII). +/// Assumes that the `pattern` is already converted to lower case. +fn equals(s: &str, pattern: &str) -> bool { + let mut xs = s.as_bytes().iter().map(|&c| match c { + b'A'...b'Z' => c + 32, + _ => c, + }); + let mut ys = pattern.as_bytes().iter().cloned(); + loop { + match (xs.next(), ys.next()) { + (None, None) => return true, + (None, _) | (_, None) => return false, + (Some(x), Some(y)) if x != y => return false, + _ => (), + } + } +} + +/// Tries to parse the non-negative number from `min` to `max` digits. +/// +/// The absence of digits at all is an unconditional error. +/// More than `max` digits are consumed up to the first `max` digits. +/// Any number that does not fit in `i64` is an error. +#[inline] +pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { + assert!(min <= max); + + // We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on + // the first non-numeric byte, which may be another ascii character or beginning of multi-byte + // UTF-8 character. + let bytes = s.as_bytes(); + if bytes.len() < min { + return Err(TOO_SHORT); + } + + let mut n = 0i64; + for (i, c) in bytes.iter().take(max).cloned().enumerate() { + // cloned() = copied() + if c < b'0' || b'9' < c { + if i < min { + return Err(INVALID); + } else { + return Ok((&s[i..], n)); + } + } + + n = match n.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i64)) { + Some(n) => n, + None => return Err(OUT_OF_RANGE), + }; + } + + Ok((&s[::core::cmp::min(max, bytes.len())..], n)) +} + +/// Tries to consume at least one digits as a fractional second. +/// Returns the number of whole nanoseconds (0--999,999,999). +pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> { + // record the number of digits consumed for later scaling. + let origlen = s.len(); + let (s, v) = number(s, 1, 9)?; + let consumed = origlen - s.len(); + + // scale the number accordingly. + static SCALE: [i64; 10] = + [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1]; + let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?; + + // if there are more than 9 digits, skip next digits. + let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9'); + + Ok((s, v)) +} + +/// Tries to consume a fixed number of digits as a fractional second. +/// Returns the number of whole nanoseconds (0--999,999,999). +pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> { + // record the number of digits consumed for later scaling. + let (s, v) = number(s, digits, digits)?; + + // scale the number accordingly. + static SCALE: [i64; 10] = + [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1]; + let v = v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)?; + + Ok((s, v)) +} + +/// Tries to parse the month index (0 through 11) with the first three ASCII letters. +pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> { + if s.len() < 3 { + return Err(TOO_SHORT); + } + let buf = s.as_bytes(); + let month0 = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) { + (b'j', b'a', b'n') => 0, + (b'f', b'e', b'b') => 1, + (b'm', b'a', b'r') => 2, + (b'a', b'p', b'r') => 3, + (b'm', b'a', b'y') => 4, + (b'j', b'u', b'n') => 5, + (b'j', b'u', b'l') => 6, + (b'a', b'u', b'g') => 7, + (b's', b'e', b'p') => 8, + (b'o', b'c', b't') => 9, + (b'n', b'o', b'v') => 10, + (b'd', b'e', b'c') => 11, + _ => return Err(INVALID), + }; + Ok((&s[3..], month0)) +} + +/// Tries to parse the weekday with the first three ASCII letters. +pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> { + if s.len() < 3 { + return Err(TOO_SHORT); + } + let buf = s.as_bytes(); + let weekday = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) { + (b'm', b'o', b'n') => Weekday::Mon, + (b't', b'u', b'e') => Weekday::Tue, + (b'w', b'e', b'd') => Weekday::Wed, + (b't', b'h', b'u') => Weekday::Thu, + (b'f', b'r', b'i') => Weekday::Fri, + (b's', b'a', b't') => Weekday::Sat, + (b's', b'u', b'n') => Weekday::Sun, + _ => return Err(INVALID), + }; + Ok((&s[3..], weekday)) +} + +/// Tries to parse the month index (0 through 11) with short or long month names. +/// It prefers long month names to short month names when both are possible. +pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> { + // lowercased month names, minus first three chars + static LONG_MONTH_SUFFIXES: [&'static str; 12] = + ["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"]; + + let (mut s, month0) = short_month0(s)?; + + // tries to consume the suffix if possible + let suffix = LONG_MONTH_SUFFIXES[month0 as usize]; + if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) { + s = &s[suffix.len()..]; + } + + Ok((s, month0)) +} + +/// Tries to parse the weekday with short or long weekday names. +/// It prefers long weekday names to short weekday names when both are possible. +pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> { + // lowercased weekday names, minus first three chars + static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] = + ["day", "sday", "nesday", "rsday", "day", "urday", "day"]; + + let (mut s, weekday) = short_weekday(s)?; + + // tries to consume the suffix if possible + let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize]; + if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) { + s = &s[suffix.len()..]; + } + + Ok((s, weekday)) +} + +/// Tries to consume exactly one given character. +pub fn char(s: &str, c1: u8) -> ParseResult<&str> { + match s.as_bytes().first() { + Some(&c) if c == c1 => Ok(&s[1..]), + Some(_) => Err(INVALID), + None => Err(TOO_SHORT), + } +} + +/// Tries to consume one or more whitespace. +pub fn space(s: &str) -> ParseResult<&str> { + let s_ = s.trim_left(); + if s_.len() < s.len() { + Ok(s_) + } else if s.is_empty() { + Err(TOO_SHORT) + } else { + Err(INVALID) + } +} + +/// Consumes any number (including zero) of colon or spaces. +pub fn colon_or_space(s: &str) -> ParseResult<&str> { + Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace())) +} + +/// Tries to parse `[-+]\d\d` continued by `\d\d`. Return an offset in seconds if possible. +/// +/// The additional `colon` may be used to parse a mandatory or optional `:` +/// between hours and minutes, and should return either a new suffix or `Err` when parsing fails. +pub fn timezone_offset(s: &str, consume_colon: F) -> ParseResult<(&str, i32)> +where + F: FnMut(&str) -> ParseResult<&str>, +{ + timezone_offset_internal(s, consume_colon, false) +} + +fn timezone_offset_internal( + mut s: &str, + mut consume_colon: F, + allow_missing_minutes: bool, +) -> ParseResult<(&str, i32)> +where + F: FnMut(&str) -> ParseResult<&str>, +{ + fn digits(s: &str) -> ParseResult<(u8, u8)> { + let b = s.as_bytes(); + if b.len() < 2 { + Err(TOO_SHORT) + } else { + Ok((b[0], b[1])) + } + } + let negative = match s.as_bytes().first() { + Some(&b'+') => false, + Some(&b'-') => true, + Some(_) => return Err(INVALID), + None => return Err(TOO_SHORT), + }; + s = &s[1..]; + + // hours (00--99) + let hours = match digits(s)? { + (h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')), + _ => return Err(INVALID), + }; + s = &s[2..]; + + // colons (and possibly other separators) + s = consume_colon(s)?; + + // minutes (00--59) + // if the next two items are digits then we have to add minutes + let minutes = if let Ok(ds) = digits(s) { + match ds { + (m1 @ b'0'...b'5', m2 @ b'0'...b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')), + (b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE), + _ => return Err(INVALID), + } + } else if allow_missing_minutes { + 0 + } else { + return Err(TOO_SHORT); + }; + s = match s.len() { + len if len >= 2 => &s[2..], + len if len == 0 => s, + _ => return Err(TOO_SHORT), + }; + + let seconds = hours * 3600 + minutes * 60; + Ok((s, if negative { -seconds } else { seconds })) +} + +/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`. +pub fn timezone_offset_zulu(s: &str, colon: F) -> ParseResult<(&str, i32)> +where + F: FnMut(&str) -> ParseResult<&str>, +{ + let bytes = s.as_bytes(); + match bytes.first() { + Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)), + Some(&b'u') | Some(&b'U') => { + if bytes.len() >= 3 { + let (b, c) = (bytes[1], bytes[2]); + match (b | 32, c | 32) { + (b't', b'c') => Ok((&s[3..], 0)), + _ => Err(INVALID), + } + } else { + Err(INVALID) + } + } + _ => timezone_offset(s, colon), + } +} + +/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as +/// `+00:00`, and allows missing minutes entirely. +pub fn timezone_offset_permissive(s: &str, colon: F) -> ParseResult<(&str, i32)> +where + F: FnMut(&str) -> ParseResult<&str>, +{ + match s.as_bytes().first() { + Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)), + _ => timezone_offset_internal(s, colon, true), + } +} + +/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones. +/// May return `None` which indicates an insufficient offset data (i.e. `-0000`). +pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option)> { + // tries to parse legacy time zone names + let upto = s + .as_bytes() + .iter() + .position(|&c| match c { + b'a'...b'z' | b'A'...b'Z' => false, + _ => true, + }) + .unwrap_or_else(|| s.len()); + if upto > 0 { + let name = &s[..upto]; + let s = &s[upto..]; + let offset_hours = |o| Ok((s, Some(o * 3600))); + if equals(name, "gmt") || equals(name, "ut") { + offset_hours(0) + } else if equals(name, "edt") { + offset_hours(-4) + } else if equals(name, "est") || equals(name, "cdt") { + offset_hours(-5) + } else if equals(name, "cst") || equals(name, "mdt") { + offset_hours(-6) + } else if equals(name, "mst") || equals(name, "pdt") { + offset_hours(-7) + } else if equals(name, "pst") { + offset_hours(-8) + } else { + Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000 + } + } else { + let (s_, offset) = timezone_offset(s, |s| Ok(s))?; + Ok((s_, Some(offset))) + } +} + +/// Tries to consume everyting until next whitespace-like symbol. +/// Does not provide any offset information from the consumed data. +pub fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> { + Ok((s.trim_left_matches(|c: char| !c.is_whitespace()), ())) +} diff --git a/src/format/strftime.rs b/src/format/strftime.rs new file mode 100644 index 0000000..93820a2 --- /dev/null +++ b/src/format/strftime.rs @@ -0,0 +1,649 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +/*! +`strftime`/`strptime`-inspired date and time formatting syntax. + +## Specifiers + +The following specifiers are available both to formatting and parsing. + +| Spec. | Example | Description | +|-------|----------|----------------------------------------------------------------------------| +| | | **DATE SPECIFIERS:** | +| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. [^1] | +| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^2] | +| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^2] | +| | | | +| `%m` | `07` | Month number (01--12), zero-padded to 2 digits. | +| `%b` | `Jul` | Abbreviated month name. Always 3 letters. | +| `%B` | `July` | Full month name. Also accepts corresponding abbreviation in parsing. | +| `%h` | `Jul` | Same as `%b`. | +| | | | +| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. | +| `%e` | ` 8` | Same as `%d` but space-padded. Same as `%_d`. | +| | | | +| `%a` | `Sun` | Abbreviated weekday name. Always 3 letters. | +| `%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. | +| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. | +| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) | +| | | | +| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^3] | +| `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.| +| | | | +| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^4] | +| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^4] | +| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^4] | +| | | | +| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. | +| | | | +| `%D` | `07/08/01` | Month-day-year format. Same as `%m/%d/%y`. | +| `%x` | `07/08/01` | Locale's date representation (e.g., 12/31/99). | +| `%F` | `2001-07-08` | Year-month-day format (ISO 8601). Same as `%Y-%m-%d`. | +| `%v` | ` 8-Jul-2001` | Day-month-year format. Same as `%e-%b-%Y`. | +| | | | +| | | **TIME SPECIFIERS:** | +| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. | +| `%k` | ` 0` | Same as `%H` but space-padded. Same as `%_H`. | +| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. | +| `%l` | `12` | Same as `%I` but space-padded. Same as `%_I`. | +| | | | +| `%P` | `am` | `am` or `pm` in 12-hour clocks. | +| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. | +| | | | +| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. | +| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^5] | +| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^8] | +| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^8] | +| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^8] | +| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^8] | +| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^8] | +| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^8] | +| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^8] | +| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^8] | +| | | | +| `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. | +| `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. | +| `%X` | `00:34:60` | Locale's time representation (e.g., 23:13:48). | +| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. | +| | | | +| | | **TIME ZONE SPECIFIERS:** | +| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^9] | +| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). | +| `%:z` | `+09:30` | Same as `%z` but with a colon. | +| `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. | +| | | | +| | | **DATE & TIME SPECIFIERS:** | +|`%c`|`Sun Jul 8 00:34:60 2001`|Locale's date and time (e.g., Thu Mar 3 23:05:25 2005). | +| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^6] | +| | | | +| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^7]| +| | | | +| | | **SPECIAL SPECIFIERS:** | +| `%t` | | Literal tab (`\t`). | +| `%n` | | Literal newline (`\n`). | +| `%%` | | Literal percent sign. | + +It is possible to override the default padding behavior of numeric specifiers `%?`. +This is not allowed for other specifiers and will result in the `BAD_FORMAT` error. + +Modifier | Description +-------- | ----------- +`%-?` | Suppresses any padding including spaces and zeroes. (e.g. `%j` = `012`, `%-j` = `12`) +`%_?` | Uses spaces as a padding. (e.g. `%j` = `012`, `%_j` = ` 12`) +`%0?` | Uses zeroes as a padding. (e.g. `%e` = ` 9`, `%0e` = `09`) + +Notes: + +[^1]: `%Y`: + Negative years are allowed in formatting but not in parsing. + +[^2]: `%C`, `%y`: + This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively. + +[^3]: `%U`: + Week 1 starts with the first Sunday in that year. + It is possible to have week 0 for days before the first Sunday. + +[^4]: `%G`, `%g`, `%V`: + Week 1 is the first week with at least 4 days in that year. + Week 0 does not exist, so this should be used with `%G` or `%g`. + +[^5]: `%S`: + It accounts for leap seconds, so `60` is possible. + +[^6]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional + digits for seconds and colons in the time zone offset. +
+
+ The typical `strftime` implementations have different (and locale-dependent) + formats for this specifier. While Chrono's format for `%+` is far more + stable, it is best to avoid this specifier if you want to control the exact + output. + +[^7]: `%s`: + This is not padded and can be negative. + For the purpose of Chrono, it only accounts for non-leap seconds + so it slightly differs from ISO C `strftime` behavior. + +[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`: +
+ The default `%f` is right-aligned and always zero-padded to 9 digits + for the compatibility with glibc and others, + so it always counts the number of nanoseconds since the last whole second. + E.g. 7ms after the last second will print `007000000`, + and parsing `7000000` will yield the same. +
+
+ The variant `%.f` is left-aligned and print 0, 3, 6 or 9 fractional digits + according to the precision. + E.g. 70ms after the last second under `%.f` will print `.070` (note: not `.07`), + and parsing `.07`, `.070000` etc. will yield the same. + Note that they can print or read nothing if the fractional part is zero or + the next character is not `.`. +
+
+ The variant `%.3f`, `%.6f` and `%.9f` are left-aligned and print 3, 6 or 9 fractional digits + according to the number preceding `f`. + E.g. 70ms after the last second under `%.3f` will print `.070` (note: not `.07`), + and parsing `.07`, `.070000` etc. will yield the same. + Note that they can read nothing if the fractional part is zero or + the next character is not `.` however will print with the specified length. +
+
+ The variant `%3f`, `%6f` and `%9f` are left-aligned and print 3, 6 or 9 fractional digits + according to the number preceding `f`, but without the leading dot. + E.g. 70ms after the last second under `%3f` will print `070` (note: not `07`), + and parsing `07`, `070000` etc. will yield the same. + Note that they can read nothing if the fractional part is zero. + +[^9]: `%Z`: + Offset will not be populated from the parsed data, nor will it be validated. + Timezone is completely ignored. Similar to the glibc `strptime` treatment of + this format code. +
+
+ It is not possible to reliably convert from an abbreviation to an offset, + for example CDT can mean either Central Daylight Time (North America) or + China Daylight Time. +*/ + +#[cfg(feature = "unstable-locales")] +use super::{locales, Locale}; +use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad}; + +#[cfg(feature = "unstable-locales")] +type Fmt<'a> = Vec>; +#[cfg(not(feature = "unstable-locales"))] +type Fmt<'a> = &'static [Item<'static>]; + +static D_FMT: &'static [Item<'static>] = + &[num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)]; +static D_T_FMT: &'static [Item<'static>] = &[ + fix!(ShortWeekdayName), + sp!(" "), + fix!(ShortMonthName), + sp!(" "), + nums!(Day), + sp!(" "), + num0!(Hour), + lit!(":"), + num0!(Minute), + lit!(":"), + num0!(Second), + sp!(" "), + num0!(Year), +]; +static T_FMT: &'static [Item<'static>] = + &[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)]; + +/// Parsing iterator for `strftime`-like format strings. +#[derive(Clone, Debug)] +pub struct StrftimeItems<'a> { + /// Remaining portion of the string. + remainder: &'a str, + /// If the current specifier is composed of multiple formatting items (e.g. `%+`), + /// parser refers to the statically reconstructed slice of them. + /// If `recons` is not empty they have to be returned earlier than the `remainder`. + recons: Fmt<'a>, + /// Date format + d_fmt: Fmt<'a>, + /// Date and time format + d_t_fmt: Fmt<'a>, + /// Time format + t_fmt: Fmt<'a>, +} + +impl<'a> StrftimeItems<'a> { + /// Creates a new parsing iterator from the `strftime`-like format string. + pub fn new(s: &'a str) -> StrftimeItems<'a> { + Self::with_remainer(s) + } + + /// Creates a new parsing iterator from the `strftime`-like format string. + #[cfg(feature = "unstable-locales")] + pub fn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> { + let d_fmt = StrftimeItems::new(locales::d_fmt(locale)).collect(); + let d_t_fmt = StrftimeItems::new(locales::d_t_fmt(locale)).collect(); + let t_fmt = StrftimeItems::new(locales::t_fmt(locale)).collect(); + + StrftimeItems { + remainder: s, + recons: Vec::new(), + d_fmt: d_fmt, + d_t_fmt: d_t_fmt, + t_fmt: t_fmt, + } + } + + #[cfg(not(feature = "unstable-locales"))] + fn with_remainer(s: &'a str) -> StrftimeItems<'a> { + static FMT_NONE: &'static [Item<'static>; 0] = &[]; + + StrftimeItems { + remainder: s, + recons: FMT_NONE, + d_fmt: D_FMT, + d_t_fmt: D_T_FMT, + t_fmt: T_FMT, + } + } + + #[cfg(feature = "unstable-locales")] + fn with_remainer(s: &'a str) -> StrftimeItems<'a> { + StrftimeItems { + remainder: s, + recons: Vec::new(), + d_fmt: D_FMT.to_vec(), + d_t_fmt: D_T_FMT.to_vec(), + t_fmt: T_FMT.to_vec(), + } + } +} + +const HAVE_ALTERNATES: &'static str = "z"; + +impl<'a> Iterator for StrftimeItems<'a> { + type Item = Item<'a>; + + fn next(&mut self) -> Option> { + // we have some reconstructed items to return + if !self.recons.is_empty() { + let item; + #[cfg(feature = "unstable-locales")] + { + item = self.recons.remove(0); + } + #[cfg(not(feature = "unstable-locales"))] + { + item = self.recons[0].clone(); + self.recons = &self.recons[1..]; + } + return Some(item); + } + + match self.remainder.chars().next() { + // we are done + None => None, + + // the next item is a specifier + Some('%') => { + self.remainder = &self.remainder[1..]; + + macro_rules! next { + () => { + match self.remainder.chars().next() { + Some(x) => { + self.remainder = &self.remainder[x.len_utf8()..]; + x + } + None => return Some(Item::Error), // premature end of string + } + }; + } + + let spec = next!(); + let pad_override = match spec { + '-' => Some(Pad::None), + '0' => Some(Pad::Zero), + '_' => Some(Pad::Space), + _ => None, + }; + let is_alternate = spec == '#'; + let spec = if pad_override.is_some() || is_alternate { next!() } else { spec }; + if is_alternate && !HAVE_ALTERNATES.contains(spec) { + return Some(Item::Error); + } + + macro_rules! recons { + [$head:expr, $($tail:expr),+ $(,)*] => ({ + #[cfg(feature = "unstable-locales")] + { + self.recons.clear(); + $(self.recons.push($tail);)+ + } + #[cfg(not(feature = "unstable-locales"))] + { + const RECONS: &'static [Item<'static>] = &[$($tail),+]; + self.recons = RECONS; + } + $head + }) + } + + macro_rules! recons_from_slice { + ($slice:expr) => {{ + #[cfg(feature = "unstable-locales")] + { + self.recons.clear(); + self.recons.extend_from_slice(&$slice[1..]); + } + #[cfg(not(feature = "unstable-locales"))] + { + self.recons = &$slice[1..]; + } + $slice[0].clone() + }}; + } + + let item = match spec { + 'A' => fix!(LongWeekdayName), + 'B' => fix!(LongMonthName), + 'C' => num0!(YearDiv100), + 'D' => { + recons![num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)] + } + 'F' => recons![num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)], + 'G' => num0!(IsoYear), + 'H' => num0!(Hour), + 'I' => num0!(Hour12), + 'M' => num0!(Minute), + 'P' => fix!(LowerAmPm), + 'R' => recons![num0!(Hour), lit!(":"), num0!(Minute)], + 'S' => num0!(Second), + 'T' => recons![num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)], + 'U' => num0!(WeekFromSun), + 'V' => num0!(IsoWeek), + 'W' => num0!(WeekFromMon), + 'X' => recons_from_slice!(self.t_fmt), + 'Y' => num0!(Year), + 'Z' => fix!(TimezoneName), + 'a' => fix!(ShortWeekdayName), + 'b' | 'h' => fix!(ShortMonthName), + 'c' => recons_from_slice!(self.d_t_fmt), + 'd' => num0!(Day), + 'e' => nums!(Day), + 'f' => num0!(Nanosecond), + 'g' => num0!(IsoYearMod100), + 'j' => num0!(Ordinal), + 'k' => nums!(Hour), + 'l' => nums!(Hour12), + 'm' => num0!(Month), + 'n' => sp!("\n"), + 'p' => fix!(UpperAmPm), + 'r' => recons![ + num0!(Hour12), + lit!(":"), + num0!(Minute), + lit!(":"), + num0!(Second), + sp!(" "), + fix!(UpperAmPm) + ], + 's' => num!(Timestamp), + 't' => sp!("\t"), + 'u' => num!(WeekdayFromMon), + 'v' => { + recons![nums!(Day), lit!("-"), fix!(ShortMonthName), lit!("-"), num0!(Year)] + } + 'w' => num!(NumDaysFromSun), + 'x' => recons_from_slice!(self.d_fmt), + 'y' => num0!(YearMod100), + 'z' => { + if is_alternate { + internal_fix!(TimezoneOffsetPermissive) + } else { + fix!(TimezoneOffset) + } + } + '+' => fix!(RFC3339), + ':' => match next!() { + 'z' => fix!(TimezoneOffsetColon), + _ => Item::Error, + }, + '.' => match next!() { + '3' => match next!() { + 'f' => fix!(Nanosecond3), + _ => Item::Error, + }, + '6' => match next!() { + 'f' => fix!(Nanosecond6), + _ => Item::Error, + }, + '9' => match next!() { + 'f' => fix!(Nanosecond9), + _ => Item::Error, + }, + 'f' => fix!(Nanosecond), + _ => Item::Error, + }, + '3' => match next!() { + 'f' => internal_fix!(Nanosecond3NoDot), + _ => Item::Error, + }, + '6' => match next!() { + 'f' => internal_fix!(Nanosecond6NoDot), + _ => Item::Error, + }, + '9' => match next!() { + 'f' => internal_fix!(Nanosecond9NoDot), + _ => Item::Error, + }, + '%' => lit!("%"), + _ => Item::Error, // no such specifier + }; + + // adjust `item` if we have any padding modifier + if let Some(new_pad) = pad_override { + match item { + 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 { + Some(item) + } + } + + // the next item is space + Some(c) if c.is_whitespace() => { + // `%` is not a whitespace, so `c != '%'` is redundant + let nextspec = self + .remainder + .find(|c: char| !c.is_whitespace()) + .unwrap_or_else(|| self.remainder.len()); + assert!(nextspec > 0); + let item = sp!(&self.remainder[..nextspec]); + self.remainder = &self.remainder[nextspec..]; + Some(item) + } + + // the next item is literal + _ => { + let nextspec = self + .remainder + .find(|c: char| c.is_whitespace() || c == '%') + .unwrap_or_else(|| self.remainder.len()); + assert!(nextspec > 0); + let item = lit!(&self.remainder[..nextspec]); + self.remainder = &self.remainder[nextspec..]; + Some(item) + } + } + } +} + +#[cfg(test)] +#[test] +fn test_strftime_items() { + fn parse_and_collect<'a>(s: &'a str) -> Vec> { + // map any error into `[Item::Error]`. useful for easy testing. + let items = StrftimeItems::new(s); + let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) }); + items.collect::>>().unwrap_or(vec![Item::Error]) + } + + assert_eq!(parse_and_collect(""), []); + assert_eq!(parse_and_collect(" \t\n\r "), [sp!(" \t\n\r ")]); + assert_eq!(parse_and_collect("hello?"), [lit!("hello?")]); + assert_eq!( + parse_and_collect("a b\t\nc"), + [lit!("a"), sp!(" "), lit!("b"), sp!("\t\n"), lit!("c")] + ); + assert_eq!(parse_and_collect("100%%"), [lit!("100"), lit!("%")]); + assert_eq!(parse_and_collect("100%% ok"), [lit!("100"), lit!("%"), sp!(" "), lit!("ok")]); + assert_eq!(parse_and_collect("%%PDF-1.0"), [lit!("%"), lit!("PDF-1.0")]); + assert_eq!( + parse_and_collect("%Y-%m-%d"), + [num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)] + ); + assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]")); + assert_eq!(parse_and_collect("%m %d"), [num0!(Month), sp!(" "), num0!(Day)]); + assert_eq!(parse_and_collect("%"), [Item::Error]); + assert_eq!(parse_and_collect("%%"), [lit!("%")]); + assert_eq!(parse_and_collect("%%%"), [Item::Error]); + assert_eq!(parse_and_collect("%%%%"), [lit!("%"), lit!("%")]); + assert_eq!(parse_and_collect("foo%?"), [Item::Error]); + assert_eq!(parse_and_collect("bar%42"), [Item::Error]); + assert_eq!(parse_and_collect("quux% +"), [Item::Error]); + assert_eq!(parse_and_collect("%.Z"), [Item::Error]); + assert_eq!(parse_and_collect("%:Z"), [Item::Error]); + assert_eq!(parse_and_collect("%-Z"), [Item::Error]); + assert_eq!(parse_and_collect("%0Z"), [Item::Error]); + assert_eq!(parse_and_collect("%_Z"), [Item::Error]); + assert_eq!(parse_and_collect("%.j"), [Item::Error]); + assert_eq!(parse_and_collect("%:j"), [Item::Error]); + assert_eq!(parse_and_collect("%-j"), [num!(Ordinal)]); + assert_eq!(parse_and_collect("%0j"), [num0!(Ordinal)]); + assert_eq!(parse_and_collect("%_j"), [nums!(Ordinal)]); + assert_eq!(parse_and_collect("%.e"), [Item::Error]); + assert_eq!(parse_and_collect("%:e"), [Item::Error]); + assert_eq!(parse_and_collect("%-e"), [num!(Day)]); + assert_eq!(parse_and_collect("%0e"), [num0!(Day)]); + assert_eq!(parse_and_collect("%_e"), [nums!(Day)]); + assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]); + assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]); + assert_eq!(parse_and_collect("%#m"), [Item::Error]); +} + +#[cfg(test)] +#[test] +fn test_strftime_docs() { + use {FixedOffset, TimeZone, Timelike}; + + let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708); + + // date specifiers + assert_eq!(dt.format("%Y").to_string(), "2001"); + assert_eq!(dt.format("%C").to_string(), "20"); + assert_eq!(dt.format("%y").to_string(), "01"); + assert_eq!(dt.format("%m").to_string(), "07"); + assert_eq!(dt.format("%b").to_string(), "Jul"); + assert_eq!(dt.format("%B").to_string(), "July"); + assert_eq!(dt.format("%h").to_string(), "Jul"); + assert_eq!(dt.format("%d").to_string(), "08"); + assert_eq!(dt.format("%e").to_string(), " 8"); + assert_eq!(dt.format("%e").to_string(), dt.format("%_d").to_string()); + assert_eq!(dt.format("%a").to_string(), "Sun"); + assert_eq!(dt.format("%A").to_string(), "Sunday"); + assert_eq!(dt.format("%w").to_string(), "0"); + assert_eq!(dt.format("%u").to_string(), "7"); + assert_eq!(dt.format("%U").to_string(), "28"); + assert_eq!(dt.format("%W").to_string(), "27"); + assert_eq!(dt.format("%G").to_string(), "2001"); + assert_eq!(dt.format("%g").to_string(), "01"); + assert_eq!(dt.format("%V").to_string(), "27"); + assert_eq!(dt.format("%j").to_string(), "189"); + assert_eq!(dt.format("%D").to_string(), "07/08/01"); + assert_eq!(dt.format("%x").to_string(), "07/08/01"); + assert_eq!(dt.format("%F").to_string(), "2001-07-08"); + assert_eq!(dt.format("%v").to_string(), " 8-Jul-2001"); + + // time specifiers + assert_eq!(dt.format("%H").to_string(), "00"); + assert_eq!(dt.format("%k").to_string(), " 0"); + assert_eq!(dt.format("%k").to_string(), dt.format("%_H").to_string()); + assert_eq!(dt.format("%I").to_string(), "12"); + assert_eq!(dt.format("%l").to_string(), "12"); + assert_eq!(dt.format("%l").to_string(), dt.format("%_I").to_string()); + assert_eq!(dt.format("%P").to_string(), "am"); + assert_eq!(dt.format("%p").to_string(), "AM"); + assert_eq!(dt.format("%M").to_string(), "34"); + assert_eq!(dt.format("%S").to_string(), "60"); + assert_eq!(dt.format("%f").to_string(), "026490708"); + assert_eq!(dt.format("%.f").to_string(), ".026490708"); + assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(), ".026490"); + assert_eq!(dt.format("%.3f").to_string(), ".026"); + assert_eq!(dt.format("%.6f").to_string(), ".026490"); + assert_eq!(dt.format("%.9f").to_string(), ".026490708"); + assert_eq!(dt.format("%3f").to_string(), "026"); + assert_eq!(dt.format("%6f").to_string(), "026490"); + assert_eq!(dt.format("%9f").to_string(), "026490708"); + assert_eq!(dt.format("%R").to_string(), "00:34"); + assert_eq!(dt.format("%T").to_string(), "00:34:60"); + assert_eq!(dt.format("%X").to_string(), "00:34:60"); + assert_eq!(dt.format("%r").to_string(), "12:34:60 AM"); + + // time zone specifiers + //assert_eq!(dt.format("%Z").to_string(), "ACST"); + assert_eq!(dt.format("%z").to_string(), "+0930"); + assert_eq!(dt.format("%:z").to_string(), "+09:30"); + + // date & time specifiers + assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001"); + assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30"); + assert_eq!( + dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(), + "2001-07-08T00:34:60.026490+09:30" + ); + assert_eq!(dt.format("%s").to_string(), "994518299"); + + // special specifiers + assert_eq!(dt.format("%t").to_string(), "\t"); + assert_eq!(dt.format("%n").to_string(), "\n"); + assert_eq!(dt.format("%%").to_string(), "%"); +} + +#[cfg(feature = "unstable-locales")] +#[test] +fn test_strftime_docs_localized() { + use {FixedOffset, TimeZone}; + + let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708); + + // date specifiers + assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui"); + assert_eq!(dt.format_localized("%B", Locale::fr_BE).to_string(), "juillet"); + assert_eq!(dt.format_localized("%h", Locale::fr_BE).to_string(), "jui"); + assert_eq!(dt.format_localized("%a", Locale::fr_BE).to_string(), "dim"); + assert_eq!(dt.format_localized("%A", Locale::fr_BE).to_string(), "dimanche"); + assert_eq!(dt.format_localized("%D", Locale::fr_BE).to_string(), "07/08/01"); + assert_eq!(dt.format_localized("%x", Locale::fr_BE).to_string(), "08/07/01"); + assert_eq!(dt.format_localized("%F", Locale::fr_BE).to_string(), "2001-07-08"); + assert_eq!(dt.format_localized("%v", Locale::fr_BE).to_string(), " 8-jui-2001"); + + // time specifiers + assert_eq!(dt.format_localized("%P", Locale::fr_BE).to_string(), ""); + assert_eq!(dt.format_localized("%p", Locale::fr_BE).to_string(), ""); + assert_eq!(dt.format_localized("%R", Locale::fr_BE).to_string(), "00:34"); + assert_eq!(dt.format_localized("%T", Locale::fr_BE).to_string(), "00:34:60"); + assert_eq!(dt.format_localized("%X", Locale::fr_BE).to_string(), "00:34:60"); + assert_eq!(dt.format_localized("%r", Locale::fr_BE).to_string(), "12:34:60 "); + + // date & time specifiers + assert_eq!( + dt.format_localized("%c", Locale::fr_BE).to_string(), + "dim 08 jui 2001 00:34:60 +09:30" + ); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9d66ae3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,1535 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! # Chrono: Date and Time for Rust +//! +//! It aims to be a feature-complete superset of +//! the [time](https://github.com/rust-lang-deprecated/time) library. +//! In particular, +//! +//! * Chrono strictly adheres to ISO 8601. +//! * Chrono is timezone-aware by default, with separate timezone-naive types. +//! * Chrono is space-optimal and (while not being the primary goal) reasonably efficient. +//! +//! There were several previous attempts to bring a good date and time library to Rust, +//! which Chrono builds upon and should acknowledge: +//! +//! * [Initial research on +//! the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md) +//! * Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs) +//! * Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime) +//! +//! Any significant changes to Chrono are documented in +//! the [`CHANGELOG.md`](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) file. +//! +//! ## Usage +//! +//! Put this in your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! chrono = "0.4" +//! ``` +//! +//! ### Features +//! +//! Chrono supports various runtime environments and operating systems, and has +//! several features that may be enabled or disabled. +//! +//! Default features: +//! +//! - `alloc`: Enable features that depend on allocation (primarily string formatting) +//! - `std`: Enables functionality that depends on the standard library. This +//! is a superset of `alloc` and adds interoperation with standard library types +//! and traits. +//! - `clock`: enables reading the system time (`now`), independent of whether +//! `std::time::SystemTime` is present, depends on having a libc. +//! +//! Optional features: +//! +//! - `wasmbind`: Enable integration with [wasm-bindgen][] and its `js-sys` project +//! - [`serde`][]: Enable serialization/deserialization via serde. +//! - `unstable-locales`: Enable localization. This adds various methods with a +//! `_localized` suffix. The implementation and API may change or even be +//! removed in a patch release. Feedback welcome. +//! +//! [`serde`]: https://github.com/serde-rs/serde +//! [wasm-bindgen]: https://github.com/rustwasm/wasm-bindgen +//! +//! See the [cargo docs][] for examples of specifying features. +//! +//! [cargo docs]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features +//! +//! ## Overview +//! +//! ### Duration +//! +//! Chrono currently uses its own [`Duration`] type to represent the magnitude +//! of a time span. Since this has the same name as 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. +//! +//! When the `oldtime` feature is enabled, [`Duration`] is an alias for the +//! [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) +//! type from v0.1 of the time crate. time v0.1 is deprecated, so new code +//! should disable the `oldtime` feature and use the `chrono::Duration` type +//! instead. The `oldtime` feature is enabled by default for backwards +//! compatibility, but future versions of Chrono are likely to remove the +//! feature entirely. +//! +//! 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://docs.rs/time/0.1.40/time/struct.Duration.html#method.from_std) +//! and +//! [`Duration::to_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.to_std) +//! methods. +//! +//! ### Date and Time +//! +//! Chrono provides a +//! [**`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 +//! that is unconcerned with timezones, consider +//! [`time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html), +//! which tracks your system clock, or +//! [`time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which +//! is an opaque but monotonically-increasing representation of a moment in time. +//! +//! `DateTime` is timezone-aware and must be constructed from +//! the [**`TimeZone`**](./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`**](./offset/struct.Utc.html) specifies the UTC time zone. It is most efficient. +//! +//! * [**`Local`**](./offset/struct.Local.html) specifies the system local time zone. +//! +//! * [**`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, +//! you would want to normalize other `TimeZone`s into this type. +//! +//! `DateTime`s with different `TimeZone` types are distinct and do not mix, +//! but can be converted to each other using +//! 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)) +//! 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 local: DateTime = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00` +//! # let _ = utc; let _ = local; +//! ``` +//! +//! Alternatively, you can create your own date and time. +//! This is a bit verbose due to Rust's lack of function and method overloading, +//! but in turn we get a rich combination of initialization methods. +//! +//! ```rust +//! use chrono::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") +//! assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11)); +//! // July 8 is Tuesday in ISO week 28 of the year 2014. +//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11)); +//! +//! let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z` +//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000)); +//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000)); +//! +//! // dynamic verification +//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33), +//! LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33))); +//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None); +//! assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); +//! +//! // 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. +//! 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); +//! # 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 +//! [`Timelike`](./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: +//! +//! ```rust +//! # extern crate chrono; +//! +//! # fn main() { +//! use chrono::prelude::*; +//! use chrono::Duration; +//! +//! // assume this returned `2014-11-28T21:45:59.324310806+09:00`: +//! let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806); +//! +//! // property accessors +//! assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); +//! assert_eq!((dt.month0(), dt.day0()), (10, 27)); // for unfortunate souls +//! assert_eq!((dt.hour(), dt.minute(), dt.second()), (21, 45, 59)); +//! assert_eq!(dt.weekday(), Weekday::Fri); +//! assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sun=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().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)); +//! +//! // a sample of property manipulations (validates dynamically) +//! assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday +//! assert_eq!(dt.with_day(32), None); +//! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE +//! +//! // arithmetic operations +//! 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 and Parsing +//! +//! Formatting is done via the [`format`](./struct.DateTime.html#method.format) method, +//! which format is equivalent to the familiar `strftime` format. +//! +//! See [`format::strftime`](./format/strftime/index.html#specifiers) +//! documentation for full syntax and list of specifiers. +//! +//! The default `to_string` method and `{:?}` specifier also give a reasonable representation. +//! 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. +//! +//! Chrono now also provides date formatting in almost any language without the +//! help of an additional C library. This functionality is under the feature +//! `unstable-locales`: +//! +//! ```text +//! chrono { version = "0.4", features = ["unstable-locales"] +//! ``` +//! +//! The `unstable-locales` feature requires and implies at least the `alloc` feature. +//! +//! ```rust +//! 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"); +//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); +//! assert_eq!(dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), "vendredi 28 novembre 2014, 12:00:09"); +//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); +//! +//! 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"); +//! +//! // Note that milli/nanoseconds are only printed if they are non-zero +//! let dt_nano = Utc.ymd(2014, 11, 28).and_hms_nano(12, 0, 9, 1); +//! assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z"); +//! ``` +//! +//! 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 +//! `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. +//! +//! 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`](./struct.DateTime.html#method.parse_from_rfc2822) +//! and +//! [`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 +//! 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`](./format/index.html) module. +//! +//! ```rust +//! 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)); +//! +//! // 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-28T21:00:09+09:00".parse::>(), Ok(fixed_dt.clone())); +//! +//! // method 2 +//! assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"), +//! Ok(fixed_dt.clone())); +//! assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"), +//! Ok(fixed_dt.clone())); +//! 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())); +//! +//! // oops, the year is missing! +//! 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()); +//! // oops, the weekday is incorrect! +//! assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); +//! ``` +//! +//! Again : See [`format::strftime`](./format/strftime/index.html#specifiers) +//! documentation for full syntax and list of specifiers. +//! +//! ### Conversion from and to EPOCH timestamps +//! +//! Use [`Utc.timestamp(seconds, nanoseconds)`](./offset/trait.TimeZone.html#method.timestamp) +//! to construct a [`DateTime`](./struct.DateTime.html) from a UNIX timestamp +//! (seconds, nanoseconds that passed since January 1st 1970). +//! +//! Use [`DateTime.timestamp`](./struct.DateTime.html#method.timestamp) to get the timestamp (in seconds) +//! from a [`DateTime`](./struct.DateTime.html). Additionally, you can use +//! [`DateTime.timestamp_subsec_nanos`](./struct.DateTime.html#method.timestamp_subsec_nanos) +//! to get the number of additional number of nanoseconds. +//! +//! ```rust +//! // We need the trait in scope to use Utc::timestamp(). +//! use chrono::{DateTime, TimeZone, Utc}; +//! +//! // Construct a datetime from epoch: +//! let dt = Utc.timestamp(1_500_000_000, 0); +//! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000"); +//! +//! // Get epoch value from a datetime: +//! let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); +//! assert_eq!(dt.timestamp(), 1_500_000_000); +//! ``` +//! +//! ### Individual date +//! +//! 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. +//! +//! ```rust +//! 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()); +//! 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(), +//! "070809"); +//! ``` +//! +//! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. +//! +//! `DateTime` has [`date`](./struct.DateTime.html#method.date) method +//! which returns a `Date` which represents its date component. +//! 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/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`](./struct.DateTime.html#method.naive_local) returns +//! a view to the naive local time, +//! and [`naive_utc`](./struct.DateTime.html#method.naive_utc) returns +//! a view to the naive UTC time. +//! +//! ## Limitations +//! +//! Only proleptic Gregorian calendar (i.e. extended to support older dates) is supported. +//! Be very careful if you really have to deal with pre-20C dates, they can be in Julian or others. +//! +//! 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](./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 +//! 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`. +//! +//! Non ISO week handling is not yet supported. +//! For now you can use the [chrono_ext](https://crates.io/crates/chrono_ext) +//! crate ([sources](https://github.com/bcourtine/chrono-ext/)). +//! +//! 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/latest/")] +#![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507 +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![deny(dead_code)] +// lints are added all the time, we test on 1.13 +#![allow(unknown_lints)] +#![cfg_attr(not(any(feature = "std", test)), no_std)] +#![cfg_attr(feature = "cargo-clippy", allow( + renamed_and_removed_lints, + // The explicit 'static lifetimes are still needed for rustc 1.13-16 + // backward compatibility, and this appeases clippy. If minimum rustc + // becomes 1.17, should be able to remove this, those 'static lifetimes, + // and use `static` in a lot of places `const` is used now. + redundant_static_lifetimes, + // Similarly, redundant_field_names lints on not using the + // field-init-shorthand, which was stabilized in rust 1.17. + redundant_field_names, + // Changing trivially_copy_pass_by_ref would require an incompatible version + // bump. + trivially_copy_pass_by_ref, + try_err, + // Currently deprecated, we use the separate implementation to add docs + // warning that putting a time in a hash table is probably a bad idea + derive_hash_xor_eq, +))] + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(all(feature = "std", not(feature = "alloc")))] +extern crate std as alloc; +#[cfg(any(feature = "std", test))] +extern crate std as core; + +#[cfg(feature = "oldtime")] +extern crate time as oldtime; +#[cfg(not(feature = "oldtime"))] +mod oldtime; + +#[cfg(feature = "clock")] +extern crate libc; +#[cfg(all(feature = "clock", windows))] +extern crate winapi; +#[cfg(all( + feature = "clock", + not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")) +))] +mod sys; + +extern crate num_integer; +extern crate num_traits; +#[cfg(feature = "rustc-serialize")] +extern crate rustc_serialize; +#[cfg(feature = "serde")] +extern crate serde as serdelib; +#[cfg(feature = "__doctest")] +#[cfg_attr(feature = "__doctest", cfg(doctest))] +#[macro_use] +extern crate doc_comment; +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] +extern crate js_sys; +#[cfg(feature = "unstable-locales")] +extern crate pure_rust_locales; +#[cfg(feature = "bench")] +extern crate test; +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] +extern crate wasm_bindgen; + +#[cfg(feature = "__doctest")] +#[cfg_attr(feature = "__doctest", cfg(doctest))] +doctest!("../README.md"); + +// this reexport is to aid the transition and should not be in the prelude! +pub use oldtime::Duration; + +pub use date::{Date, MAX_DATE, MIN_DATE}; +#[cfg(feature = "rustc-serialize")] +pub use datetime::rustc_serialize::TsSeconds; +pub use datetime::{DateTime, SecondsFormat, MAX_DATETIME, MIN_DATETIME}; +/// L10n locales. +#[cfg(feature = "unstable-locales")] +pub use format::Locale; +pub use format::{ParseError, ParseResult}; +#[doc(no_inline)] +pub use naive::{IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; +#[cfg(feature = "clock")] +#[doc(no_inline)] +pub use offset::Local; +#[doc(no_inline)] +pub use offset::{FixedOffset, LocalResult, Offset, TimeZone, Utc}; +pub use round::{DurationRound, RoundingError, SubsecRound}; + +/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). +pub mod prelude { + #[doc(no_inline)] + pub use Date; + #[cfg(feature = "clock")] + #[doc(no_inline)] + pub use Local; + #[cfg(feature = "unstable-locales")] + #[doc(no_inline)] + pub use Locale; + #[doc(no_inline)] + pub use SubsecRound; + #[doc(no_inline)] + pub use {DateTime, SecondsFormat}; + #[doc(no_inline)] + pub use {Datelike, Month, Timelike, Weekday}; + #[doc(no_inline)] + pub use {FixedOffset, Utc}; + #[doc(no_inline)] + pub use {NaiveDate, NaiveDateTime, NaiveTime}; + #[doc(no_inline)] + pub use {Offset, TimeZone}; +} + +// useful throughout the codebase +macro_rules! try_opt { + ($e:expr) => { + match $e { + Some(v) => v, + None => return None, + } + }; +} + +mod div; +pub mod offset; +pub mod naive { + //! Date and time types unconcerned with timezones. + //! + //! 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. + + mod date; + mod datetime; + mod internals; + mod isoweek; + mod time; + + pub use self::date::{NaiveDate, MAX_DATE, MIN_DATE}; + #[cfg(feature = "rustc-serialize")] + #[allow(deprecated)] + pub use self::datetime::rustc_serialize::TsSeconds; + pub use self::datetime::{NaiveDateTime, MAX_DATETIME, MIN_DATETIME}; + pub use self::isoweek::IsoWeek; + pub use self::time::NaiveTime; + + #[cfg(feature = "__internal_bench")] + #[doc(hidden)] + pub use self::internals::YearFlags as __BenchYearFlags; + + /// Serialization/Deserialization of naive types in alternate formats + /// + /// The various modules in here are intended to be used with serde's [`with` + /// annotation][1] to serialize as something other than the default [RFC + /// 3339][2] format. + /// + /// [1]: https://serde.rs/attributes.html#field-attributes + /// [2]: https://tools.ietf.org/html/rfc3339 + #[cfg(feature = "serde")] + pub mod serde { + pub use super::datetime::serde::*; + } +} +mod date; +mod datetime; +pub mod format; +mod round; + +#[cfg(feature = "__internal_bench")] +#[doc(hidden)] +pub use naive::__BenchYearFlags; + +/// Serialization/Deserialization in alternate formats +/// +/// The various modules in here are intended to be used with serde's [`with` +/// annotation][1] to serialize as something other than the default [RFC +/// 3339][2] format. +/// +/// [1]: https://serde.rs/attributes.html#field-attributes +/// [2]: https://tools.ietf.org/html/rfc3339 +#[cfg(feature = "serde")] +pub mod serde { + pub use super::datetime::serde::*; +} + +// Until rust 1.18 there is no "pub(crate)" so to share this we need it in the root + +#[cfg(feature = "serde")] +enum SerdeError { + NonExistent { timestamp: V }, + Ambiguous { timestamp: V, min: D, max: D }, +} + +/// Construct a [`SerdeError::NonExistent`] +#[cfg(feature = "serde")] +fn ne_timestamp(ts: T) -> SerdeError { + SerdeError::NonExistent:: { timestamp: ts } +} + +#[cfg(feature = "serde")] +impl fmt::Debug for SerdeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChronoSerdeError({})", self) + } +} + +// impl core::error::Error for SerdeError {} +#[cfg(feature = "serde")] +impl fmt::Display for SerdeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &SerdeError::NonExistent { ref timestamp } => { + write!(f, "value is not a legal timestamp: {}", timestamp) + } + &SerdeError::Ambiguous { ref timestamp, ref min, ref max } => write!( + f, + "value is an ambiguous timestamp: {}, could be either of {}, {}", + timestamp, min, max + ), + } + } +} + +/// The day of week. +/// +/// 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, Hash)] +#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] +pub enum Weekday { + /// Monday. + Mon = 0, + /// Tuesday. + Tue = 1, + /// Wednesday. + Wed = 2, + /// Thursday. + Thu = 3, + /// Friday. + Fri = 4, + /// Saturday. + Sat = 5, + /// Sunday. + Sun = 6, +} + +impl Weekday { + /// The next day in the week. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon` + #[inline] + pub fn succ(&self) -> Weekday { + match *self { + Weekday::Mon => Weekday::Tue, + Weekday::Tue => Weekday::Wed, + Weekday::Wed => Weekday::Thu, + Weekday::Thu => Weekday::Fri, + Weekday::Fri => Weekday::Sat, + Weekday::Sat => Weekday::Sun, + Weekday::Sun => Weekday::Mon, + } + } + + /// The previous day in the week. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` + #[inline] + pub fn pred(&self) -> Weekday { + match *self { + Weekday::Mon => Weekday::Sun, + Weekday::Tue => Weekday::Mon, + Weekday::Wed => Weekday::Tue, + Weekday::Thu => Weekday::Wed, + Weekday::Fri => Weekday::Thu, + Weekday::Sat => Weekday::Fri, + Weekday::Sun => Weekday::Sat, + } + } + + /// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number) + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7 + #[inline] + pub fn number_from_monday(&self) -> u32 { + match *self { + Weekday::Mon => 1, + Weekday::Tue => 2, + Weekday::Wed => 3, + Weekday::Thu => 4, + Weekday::Fri => 5, + Weekday::Sat => 6, + Weekday::Sun => 7, + } + } + + /// Returns a day-of-week number starting from Sunday = 1. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1 + #[inline] + pub fn number_from_sunday(&self) -> u32 { + match *self { + Weekday::Mon => 2, + Weekday::Tue => 3, + Weekday::Wed => 4, + Weekday::Thu => 5, + Weekday::Fri => 6, + Weekday::Sat => 7, + Weekday::Sun => 1, + } + } + + /// Returns a day-of-week number starting from Monday = 0. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6 + #[inline] + pub fn num_days_from_monday(&self) -> u32 { + match *self { + Weekday::Mon => 0, + Weekday::Tue => 1, + Weekday::Wed => 2, + Weekday::Thu => 3, + Weekday::Fri => 4, + Weekday::Sat => 5, + Weekday::Sun => 6, + } + } + + /// Returns a day-of-week number starting from Sunday = 0. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0 + #[inline] + pub fn num_days_from_sunday(&self) -> u32 { + match *self { + Weekday::Mon => 1, + Weekday::Tue => 2, + Weekday::Wed => 3, + Weekday::Thu => 4, + Weekday::Fri => 5, + Weekday::Sat => 6, + Weekday::Sun => 0, + } + } +} + +impl fmt::Display for Weekday { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + Weekday::Mon => "Mon", + Weekday::Tue => "Tue", + Weekday::Wed => "Wed", + Weekday::Thu => "Thu", + Weekday::Fri => "Fri", + Weekday::Sat => "Sat", + Weekday::Sun => "Sun", + }) + } +} + +/// Any weekday can be represented as an integer from 0 to 6, which equals to +/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation. +/// Do not heavily depend on this though; use explicit methods whenever possible. +impl num_traits::FromPrimitive for Weekday { + #[inline] + fn from_i64(n: i64) -> Option { + match n { + 0 => Some(Weekday::Mon), + 1 => Some(Weekday::Tue), + 2 => Some(Weekday::Wed), + 3 => Some(Weekday::Thu), + 4 => Some(Weekday::Fri), + 5 => Some(Weekday::Sat), + 6 => Some(Weekday::Sun), + _ => None, + } + } + + #[inline] + fn from_u64(n: u64) -> Option { + match n { + 0 => Some(Weekday::Mon), + 1 => Some(Weekday::Tue), + 2 => Some(Weekday::Wed), + 3 => Some(Weekday::Thu), + 4 => Some(Weekday::Fri), + 5 => Some(Weekday::Sat), + 6 => Some(Weekday::Sun), + _ => None, + } + } +} + +use core::fmt; + +/// An error resulting from reading `Weekday` value with `FromStr`. +#[derive(Clone, PartialEq)] +pub struct ParseWeekdayError { + _dummy: (), +} + +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; + use core::fmt; + use serdelib::{de, ser}; + + impl ser::Serialize for Weekday { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.collect_str(&self) + } + } + + struct WeekdayVisitor; + + impl<'de> de::Visitor<'de> for WeekdayVisitor { + type Value = Weekday; + + 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(|_| E::custom("short or long weekday names expected")) + } + } + + impl<'de> de::Deserialize<'de> for Weekday { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + 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), + ("\"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", "\"thur\"", "\"thurs\""]; + + for str in errors { + from_str::(str).unwrap_err(); + } + } +} + +/// The month of the year. +/// +/// This enum is just a convenience implementation. +/// The month in dates created by DateLike objects does not return this enum. +/// +/// It is possible to convert from a date to a month independently +/// ``` +/// # extern crate num_traits; +/// use num_traits::FromPrimitive; +/// use chrono::prelude::*; +/// let date = Utc.ymd(2019, 10, 28).and_hms(9, 10, 11); +/// // `2019-10-28T09:10:11Z` +/// let month = Month::from_u32(date.month()); +/// assert_eq!(month, Some(Month::October)) +/// ``` +/// Or from a Month to an integer usable by dates +/// ``` +/// # use chrono::prelude::*; +/// let month = Month::January; +/// let dt = Utc.ymd(2019, month.number_from_month(), 28).and_hms(9, 10, 11); +/// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); +/// ``` +/// Allows mapping from and to month, from 1-January to 12-December. +/// Can be Serialized/Deserialized with serde +// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior. +#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] +pub enum Month { + /// January + January = 0, + /// February + February = 1, + /// March + March = 2, + /// April + April = 3, + /// May + May = 4, + /// June + June = 5, + /// July + July = 6, + /// August + August = 7, + /// September + September = 8, + /// October + October = 9, + /// November + November = 10, + /// December + December = 11, +} + +impl Month { + /// The next month. + /// + /// `m`: | `January` | `February` | `...` | `December` + /// ----------- | --------- | ---------- | --- | --------- + /// `m.succ()`: | `February` | `March` | `...` | `January` + #[inline] + pub fn succ(&self) -> Month { + match *self { + Month::January => Month::February, + Month::February => Month::March, + Month::March => Month::April, + Month::April => Month::May, + Month::May => Month::June, + Month::June => Month::July, + Month::July => Month::August, + Month::August => Month::September, + Month::September => Month::October, + Month::October => Month::November, + Month::November => Month::December, + Month::December => Month::January, + } + } + + /// The previous month. + /// + /// `m`: | `January` | `February` | `...` | `December` + /// ----------- | --------- | ---------- | --- | --------- + /// `m.succ()`: | `December` | `January` | `...` | `November` + #[inline] + pub fn pred(&self) -> Month { + match *self { + Month::January => Month::December, + Month::February => Month::January, + Month::March => Month::February, + Month::April => Month::March, + Month::May => Month::April, + Month::June => Month::May, + Month::July => Month::June, + Month::August => Month::July, + Month::September => Month::August, + Month::October => Month::September, + Month::November => Month::October, + Month::December => Month::November, + } + } + + /// Returns a month-of-year number starting from January = 1. + /// + /// `m`: | `January` | `February` | `...` | `December` + /// -------------------------| --------- | ---------- | --- | ----- + /// `m.number_from_month()`: | 1 | 2 | `...` | 12 + #[inline] + pub fn number_from_month(&self) -> u32 { + match *self { + Month::January => 1, + Month::February => 2, + Month::March => 3, + Month::April => 4, + Month::May => 5, + Month::June => 6, + Month::July => 7, + Month::August => 8, + Month::September => 9, + Month::October => 10, + Month::November => 11, + Month::December => 12, + } + } + + /// Get the name of the month + /// + /// ``` + /// use chrono::Month; + /// + /// assert_eq!(Month::January.name(), "January") + /// ``` + pub fn name(&self) -> &'static str { + match *self { + Month::January => "January", + Month::February => "February", + Month::March => "March", + Month::April => "April", + Month::May => "May", + Month::June => "June", + Month::July => "July", + Month::August => "August", + Month::September => "September", + Month::October => "October", + Month::November => "November", + Month::December => "December", + } + } +} + +impl num_traits::FromPrimitive for Month { + /// Returns an Option from a i64, assuming a 1-index, January = 1. + /// + /// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12` + /// ---------------------------| -------------------- | --------------------- | ... | ----- + /// ``: | Some(Month::January) | Some(Month::February) | ... | Some(Month::December) + + #[inline] + fn from_u64(n: u64) -> Option { + Self::from_u32(n as u32) + } + + #[inline] + fn from_i64(n: i64) -> Option { + Self::from_u32(n as u32) + } + + #[inline] + fn from_u32(n: u32) -> Option { + match n { + 1 => Some(Month::January), + 2 => Some(Month::February), + 3 => Some(Month::March), + 4 => Some(Month::April), + 5 => Some(Month::May), + 6 => Some(Month::June), + 7 => Some(Month::July), + 8 => Some(Month::August), + 9 => Some(Month::September), + 10 => Some(Month::October), + 11 => Some(Month::November), + 12 => Some(Month::December), + _ => None, + } + } +} + +/// An error resulting from reading `` value with `FromStr`. +#[derive(Clone, PartialEq)] +pub struct ParseMonthError { + _dummy: (), +} + +impl fmt::Debug for ParseMonthError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ParseMonthError {{ .. }}") + } +} + +#[cfg(feature = "serde")] +mod month_serde { + use super::Month; + use serdelib::{de, ser}; + + use core::fmt; + + impl ser::Serialize for Month { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.collect_str(self.name()) + } + } + + struct MonthVisitor; + + impl<'de> de::Visitor<'de> for MonthVisitor { + type Value = Month; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Month") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected")) + } + } + + impl<'de> de::Deserialize<'de> for Month { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(MonthVisitor) + } + } + + #[cfg(test)] + extern crate serde_json; + + #[test] + fn test_serde_serialize() { + use self::serde_json::to_string; + use Month::*; + + let cases: Vec<(Month, &str)> = vec![ + (January, "\"January\""), + (February, "\"February\""), + (March, "\"March\""), + (April, "\"April\""), + (May, "\"May\""), + (June, "\"June\""), + (July, "\"July\""), + (August, "\"August\""), + (September, "\"September\""), + (October, "\"October\""), + (November, "\"November\""), + (December, "\"December\""), + ]; + + for (month, expected_str) in cases { + let string = to_string(&month).unwrap(); + assert_eq!(string, expected_str); + } + } + + #[test] + fn test_serde_deserialize() { + use self::serde_json::from_str; + use Month::*; + + let cases: Vec<(&str, Month)> = vec![ + ("\"january\"", January), + ("\"jan\"", January), + ("\"FeB\"", February), + ("\"MAR\"", March), + ("\"mar\"", March), + ("\"april\"", April), + ("\"may\"", May), + ("\"june\"", June), + ("\"JULY\"", July), + ("\"august\"", August), + ("\"september\"", September), + ("\"October\"", October), + ("\"November\"", November), + ("\"DECEmbEr\"", December), + ]; + + for (string, expected_month) in cases { + let month = from_str::(string).unwrap(); + assert_eq!(month, expected_month); + } + + let errors: Vec<&str> = + vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""]; + + for string in errors { + from_str::(string).unwrap_err(); + } + } +} + +/// The common set of methods for date component. +pub trait Datelike: Sized { + /// 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, + /// which is false when the year predates the epoch (BCE/BC) and true otherwise (CE/AD). + #[inline] + fn year_ce(&self) -> (bool, u32) { + let year = self.year(); + if year < 1 { + (false, (1 - year) as u32) + } else { + (true, year as u32) + } + } + + /// Returns the month number starting from 1. + /// + /// The return value ranges from 1 to 12. + fn month(&self) -> u32; + + /// Returns the month number starting from 0. + /// + /// The return value ranges from 0 to 11. + fn month0(&self) -> u32; + + /// Returns the day of month starting from 1. + /// + /// The return value ranges from 1 to 31. (The last day of month differs by months.) + fn day(&self) -> u32; + + /// Returns the day of month starting from 0. + /// + /// The return value ranges from 0 to 30. (The last day of month differs by months.) + fn day0(&self) -> u32; + + /// Returns the day of year starting from 1. + /// + /// The return value ranges from 1 to 366. (The last day of year differs by years.) + fn ordinal(&self) -> u32; + + /// Returns the day of year starting from 0. + /// + /// The return value ranges from 0 to 365. (The last day of year differs by years.) + fn ordinal0(&self) -> u32; + + /// Returns the day of week. + fn weekday(&self) -> Weekday; + + /// Returns the ISO week. + fn iso_week(&self) -> IsoWeek; + + /// Makes a new value with the year number changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_year(&self, year: i32) -> Option; + + /// Makes a new value with the month number (starting from 1) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_month(&self, month: u32) -> Option; + + /// Makes a new value with the month number (starting from 0) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_month0(&self, month0: u32) -> Option; + + /// Makes a new value with the day of month (starting from 1) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_day(&self, day: u32) -> Option; + + /// Makes a new value with the day of month (starting from 0) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_day0(&self, day0: u32) -> Option; + + /// Makes a new value with the day of year (starting from 1) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_ordinal(&self, ordinal: u32) -> Option; + + /// Makes a new value with the day of year (starting from 0) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_ordinal0(&self, ordinal0: u32) -> Option; + + /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1. + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719_163); + /// assert_eq!(NaiveDate::from_ymd(2, 1, 1).num_days_from_ce(), 366); + /// assert_eq!(NaiveDate::from_ymd(1, 1, 1).num_days_from_ce(), 1); + /// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365); + /// ``` + fn num_days_from_ce(&self) -> i32 { + // See test_num_days_from_ce_against_alternative_impl below for a more straightforward + // implementation. + + // we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range. + let mut year = self.year() - 1; + let mut ndays = 0; + if year < 0 { + let excess = 1 + (-year) / 400; + year += excess * 400; + ndays -= excess * 146_097; + } + let div_100 = year / 100; + ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2); + ndays + self.ordinal() as i32 + } +} + +/// The common set of methods for time component. +pub trait Timelike: Sized { + /// Returns the hour number from 0 to 23. + fn hour(&self) -> u32; + + /// Returns the hour number from 1 to 12 with a boolean flag, + /// which is false for AM and true for PM. + #[inline] + fn hour12(&self) -> (bool, u32) { + let hour = self.hour(); + let mut hour12 = hour % 12; + if hour12 == 0 { + hour12 = 12; + } + (hour >= 12, hour12) + } + + /// Returns the minute number from 0 to 59. + fn minute(&self) -> u32; + + /// Returns the second number from 0 to 59. + fn second(&self) -> u32; + + /// 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/struct.NaiveTime.html#leap-second-handling). + fn nanosecond(&self) -> u32; + + /// Makes a new value with the hour number changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_hour(&self, hour: u32) -> Option; + + /// Makes a new value with the minute number changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_minute(&self, min: u32) -> Option; + + /// Makes a new value with the second number changed. + /// + /// Returns `None` when the resulting value would be invalid. + /// As with the [`second`](#tymethod.second) method, + /// the input range is restricted to 0 through 59. + fn with_second(&self, sec: u32) -> Option; + + /// Makes a new value with nanoseconds since the whole non-leap second changed. + /// + /// Returns `None` when the resulting value would be invalid. + /// As with the [`nanosecond`](#tymethod.nanosecond) method, + /// the input range can exceed 1,000,000,000 for leap seconds. + fn with_nanosecond(&self, nano: u32) -> Option; + + /// Returns the number of non-leap seconds past the last midnight. + #[inline] + fn num_seconds_from_midnight(&self) -> u32 { + self.hour() * 3600 + self.minute() * 60 + self.second() + } +} + +#[cfg(test)] +extern crate num_iter; + +mod test { + #[allow(unused_imports)] + use super::*; + + #[test] + fn test_readme_doomsday() { + use num_iter::range_inclusive; + + 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); + let d8 = NaiveDate::from_ymd(y, 8, 8); + let d10 = NaiveDate::from_ymd(y, 10, 10); + let d12 = NaiveDate::from_ymd(y, 12, 12); + + // nine to five, seven-eleven + let d59 = NaiveDate::from_ymd(y, 5, 9); + let d95 = NaiveDate::from_ymd(y, 9, 5); + let d711 = NaiveDate::from_ymd(y, 7, 11); + let d117 = NaiveDate::from_ymd(y, 11, 7); + + // "March 0" + let d30 = NaiveDate::from_ymd(y, 3, 1).pred(); + + let weekday = d30.weekday(); + let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117]; + assert!(other_dates.iter().all(|d| d.weekday() == weekday)); + } + } + + #[test] + fn test_month_enum_primitive_parse() { + use num_traits::FromPrimitive; + + let jan_opt = Month::from_u32(1); + let feb_opt = Month::from_u64(2); + let dec_opt = Month::from_i64(12); + let no_month = Month::from_u32(13); + assert_eq!(jan_opt, Some(Month::January)); + assert_eq!(feb_opt, Some(Month::February)); + assert_eq!(dec_opt, Some(Month::December)); + assert_eq!(no_month, None); + + let date = Utc.ymd(2019, 10, 28).and_hms(9, 10, 11); + assert_eq!(Month::from_u32(date.month()), Some(Month::October)); + + let month = Month::January; + let dt = Utc.ymd(2019, month.number_from_month(), 28).and_hms(9, 10, 11); + assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); + } +} + +/// Tests `Datelike::num_days_from_ce` against an alternative implementation. +/// +/// The alternative implementation is not as short as the current one but it is simpler to +/// understand, with less unexplained magic constants. +#[test] +fn test_num_days_from_ce_against_alternative_impl() { + /// Returns the number of multiples of `div` in the range `start..end`. + /// + /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the + /// behaviour is defined by the following equation: + /// `in_between(start, end, div) == - in_between(end, start, div)`. + /// + /// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`. + /// + /// # Panics + /// + /// Panics if `div` is not positive. + fn in_between(start: i32, end: i32, div: i32) -> i32 { + assert!(div > 0, "in_between: nonpositive div = {}", div); + let start = (start.div_euclid(div), start.rem_euclid(div)); + let end = (end.div_euclid(div), end.rem_euclid(div)); + // The lowest multiple of `div` greater than or equal to `start`, divided. + let start = start.0 + (start.1 != 0) as i32; + // The lowest multiple of `div` greater than or equal to `end`, divided. + let end = end.0 + (end.1 != 0) as i32; + end - start + } + + /// Alternative implementation to `Datelike::num_days_from_ce` + fn num_days_from_ce(date: &Date) -> i32 { + let year = date.year(); + let diff = move |div| in_between(1, year, div); + // 365 days a year, one more in leap years. In the gregorian calendar, leap years are all + // the multiples of 4 except multiples of 100 but including multiples of 400. + date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400) + } + + use num_iter::range_inclusive; + + for year in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) { + let jan1_year = NaiveDate::from_ymd(year, 1, 1); + assert_eq!( + jan1_year.num_days_from_ce(), + num_days_from_ce(&jan1_year), + "on {:?}", + jan1_year + ); + let mid_year = jan1_year + Duration::days(133); + assert_eq!(mid_year.num_days_from_ce(), num_days_from_ce(&mid_year), "on {:?}", mid_year); + } +} + +#[test] +fn test_month_enum_succ_pred() { + assert_eq!(Month::January.succ(), Month::February); + assert_eq!(Month::December.succ(), Month::January); + assert_eq!(Month::January.pred(), Month::December); + assert_eq!(Month::February.pred(), Month::January); +} diff --git a/src/naive/date.rs b/src/naive/date.rs new file mode 100644 index 0000000..3e34e20 --- /dev/null +++ b/src/naive/date.rs @@ -0,0 +1,2392 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 calendar date without timezone. + +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::{fmt, str}; +use num_traits::ToPrimitive; +use oldtime::Duration as OldDuration; + +use div::div_mod_floor; +#[cfg(any(feature = "alloc", feature = "std", test))] +use format::DelayedFormat; +use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use format::{Item, Numeric, Pad}; +use naive::{IsoWeek, NaiveDateTime, NaiveTime}; +use {Datelike, Weekday}; + +use super::internals::{self, DateImpl, Mdf, Of, YearFlags}; +use super::isoweek; + +const MAX_YEAR: i32 = internals::MAX_YEAR; +const MIN_YEAR: i32 = internals::MIN_YEAR; + +// MAX_YEAR-12-31 minus 0000-01-01 +// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + (0001-01-01 minus 0000-01-01) - 1 day +// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + 365 days +// = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365 days +#[cfg(test)] // only used for testing +const MAX_DAYS_FROM_YEAR_0: i32 = + MAX_YEAR * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400 + 365; + +// MIN_YEAR-01-01 minus 0000-01-01 +// = (MIN_YEAR+400n+1)-01-01 minus (400n+1)-01-01 +// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - ((400n+1)-01-01 minus 0001-01-01) +// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - 146097n days +// +// n is set to 1000 for convenience. +#[cfg(test)] // only used for testing +const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 + (MIN_YEAR + 400_000) / 4 + - (MIN_YEAR + 400_000) / 100 + + (MIN_YEAR + 400_000) / 400 + - 146097_000; + +#[cfg(test)] // only used for testing, but duplicated in naive::datetime +const MAX_BITS: usize = 44; + +/// ISO 8601 calendar date without timezone. +/// 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 [`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 +/// +/// 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 the same as that of the [calendar date](#calendar-date). +/// +/// This is currently the internal format of Chrono's date types. +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +pub struct NaiveDate { + ymdf: DateImpl, // (year << 13) | of +} + +/// The minimum possible `NaiveDate` (January 1, 262145 BCE). +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_DATE: NaiveDate = NaiveDate { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o17 /*F*/ }; + +// 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_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_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 + ); +} + +impl NaiveDate { + /// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification. + fn from_of(year: i32, of: Of) -> Option { + if year >= MIN_YEAR && year <= MAX_YEAR && of.valid() { + let Of(of) = of; + Some(NaiveDate { ymdf: (year << 13) | (of as DateImpl) }) + } else { + None + } + } + + /// Makes a new `NaiveDate` from year and packed month-day-flags, with a verification. + fn from_mdf(year: i32, mdf: Mdf) -> Option { + NaiveDate::from_of(year, mdf.to_of()) + } + + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_ymd(2015, 3, 14); + /// assert_eq!(d.year(), 2015); + /// assert_eq!(d.month(), 3); + /// assert_eq!(d.day(), 14); + /// assert_eq!(d.ordinal(), 73); // day of year + /// 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 { + NaiveDate::from_ymd_opt(year, month, day).expect("invalid or out-of-range 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let from_ymd_opt = NaiveDate::from_ymd_opt; + /// + /// assert!(from_ymd_opt(2015, 3, 14).is_some()); + /// assert!(from_ymd_opt(2015, 0, 14).is_none()); + /// assert!(from_ymd_opt(2015, 2, 29).is_none()); + /// assert!(from_ymd_opt(-4, 2, 29).is_some()); // 5 BCE is a leap year + /// assert!(from_ymd_opt(400000, 1, 1).is_none()); + /// assert!(from_ymd_opt(-400000, 1, 1).is_none()); + /// ~~~~ + pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option { + let flags = YearFlags::from_year(year); + NaiveDate::from_mdf(year, Mdf::new(month, day, flags)) + } + + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_yo(2015, 73); + /// assert_eq!(d.ordinal(), 73); + /// assert_eq!(d.year(), 2015); + /// assert_eq!(d.month(), 3); + /// assert_eq!(d.day(), 14); + /// 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 { + NaiveDate::from_yo_opt(year, ordinal).expect("invalid or out-of-range 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let from_yo_opt = NaiveDate::from_yo_opt; + /// + /// assert!(from_yo_opt(2015, 100).is_some()); + /// assert!(from_yo_opt(2015, 0).is_none()); + /// assert!(from_yo_opt(2015, 365).is_some()); + /// assert!(from_yo_opt(2015, 366).is_none()); + /// assert!(from_yo_opt(-4, 366).is_some()); // 5 BCE is a leap year + /// assert!(from_yo_opt(400000, 1).is_none()); + /// assert!(from_yo_opt(-400000, 1).is_none()); + /// ~~~~ + pub fn from_yo_opt(year: i32, ordinal: u32) -> Option { + let flags = YearFlags::from_year(year); + NaiveDate::from_of(year, Of::new(ordinal, flags)) + } + + /// 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. + /// + /// Panics on the out-of-range date and/or invalid week number. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_isoywd(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); + /// assert_eq!(d.ordinal(), 73); // day of year + /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE + /// ~~~~ + pub fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> NaiveDate { + NaiveDate::from_isoywd_opt(year, week, weekday).expect("invalid or out-of-range 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. + /// + /// Returns `None` on the out-of-range date and/or invalid week number. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Weekday}; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// let from_isoywd_opt = NaiveDate::from_isoywd_opt; + /// + /// assert_eq!(from_isoywd_opt(2015, 0, Weekday::Sun), None); + /// assert_eq!(from_isoywd_opt(2015, 10, Weekday::Sun), Some(from_ymd(2015, 3, 8))); + /// assert_eq!(from_isoywd_opt(2015, 30, Weekday::Mon), Some(from_ymd(2015, 7, 20))); + /// assert_eq!(from_isoywd_opt(2015, 60, Weekday::Mon), None); + /// + /// assert_eq!(from_isoywd_opt(400000, 10, Weekday::Fri), None); + /// assert_eq!(from_isoywd_opt(-400000, 10, Weekday::Sat), None); + /// ~~~~ + /// + /// The year number of ISO week date may differ from that of the calendar date. + /// + /// ~~~~ + /// # use chrono::{NaiveDate, Weekday}; + /// # let from_ymd = NaiveDate::from_ymd; + /// # let from_isoywd_opt = NaiveDate::from_isoywd_opt; + /// // Mo Tu We Th Fr Sa Su + /// // 2014-W52 22 23 24 25 26 27 28 has 4+ days of new year, + /// // 2015-W01 29 30 31 1 2 3 4 <- so this is the first week + /// assert_eq!(from_isoywd_opt(2014, 52, Weekday::Sun), Some(from_ymd(2014, 12, 28))); + /// assert_eq!(from_isoywd_opt(2014, 53, Weekday::Mon), None); + /// assert_eq!(from_isoywd_opt(2015, 1, Weekday::Mon), Some(from_ymd(2014, 12, 29))); + /// + /// // 2015-W52 21 22 23 24 25 26 27 has 4+ days of old year, + /// // 2015-W53 28 29 30 31 1 2 3 <- so this is the last week + /// // 2016-W01 4 5 6 7 8 9 10 + /// assert_eq!(from_isoywd_opt(2015, 52, Weekday::Sun), Some(from_ymd(2015, 12, 27))); + /// assert_eq!(from_isoywd_opt(2015, 53, Weekday::Sun), Some(from_ymd(2016, 1, 3))); + /// assert_eq!(from_isoywd_opt(2015, 54, Weekday::Mon), None); + /// assert_eq!(from_isoywd_opt(2016, 1, Weekday::Mon), Some(from_ymd(2016, 1, 4))); + /// ~~~~ + pub fn from_isoywd_opt(year: i32, week: u32, weekday: Weekday) -> Option { + let flags = YearFlags::from_year(year); + let nweeks = flags.nisoweeks(); + if 1 <= week && week <= nweeks { + // ordinal = week ordinal - delta + let weekord = week * 7 + weekday as u32; + let delta = flags.isoweek_delta(); + if weekord <= delta { + // ordinal < 1, previous year + let prevflags = YearFlags::from_year(year - 1); + NaiveDate::from_of( + year - 1, + Of::new(weekord + prevflags.ndays() - delta, prevflags), + ) + } else { + let ordinal = weekord - delta; + let ndays = flags.ndays(); + if ordinal <= ndays { + // this year + NaiveDate::from_of(year, Of::new(ordinal, flags)) + } else { + // ordinal > ndays, next year + let nextflags = YearFlags::from_year(year + 1); + NaiveDate::from_of(year + 1, Of::new(ordinal - ndays, nextflags)) + } + } + } else { + None + } + } + + /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with + /// January 1, 1 being day 1. + /// + /// Panics if the date is out of range. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// let d = NaiveDate::from_num_days_from_ce(735671); + /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE + /// assert_eq!(d.year(), 2015); + /// assert_eq!(d.month(), 3); + /// assert_eq!(d.day(), 14); + /// assert_eq!(d.ordinal(), 73); // day of year + /// 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, + /// it is easy to convert from the Julian day number + /// (January 1, 4713 BCE in the *Julian* calendar being Day 0) + /// to Gregorian with this method. + /// (Note that this panics when `jd` is out of range.) + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// fn jd_to_date(jd: i32) -> NaiveDate { + /// // keep in mind that the Julian day number is 0-based + /// // while this method requires an 1-based number. + /// NaiveDate::from_num_days_from_ce(jd - 1721425) + /// } + /// + /// // January 1, 4713 BCE in Julian = November 24, 4714 BCE in Gregorian + /// assert_eq!(jd_to_date(0), NaiveDate::from_ymd(-4713, 11, 24)); + /// + /// assert_eq!(jd_to_date(1721426), NaiveDate::from_ymd(1, 1, 1)); + /// assert_eq!(jd_to_date(2450000), NaiveDate::from_ymd(1995, 10, 9)); + /// assert_eq!(jd_to_date(2451545), NaiveDate::from_ymd(2000, 1, 1)); + /// ~~~~ + #[inline] + pub fn from_num_days_from_ce(days: i32) -> NaiveDate { + NaiveDate::from_num_days_from_ce_opt(days).expect("out-of-range date") + } + + /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with + /// January 1, 1 being day 1. + /// + /// Returns `None` if the date is out of range. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let from_ndays_opt = NaiveDate::from_num_days_from_ce_opt; + /// let from_ymd = NaiveDate::from_ymd; + /// + /// assert_eq!(from_ndays_opt(730_000), Some(from_ymd(1999, 9, 3))); + /// assert_eq!(from_ndays_opt(1), Some(from_ymd(1, 1, 1))); + /// assert_eq!(from_ndays_opt(0), Some(from_ymd(0, 12, 31))); + /// assert_eq!(from_ndays_opt(-1), Some(from_ymd(0, 12, 30))); + /// assert_eq!(from_ndays_opt(100_000_000), None); + /// assert_eq!(from_ndays_opt(-100_000_000), None); + /// ~~~~ + pub fn from_num_days_from_ce_opt(days: i32) -> Option { + let days = days + 365; // make December 31, 1 BCE equal to day 0 + let (year_div_400, cycle) = div_mod_floor(days, 146_097); + let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); + let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); + NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) + } + + /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week + /// since the beginning of the given month. For instance, if you want the 2nd Friday of March + /// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. + /// + /// # Panics + /// + /// The resulting `NaiveDate` is guaranteed to be in `month`. If `n` is larger than the number + /// of `weekday` in `month` (eg. the 6th Friday of March 2017) then this function will panic. + /// + /// `n` is 1-indexed. Passing `n=0` will cause a panic. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Weekday}; + /// + /// let from_weekday_of_month = NaiveDate::from_weekday_of_month; + /// let from_ymd = NaiveDate::from_ymd; + /// + /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Wed, 1), from_ymd(2018, 8, 1)); + /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 1), from_ymd(2018, 8, 3)); + /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Tue, 2), from_ymd(2018, 8, 14)); + /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 4), from_ymd(2018, 8, 24)); + /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 5), from_ymd(2018, 8, 31)); + /// ~~~~ + pub fn from_weekday_of_month(year: i32, month: u32, weekday: Weekday, n: u8) -> NaiveDate { + NaiveDate::from_weekday_of_month_opt(year, month, weekday, n).expect("out-of-range date") + } + + /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week + /// since the beginning of the given month. For instance, if you want the 2nd Friday of March + /// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. `n` is 1-indexed. + /// + /// ~~~~ + /// use chrono::{NaiveDate, Weekday}; + /// assert_eq!(NaiveDate::from_weekday_of_month_opt(2017, 3, Weekday::Fri, 2), + /// NaiveDate::from_ymd_opt(2017, 3, 10)) + /// ~~~~ + /// + /// Returns `None` if `n` out-of-range; ie. if `n` is larger than the number of `weekday` in + /// `month` (eg. the 6th Friday of March 2017), or if `n == 0`. + pub fn from_weekday_of_month_opt( + year: i32, + month: u32, + weekday: Weekday, + n: u8, + ) -> Option { + if n == 0 { + return None; + } + let first = NaiveDate::from_ymd(year, month, 1).weekday(); + let first_to_dow = (7 + weekday.number_from_monday() - first.number_from_monday()) % 7; + let day = (u32::from(n) - 1) * 7 + first_to_dow + 1; + NaiveDate::from_ymd_opt(year, month, day) + } + + /// Parses a string with the specified format string and returns a new `NaiveDate`. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let parse_from_str = NaiveDate::parse_from_str; + /// + /// assert_eq!(parse_from_str("2015-09-05", "%Y-%m-%d"), + /// Ok(NaiveDate::from_ymd(2015, 9, 5))); + /// assert_eq!(parse_from_str("5sep2015", "%d%b%Y"), + /// Ok(NaiveDate::from_ymd(2015, 9, 5))); + /// ~~~~ + /// + /// Time and offset is ignored for the purpose of parsing. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # let parse_from_str = NaiveDate::parse_from_str; + /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + /// Ok(NaiveDate::from_ymd(2014, 5, 17))); + /// ~~~~ + /// + /// Out-of-bound dates or insufficient fields are errors. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # let parse_from_str = NaiveDate::parse_from_str; + /// assert!(parse_from_str("2015/9", "%Y/%m").is_err()); + /// assert!(parse_from_str("2015/9/31", "%Y/%m/%d").is_err()); + /// ~~~~ + /// + /// All parsed fields should be consistent to each other, otherwise it's an error. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # let parse_from_str = NaiveDate::parse_from_str; + /// assert!(parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err()); + /// ~~~~ + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_date() + } + + /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveTime, NaiveDateTime}; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let t = NaiveTime::from_hms_milli(12, 34, 56, 789); + /// + /// let dt: NaiveDateTime = d.and_time(t); + /// assert_eq!(dt.date(), d); + /// assert_eq!(dt.time(), t); + /// ~~~~ + #[inline] + pub fn and_time(&self, time: NaiveTime) -> NaiveDateTime { + NaiveDateTime::new(*self, time) + } + + /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. + /// + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// + /// let dt: NaiveDateTime = d.and_hms(12, 34, 56); + /// assert_eq!(dt.year(), 2015); + /// assert_eq!(dt.weekday(), Weekday::Wed); + /// assert_eq!(dt.second(), 56); + /// ~~~~ + #[inline] + pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> NaiveDateTime { + self.and_hms_opt(hour, min, sec).expect("invalid time") + } + + /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. + /// + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// assert!(d.and_hms_opt(12, 34, 56).is_some()); + /// assert!(d.and_hms_opt(12, 34, 60).is_none()); // use `and_hms_milli_opt` instead + /// assert!(d.and_hms_opt(12, 60, 56).is_none()); + /// assert!(d.and_hms_opt(24, 34, 56).is_none()); + /// ~~~~ + #[inline] + pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option { + NaiveTime::from_hms_opt(hour, min, sec).map(|time| self.and_time(time)) + } + + /// 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](./struct.NaiveTime.html#leap-second-handling). + /// + /// Panics on invalid hour, minute, second and/or millisecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// + /// let dt: NaiveDateTime = d.and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.year(), 2015); + /// assert_eq!(dt.weekday(), Weekday::Wed); + /// assert_eq!(dt.second(), 56); + /// assert_eq!(dt.nanosecond(), 789_000_000); + /// ~~~~ + #[inline] + pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> NaiveDateTime { + self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time") + } + + /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. + /// + /// The millisecond part can exceed 1,000 + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). + /// + /// Returns `None` on invalid hour, minute, second and/or millisecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// assert!(d.and_hms_milli_opt(12, 34, 56, 789).is_some()); + /// assert!(d.and_hms_milli_opt(12, 34, 59, 1_789).is_some()); // leap second + /// assert!(d.and_hms_milli_opt(12, 34, 59, 2_789).is_none()); + /// assert!(d.and_hms_milli_opt(12, 34, 60, 789).is_none()); + /// assert!(d.and_hms_milli_opt(12, 60, 56, 789).is_none()); + /// assert!(d.and_hms_milli_opt(24, 34, 56, 789).is_none()); + /// ~~~~ + #[inline] + pub fn and_hms_milli_opt( + &self, + hour: u32, + min: u32, + sec: u32, + milli: u32, + ) -> Option { + NaiveTime::from_hms_milli_opt(hour, min, sec, milli).map(|time| self.and_time(time)) + } + + /// 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](./struct.NaiveTime.html#leap-second-handling). + /// + /// Panics on invalid hour, minute, second and/or microsecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// + /// let dt: NaiveDateTime = d.and_hms_micro(12, 34, 56, 789_012); + /// assert_eq!(dt.year(), 2015); + /// assert_eq!(dt.weekday(), Weekday::Wed); + /// assert_eq!(dt.second(), 56); + /// assert_eq!(dt.nanosecond(), 789_012_000); + /// ~~~~ + #[inline] + pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> NaiveDateTime { + self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time") + } + + /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. + /// + /// The microsecond part can exceed 1,000,000 + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). + /// + /// Returns `None` on invalid hour, minute, second and/or microsecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// assert!(d.and_hms_micro_opt(12, 34, 56, 789_012).is_some()); + /// assert!(d.and_hms_micro_opt(12, 34, 59, 1_789_012).is_some()); // leap second + /// assert!(d.and_hms_micro_opt(12, 34, 59, 2_789_012).is_none()); + /// assert!(d.and_hms_micro_opt(12, 34, 60, 789_012).is_none()); + /// assert!(d.and_hms_micro_opt(12, 60, 56, 789_012).is_none()); + /// assert!(d.and_hms_micro_opt(24, 34, 56, 789_012).is_none()); + /// ~~~~ + #[inline] + pub fn and_hms_micro_opt( + &self, + hour: u32, + min: u32, + sec: u32, + micro: u32, + ) -> Option { + NaiveTime::from_hms_micro_opt(hour, min, sec, micro).map(|time| self.and_time(time)) + } + + /// 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](./struct.NaiveTime.html#leap-second-handling). + /// + /// Panics on invalid hour, minute, second and/or nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// + /// let dt: NaiveDateTime = d.and_hms_nano(12, 34, 56, 789_012_345); + /// assert_eq!(dt.year(), 2015); + /// assert_eq!(dt.weekday(), Weekday::Wed); + /// assert_eq!(dt.second(), 56); + /// assert_eq!(dt.nanosecond(), 789_012_345); + /// ~~~~ + #[inline] + pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> NaiveDateTime { + self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time") + } + + /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. + /// + /// The nanosecond part can exceed 1,000,000,000 + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). + /// + /// Returns `None` on invalid hour, minute, second and/or nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// assert!(d.and_hms_nano_opt(12, 34, 56, 789_012_345).is_some()); + /// assert!(d.and_hms_nano_opt(12, 34, 59, 1_789_012_345).is_some()); // leap second + /// assert!(d.and_hms_nano_opt(12, 34, 59, 2_789_012_345).is_none()); + /// assert!(d.and_hms_nano_opt(12, 34, 60, 789_012_345).is_none()); + /// assert!(d.and_hms_nano_opt(12, 60, 56, 789_012_345).is_none()); + /// assert!(d.and_hms_nano_opt(24, 34, 56, 789_012_345).is_none()); + /// ~~~~ + #[inline] + pub fn and_hms_nano_opt( + &self, + hour: u32, + min: u32, + sec: u32, + nano: u32, + ) -> Option { + NaiveTime::from_hms_nano_opt(hour, min, sec, nano).map(|time| self.and_time(time)) + } + + /// Returns the packed month-day-flags. + #[inline] + fn mdf(&self) -> Mdf { + self.of().to_mdf() + } + + /// Returns the packed ordinal-flags. + #[inline] + fn of(&self) -> Of { + Of((self.ymdf & 0b1_1111_1111_1111) as u32) + } + + /// Makes a new `NaiveDate` with the packed month-day-flags changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + #[inline] + fn with_mdf(&self, mdf: Mdf) -> Option { + self.with_of(mdf.to_of()) + } + + /// Makes a new `NaiveDate` with the packed ordinal-flags changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + #[inline] + fn with_of(&self, of: Of) -> Option { + if of.valid() { + let Of(of) = of; + Some(NaiveDate { ymdf: (self.ymdf & !0b1_1111_1111_1111) | of as DateImpl }) + } else { + None + } + } + + /// Makes a new `NaiveDate` for the next calendar date. + /// + /// Panics when `self` is the last representable date. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).succ(), NaiveDate::from_ymd(2015, 6, 4)); + /// assert_eq!(NaiveDate::from_ymd(2015, 6, 30).succ(), NaiveDate::from_ymd(2015, 7, 1)); + /// assert_eq!(NaiveDate::from_ymd(2015, 12, 31).succ(), NaiveDate::from_ymd(2016, 1, 1)); + /// ~~~~ + #[inline] + pub fn succ(&self) -> NaiveDate { + self.succ_opt().expect("out of bound") + } + + /// Makes a new `NaiveDate` for the next calendar date. + /// + /// Returns `None` when `self` is the last representable date. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// 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_DATE.succ_opt(), None); + /// ~~~~ + #[inline] + pub fn succ_opt(&self) -> Option { + self.with_of(self.of().succ()).or_else(|| NaiveDate::from_ymd_opt(self.year() + 1, 1, 1)) + } + + /// Makes a new `NaiveDate` for the previous calendar date. + /// + /// Panics when `self` is the first representable date. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).pred(), NaiveDate::from_ymd(2015, 6, 2)); + /// assert_eq!(NaiveDate::from_ymd(2015, 6, 1).pred(), NaiveDate::from_ymd(2015, 5, 31)); + /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).pred(), NaiveDate::from_ymd(2014, 12, 31)); + /// ~~~~ + #[inline] + pub fn pred(&self) -> NaiveDate { + self.pred_opt().expect("out of bound") + } + + /// Makes a new `NaiveDate` for the previous calendar date. + /// + /// Returns `None` when `self` is the first representable date. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// 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_DATE.pred_opt(), None); + /// ~~~~ + #[inline] + pub fn pred_opt(&self) -> Option { + self.with_of(self.of().pred()).or_else(|| NaiveDate::from_ymd_opt(self.year() - 1, 12, 31)) + } + + /// Adds the `days` part of given `Duration` to the current date. + /// + /// Returns `None` when it will result in overflow. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveDate}; + /// use chrono::naive::MAX_DATE; + /// + /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// 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_DATE.checked_add_signed(Duration::days(1)), None); + /// # } + /// ~~~~ + 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()); + let cycle = try_opt!((cycle as i32).checked_add(try_opt!(rhs.num_days().to_i32()))); + let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); + year_div_400 += cycle_div_400y; + + let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); + let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); + NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) + } + + /// Subtracts the `days` part of given `Duration` from the current date. + /// + /// Returns `None` when it will result in overflow. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveDate}; + /// use chrono::naive::MIN_DATE; + /// + /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// 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_DATE.checked_sub_signed(Duration::days(1)), None); + /// # } + /// ~~~~ + 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()); + let cycle = try_opt!((cycle as i32).checked_sub(try_opt!(rhs.num_days().to_i32()))); + let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); + year_div_400 += cycle_div_400y; + + let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); + let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); + NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, 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; fn main() { + /// use chrono::{Duration, NaiveDate}; + /// + /// 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 = i64::from(internals::yo_to_cycle(year1_mod_400 as u32, self.of().ordinal())); + let cycle2 = i64::from(internals::yo_to_cycle(year2_mod_400 as u32, rhs.of().ordinal())); + OldDuration::days( + (i64::from(year1_div_400) - i64::from(year2_div_400)) * 146_097 + (cycle1 - cycle2), + ) + } + + /// Formats the date with the specified formatting items. + /// Otherwise it is the same as the ordinary `format` method. + /// + /// The `Iterator` of items should be `Clone`able, + /// since the resulting `DelayedFormat` value may be formatted multiple times. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// use chrono::format::strftime::StrftimeItems; + /// + /// let fmt = StrftimeItems::new("%Y-%m-%d"); + /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// assert_eq!(d.format_with_items(fmt.clone()).to_string(), "2015-09-05"); + /// assert_eq!(d.format("%Y-%m-%d").to_string(), "2015-09-05"); + /// ~~~~ + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # use chrono::format::strftime::StrftimeItems; + /// # let fmt = StrftimeItems::new("%Y-%m-%d").clone(); + /// # let d = NaiveDate::from_ymd(2015, 9, 5); + /// assert_eq!(format!("{}", d.format_with_items(fmt)), "2015-09-05"); + /// ~~~~ + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + DelayedFormat::new(Some(*self), None, items) + } + + /// Formats the date with the specified format string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// This returns a `DelayedFormat`, + /// which gets converted to a string only when actual formatting happens. + /// You may use the `to_string` method to get a `String`, + /// or just feed it into `print!` and other formatting macros. + /// (In this way it avoids the redundant memory allocation.) + /// + /// A wrong format string does *not* issue an error immediately. + /// Rather, converting or formatting the `DelayedFormat` fails. + /// You are recommended to immediately use `DelayedFormat` for this reason. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// assert_eq!(d.format("%Y-%m-%d").to_string(), "2015-09-05"); + /// assert_eq!(d.format("%A, %-d %B, %C%y").to_string(), "Saturday, 5 September, 2015"); + /// ~~~~ + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # let d = NaiveDate::from_ymd(2015, 9, 5); + /// assert_eq!(format!("{}", d.format("%Y-%m-%d")), "2015-09-05"); + /// assert_eq!(format!("{}", d.format("%A, %-d %B, %C%y")), "Saturday, 5 September, 2015"); + /// ~~~~ + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { + self.format_with_items(StrftimeItems::new(fmt)) + } + + /// Returns an iterator that steps by days until the last representable date. + /// + /// # Example + /// + /// ``` + /// # use chrono::NaiveDate; + /// + /// let expected = [ + /// NaiveDate::from_ymd(2016, 2, 27), + /// NaiveDate::from_ymd(2016, 2, 28), + /// NaiveDate::from_ymd(2016, 2, 29), + /// NaiveDate::from_ymd(2016, 3, 1), + /// ]; + /// + /// let mut count = 0; + /// for (idx, d) in NaiveDate::from_ymd(2016, 2, 27).iter_days().take(4).enumerate() { + /// assert_eq!(d, expected[idx]); + /// count += 1; + /// } + /// assert_eq!(count, 4); + /// ``` + #[inline] + pub fn iter_days(&self) -> NaiveDateDaysIterator { + NaiveDateDaysIterator { value: *self } + } + + /// Returns an iterator that steps by weeks until the last representable date. + /// + /// # Example + /// + /// ``` + /// # use chrono::NaiveDate; + /// + /// let expected = [ + /// NaiveDate::from_ymd(2016, 2, 27), + /// NaiveDate::from_ymd(2016, 3, 5), + /// NaiveDate::from_ymd(2016, 3, 12), + /// NaiveDate::from_ymd(2016, 3, 19), + /// ]; + /// + /// let mut count = 0; + /// for (idx, d) in NaiveDate::from_ymd(2016, 2, 27).iter_weeks().take(4).enumerate() { + /// assert_eq!(d, expected[idx]); + /// count += 1; + /// } + /// assert_eq!(count, 4); + /// ``` + #[inline] + pub fn iter_weeks(&self) -> NaiveDateWeeksIterator { + NaiveDateWeeksIterator { value: *self } + } +} + +impl Datelike for NaiveDate { + /// Returns the year number in the [calendar date](#calendar-date). + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).year(), 2015); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).year(), -308); // 309 BCE + /// ~~~~ + #[inline] + fn year(&self) -> i32 { + self.ymdf >> 13 + } + + /// Returns the month number starting from 1. + /// + /// The return value ranges from 1 to 12. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).month(), 9); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).month(), 3); + /// ~~~~ + #[inline] + fn month(&self) -> u32 { + self.mdf().month() + } + + /// Returns the month number starting from 0. + /// + /// The return value ranges from 0 to 11. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).month0(), 8); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).month0(), 2); + /// ~~~~ + #[inline] + fn month0(&self) -> u32 { + self.mdf().month() - 1 + } + + /// Returns the day of month starting from 1. + /// + /// The return value ranges from 1 to 31. (The last day of month differs by months.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).day(), 8); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).day(), 14); + /// ~~~~ + /// + /// 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.) + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// fn ndays_in_month(year: i32, month: u32) -> u32 { + /// // the first day of the next month... + /// let (y, m) = if month == 12 { (year + 1, 1) } else { (year, month + 1) }; + /// let d = NaiveDate::from_ymd(y, m, 1); + /// + /// // ...is preceded by the last day of the original month + /// d.pred().day() + /// } + /// + /// assert_eq!(ndays_in_month(2015, 8), 31); + /// assert_eq!(ndays_in_month(2015, 9), 30); + /// assert_eq!(ndays_in_month(2015, 12), 31); + /// assert_eq!(ndays_in_month(2016, 2), 29); + /// assert_eq!(ndays_in_month(2017, 2), 28); + /// ~~~~ + #[inline] + fn day(&self) -> u32 { + self.mdf().day() + } + + /// Returns the day of month starting from 0. + /// + /// The return value ranges from 0 to 30. (The last day of month differs by months.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).day0(), 7); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).day0(), 13); + /// ~~~~ + #[inline] + fn day0(&self) -> u32 { + self.mdf().day() - 1 + } + + /// Returns the day of year starting from 1. + /// + /// The return value ranges from 1 to 366. (The last day of year differs by years.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).ordinal(), 251); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).ordinal(), 74); + /// ~~~~ + /// + /// 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.) + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// fn ndays_in_year(year: i32) -> u32 { + /// // the first day of the next year... + /// let d = NaiveDate::from_ymd(year + 1, 1, 1); + /// + /// // ...is preceded by the last day of the original year + /// d.pred().ordinal() + /// } + /// + /// assert_eq!(ndays_in_year(2015), 365); + /// assert_eq!(ndays_in_year(2016), 366); + /// assert_eq!(ndays_in_year(2017), 365); + /// assert_eq!(ndays_in_year(2000), 366); + /// assert_eq!(ndays_in_year(2100), 365); + /// ~~~~ + #[inline] + fn ordinal(&self) -> u32 { + self.of().ordinal() + } + + /// Returns the day of year starting from 0. + /// + /// The return value ranges from 0 to 365. (The last day of year differs by years.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).ordinal0(), 250); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).ordinal0(), 73); + /// ~~~~ + #[inline] + fn ordinal0(&self) -> u32 { + self.of().ordinal() - 1 + } + + /// Returns the day of week. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike, Weekday}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).weekday(), Weekday::Tue); + /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).weekday(), Weekday::Fri); + /// ~~~~ + #[inline] + fn weekday(&self) -> Weekday { + self.of().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. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_year(2016), + /// Some(NaiveDate::from_ymd(2016, 9, 8))); + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_year(-308), + /// Some(NaiveDate::from_ymd(-308, 9, 8))); + /// ~~~~ + /// + /// A leap day (February 29) is a good example that this method can return `None`. + /// + /// ~~~~ + /// # use chrono::{NaiveDate, Datelike}; + /// assert!(NaiveDate::from_ymd(2016, 2, 29).with_year(2015).is_none()); + /// assert!(NaiveDate::from_ymd(2016, 2, 29).with_year(2020).is_some()); + /// ~~~~ + #[inline] + fn with_year(&self, year: i32) -> Option { + // we need to operate with `mdf` since we should keep the month and day number as is + let mdf = self.mdf(); + + // adjust the flags as needed + let flags = YearFlags::from_year(year); + let mdf = mdf.with_flags(flags); + + NaiveDate::from_mdf(year, mdf) + } + + /// Makes a new `NaiveDate` with the month number (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month(10), + /// Some(NaiveDate::from_ymd(2015, 10, 8))); + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month(13), None); // no month 13 + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 30).with_month(2), None); // no February 30 + /// ~~~~ + #[inline] + fn with_month(&self, month: u32) -> Option { + self.with_mdf(self.mdf().with_month(month)) + } + + /// Makes a new `NaiveDate` with the month number (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month0(9), + /// Some(NaiveDate::from_ymd(2015, 10, 8))); + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month0(12), None); // no month 13 + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 30).with_month0(1), None); // no February 30 + /// ~~~~ + #[inline] + fn with_month0(&self, month0: u32) -> Option { + self.with_mdf(self.mdf().with_month(month0 + 1)) + } + + /// Makes a new `NaiveDate` with the day of month (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day(30), + /// Some(NaiveDate::from_ymd(2015, 9, 30))); + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day(31), + /// None); // no September 31 + /// ~~~~ + #[inline] + fn with_day(&self, day: u32) -> Option { + self.with_mdf(self.mdf().with_day(day)) + } + + /// Makes a new `NaiveDate` with the day of month (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day0(29), + /// Some(NaiveDate::from_ymd(2015, 9, 30))); + /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day0(30), + /// None); // no September 31 + /// ~~~~ + #[inline] + fn with_day0(&self, day0: u32) -> Option { + self.with_mdf(self.mdf().with_day(day0 + 1)) + } + + /// Makes a new `NaiveDate` with the day of year (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal(60), + /// Some(NaiveDate::from_ymd(2015, 3, 1))); + /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal(366), + /// None); // 2015 had only 365 days + /// + /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal(60), + /// Some(NaiveDate::from_ymd(2016, 2, 29))); + /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal(366), + /// Some(NaiveDate::from_ymd(2016, 12, 31))); + /// ~~~~ + #[inline] + fn with_ordinal(&self, ordinal: u32) -> Option { + self.with_of(self.of().with_ordinal(ordinal)) + } + + /// Makes a new `NaiveDate` with the day of year (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDate` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal0(59), + /// Some(NaiveDate::from_ymd(2015, 3, 1))); + /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal0(365), + /// None); // 2015 had only 365 days + /// + /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal0(59), + /// Some(NaiveDate::from_ymd(2016, 2, 29))); + /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal0(365), + /// Some(NaiveDate::from_ymd(2016, 12, 31))); + /// ~~~~ + #[inline] + fn with_ordinal0(&self, ordinal0: u32) -> Option { + self.with_of(self.of().with_ordinal(ordinal0 + 1)) + } +} + +/// An addition of `Duration` to `NaiveDate` discards the fractional days, +/// rounding to the closest integral number of days towards `Duration::zero()`. +/// +/// Panics on underflow or overflow. +/// Use [`NaiveDate::checked_add_signed`](#method.checked_add_signed) to detect that. +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveDate}; +/// +/// let from_ymd = NaiveDate::from_ymd; +/// +/// assert_eq!(from_ymd(2014, 1, 1) + Duration::zero(), from_ymd(2014, 1, 1)); +/// assert_eq!(from_ymd(2014, 1, 1) + Duration::seconds(86399), from_ymd(2014, 1, 1)); +/// assert_eq!(from_ymd(2014, 1, 1) + Duration::seconds(-86399), from_ymd(2014, 1, 1)); +/// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(1), from_ymd(2014, 1, 2)); +/// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(-1), from_ymd(2013, 12, 31)); +/// 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 { + type Output = NaiveDate; + + #[inline] + fn add(self, rhs: OldDuration) -> NaiveDate { + self.checked_add_signed(rhs).expect("`NaiveDate + Duration` overflowed") + } +} + +impl AddAssign for NaiveDate { + #[inline] + fn add_assign(&mut self, rhs: OldDuration) { + *self = self.add(rhs); + } +} + +/// A subtraction of `Duration` from `NaiveDate` discards the fractional days, +/// rounding to the closest integral number of days towards `Duration::zero()`. +/// It is the same as the addition with a negated `Duration`. +/// +/// Panics on underflow or overflow. +/// Use [`NaiveDate::checked_sub_signed`](#method.checked_sub_signed) to detect that. +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveDate}; +/// +/// let from_ymd = NaiveDate::from_ymd; +/// +/// assert_eq!(from_ymd(2014, 1, 1) - Duration::zero(), from_ymd(2014, 1, 1)); +/// assert_eq!(from_ymd(2014, 1, 1) - Duration::seconds(86399), from_ymd(2014, 1, 1)); +/// assert_eq!(from_ymd(2014, 1, 1) - Duration::seconds(-86399), from_ymd(2014, 1, 1)); +/// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(1), from_ymd(2013, 12, 31)); +/// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(-1), from_ymd(2014, 1, 2)); +/// 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 { + type Output = NaiveDate; + + #[inline] + fn sub(self, rhs: OldDuration) -> NaiveDate { + self.checked_sub_signed(rhs).expect("`NaiveDate - Duration` overflowed") + } +} + +impl SubAssign for NaiveDate { + #[inline] + fn sub_assign(&mut self, rhs: OldDuration) { + *self = self.sub(rhs); + } +} + +/// 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`. +/// +/// The implementation is a wrapper around +/// [`NaiveDate::signed_duration_since`](#method.signed_duration_since). +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveDate}; +/// +/// 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 = OldDuration; + + #[inline] + fn sub(self, rhs: NaiveDate) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +/// Iterator over `NaiveDate` with a step size of one day. +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +pub struct NaiveDateDaysIterator { + value: NaiveDate, +} + +impl Iterator for NaiveDateDaysIterator { + type Item = NaiveDate; + + fn next(&mut self) -> Option { + if self.value == MAX_DATE { + return None; + } + // current < MAX_DATE from here on: + let current = self.value; + // This can't panic because current is < MAX_DATE: + self.value = current.succ(); + Some(current) + } + + fn size_hint(&self) -> (usize, Option) { + let exact_size = MAX_DATE.signed_duration_since(self.value).num_days(); + (exact_size as usize, Some(exact_size as usize)) + } +} + +impl ExactSizeIterator for NaiveDateDaysIterator {} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +pub struct NaiveDateWeeksIterator { + value: NaiveDate, +} + +impl Iterator for NaiveDateWeeksIterator { + type Item = NaiveDate; + + fn next(&mut self) -> Option { + if MAX_DATE - self.value < OldDuration::weeks(1) { + return None; + } + let current = self.value; + self.value = current + OldDuration::weeks(1); + Some(current) + } + + fn size_hint(&self) -> (usize, Option) { + let exact_size = MAX_DATE.signed_duration_since(self.value).num_weeks(); + (exact_size as usize, Some(exact_size as usize)) + } +} + +impl ExactSizeIterator for NaiveDateWeeksIterator {} + +// TODO: NaiveDateDaysIterator and NaiveDateWeeksIterator should implement FusedIterator, +// TrustedLen, and Step once they becomes stable. +// See: https://github.com/chronotope/chrono/issues/208 + +/// The `Debug` output of the naive date `d` is the same as +/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html). +/// +/// The string printed can be readily parsed via the `parse` method on `str`. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveDate; +/// +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5)), "2015-09-05"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 1)), "0000-01-01"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31)), "9999-12-31"); +/// ~~~~ +/// +/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. +/// +/// ~~~~ +/// # use chrono::NaiveDate; +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( -1, 1, 1)), "-0001-01-01"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31)), "+10000-12-31"); +/// ~~~~ +impl fmt::Debug for NaiveDate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let year = self.year(); + let mdf = self.mdf(); + if 0 <= year && year <= 9999 { + write!(f, "{:04}-{:02}-{:02}", year, mdf.month(), mdf.day()) + } else { + // ISO 8601 requires the explicit sign for out-of-range years + write!(f, "{:+05}-{:02}-{:02}", year, mdf.month(), mdf.day()) + } + } +} + +/// The `Display` output of the naive date `d` is the same as +/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html). +/// +/// The string printed can be readily parsed via the `parse` method on `str`. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveDate; +/// +/// assert_eq!(format!("{}", NaiveDate::from_ymd(2015, 9, 5)), "2015-09-05"); +/// assert_eq!(format!("{}", NaiveDate::from_ymd( 0, 1, 1)), "0000-01-01"); +/// assert_eq!(format!("{}", NaiveDate::from_ymd(9999, 12, 31)), "9999-12-31"); +/// ~~~~ +/// +/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. +/// +/// ~~~~ +/// # use chrono::NaiveDate; +/// assert_eq!(format!("{}", NaiveDate::from_ymd( -1, 1, 1)), "-0001-01-01"); +/// assert_eq!(format!("{}", NaiveDate::from_ymd(10000, 12, 31)), "+10000-12-31"); +/// ~~~~ +impl fmt::Display for NaiveDate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +/// Parsing a `str` into a `NaiveDate` uses the same format, +/// [`%Y-%m-%d`](../format/strftime/index.html), as in `Debug` and `Display`. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveDate; +/// +/// let d = NaiveDate::from_ymd(2015, 9, 18); +/// assert_eq!("2015-09-18".parse::(), Ok(d)); +/// +/// let d = NaiveDate::from_ymd(12345, 6, 7); +/// assert_eq!("+12345-6-7".parse::(), Ok(d)); +/// +/// assert!("foo".parse::().is_err()); +/// ~~~~ +impl str::FromStr for NaiveDate { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult { + const ITEMS: &'static [Item<'static>] = &[ + Item::Numeric(Numeric::Year, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Month, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Day, Pad::Zero), + Item::Space(""), + ]; + + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_naive_date() + } +} + +#[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_DATE).ok(), Some(r#""-262144-01-01""#.into())); + assert_eq!(to_string(&MAX_DATE).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_DATE)); + assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(MAX_DATE)); + + // 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::{Decodable, Decoder, Encodable, Encoder}; + + impl Encodable for NaiveDate { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } + } + + impl Decodable for NaiveDate { + fn decode(d: &mut D) -> Result { + d.read_str()?.parse().map_err(|_| d.error("invalid date")) + } + } + + #[cfg(test)] + use rustc_serialize::json; + + #[test] + fn test_encodable() { + super::test_encodable_json(json::encode); + } + + #[test] + fn test_decodable() { + super::test_decodable_json(json::decode); + } +} + +#[cfg(feature = "serde")] +mod serde { + use super::NaiveDate; + use core::fmt; + use serdelib::{de, ser}; + + // TODO not very optimized for space (binary formats would want something better) + + impl ser::Serialize for NaiveDate { + 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) + } + } + + serializer.collect_str(&FormatWrapped { inner: &self }) + } + } + + struct NaiveDateVisitor; + + impl<'de> de::Visitor<'de> for NaiveDateVisitor { + type Value = NaiveDate; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a formatted date string") + } + + #[cfg(any(feature = "std", test))] + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + value.parse().map_err(E::custom) + } + + #[cfg(not(any(feature = "std", test)))] + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + value.parse().map_err(E::custom) + } + } + + impl<'de> de::Deserialize<'de> for NaiveDate { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(NaiveDateVisitor) + } + } + + #[cfg(test)] + extern crate bincode; + #[cfg(test)] + extern crate serde_json; + + #[test] + fn test_serde_serialize() { + super::test_encodable_json(self::serde_json::to_string); + } + + #[test] + fn test_serde_deserialize() { + super::test_decodable_json(|input| self::serde_json::from_str(&input)); + } + + #[test] + fn test_serde_bincode() { + // Bincode is relevant to test separately from JSON because + // it is not self-describing. + use self::bincode::{deserialize, serialize, Infinite}; + + let d = NaiveDate::from_ymd(2014, 7, 24); + let encoded = serialize(&d, Infinite).unwrap(); + let decoded: NaiveDate = deserialize(&encoded).unwrap(); + assert_eq!(d, decoded); + } +} + +#[cfg(test)] +mod tests { + use super::NaiveDate; + use super::{MAX_DATE, MAX_DAYS_FROM_YEAR_0, MAX_YEAR}; + use super::{MIN_DATE, MIN_DAYS_FROM_YEAR_0, MIN_YEAR}; + use oldtime::Duration; + use std::{i32, u32}; + use {Datelike, Weekday}; + + #[test] + fn test_date_from_ymd() { + let ymd_opt = |y, m, d| NaiveDate::from_ymd_opt(y, m, d); + + assert!(ymd_opt(2012, 0, 1).is_none()); + assert!(ymd_opt(2012, 1, 1).is_some()); + assert!(ymd_opt(2012, 2, 29).is_some()); + assert!(ymd_opt(2014, 2, 29).is_none()); + assert!(ymd_opt(2014, 3, 0).is_none()); + assert!(ymd_opt(2014, 3, 1).is_some()); + assert!(ymd_opt(2014, 3, 31).is_some()); + assert!(ymd_opt(2014, 3, 32).is_none()); + assert!(ymd_opt(2014, 12, 31).is_some()); + assert!(ymd_opt(2014, 13, 1).is_none()); + } + + #[test] + fn test_date_from_yo() { + let yo_opt = |y, o| NaiveDate::from_yo_opt(y, o); + let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + + assert_eq!(yo_opt(2012, 0), None); + assert_eq!(yo_opt(2012, 1), Some(ymd(2012, 1, 1))); + assert_eq!(yo_opt(2012, 2), Some(ymd(2012, 1, 2))); + assert_eq!(yo_opt(2012, 32), Some(ymd(2012, 2, 1))); + assert_eq!(yo_opt(2012, 60), Some(ymd(2012, 2, 29))); + assert_eq!(yo_opt(2012, 61), Some(ymd(2012, 3, 1))); + assert_eq!(yo_opt(2012, 100), Some(ymd(2012, 4, 9))); + assert_eq!(yo_opt(2012, 200), Some(ymd(2012, 7, 18))); + assert_eq!(yo_opt(2012, 300), Some(ymd(2012, 10, 26))); + assert_eq!(yo_opt(2012, 366), Some(ymd(2012, 12, 31))); + assert_eq!(yo_opt(2012, 367), None); + + assert_eq!(yo_opt(2014, 0), None); + assert_eq!(yo_opt(2014, 1), Some(ymd(2014, 1, 1))); + assert_eq!(yo_opt(2014, 2), Some(ymd(2014, 1, 2))); + assert_eq!(yo_opt(2014, 32), Some(ymd(2014, 2, 1))); + assert_eq!(yo_opt(2014, 59), Some(ymd(2014, 2, 28))); + assert_eq!(yo_opt(2014, 60), Some(ymd(2014, 3, 1))); + assert_eq!(yo_opt(2014, 100), Some(ymd(2014, 4, 10))); + assert_eq!(yo_opt(2014, 200), Some(ymd(2014, 7, 19))); + assert_eq!(yo_opt(2014, 300), Some(ymd(2014, 10, 27))); + assert_eq!(yo_opt(2014, 365), Some(ymd(2014, 12, 31))); + assert_eq!(yo_opt(2014, 366), None); + } + + #[test] + fn test_date_from_isoywd() { + let isoywd_opt = |y, w, d| NaiveDate::from_isoywd_opt(y, w, d); + let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + + assert_eq!(isoywd_opt(2004, 0, Weekday::Sun), None); + assert_eq!(isoywd_opt(2004, 1, Weekday::Mon), Some(ymd(2003, 12, 29))); + assert_eq!(isoywd_opt(2004, 1, Weekday::Sun), Some(ymd(2004, 1, 4))); + assert_eq!(isoywd_opt(2004, 2, Weekday::Mon), Some(ymd(2004, 1, 5))); + assert_eq!(isoywd_opt(2004, 2, Weekday::Sun), Some(ymd(2004, 1, 11))); + assert_eq!(isoywd_opt(2004, 52, Weekday::Mon), Some(ymd(2004, 12, 20))); + assert_eq!(isoywd_opt(2004, 52, Weekday::Sun), Some(ymd(2004, 12, 26))); + assert_eq!(isoywd_opt(2004, 53, Weekday::Mon), Some(ymd(2004, 12, 27))); + assert_eq!(isoywd_opt(2004, 53, Weekday::Sun), Some(ymd(2005, 1, 2))); + assert_eq!(isoywd_opt(2004, 54, Weekday::Mon), None); + + assert_eq!(isoywd_opt(2011, 0, Weekday::Sun), None); + assert_eq!(isoywd_opt(2011, 1, Weekday::Mon), Some(ymd(2011, 1, 3))); + assert_eq!(isoywd_opt(2011, 1, Weekday::Sun), Some(ymd(2011, 1, 9))); + assert_eq!(isoywd_opt(2011, 2, Weekday::Mon), Some(ymd(2011, 1, 10))); + assert_eq!(isoywd_opt(2011, 2, Weekday::Sun), Some(ymd(2011, 1, 16))); + + assert_eq!(isoywd_opt(2018, 51, Weekday::Mon), Some(ymd(2018, 12, 17))); + assert_eq!(isoywd_opt(2018, 51, Weekday::Sun), Some(ymd(2018, 12, 23))); + assert_eq!(isoywd_opt(2018, 52, Weekday::Mon), Some(ymd(2018, 12, 24))); + assert_eq!(isoywd_opt(2018, 52, Weekday::Sun), Some(ymd(2018, 12, 30))); + assert_eq!(isoywd_opt(2018, 53, Weekday::Mon), None); + } + + #[test] + 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, + Weekday::Fri, + Weekday::Sat, + Weekday::Sun, + ] + .iter() + { + let d = NaiveDate::from_isoywd_opt(year, week, weekday); + if d.is_some() { + let d = d.unwrap(); + assert_eq!(d.weekday(), weekday); + let w = d.iso_week(); + assert_eq!(w.year(), year); + assert_eq!(w.week(), week); + } + } + } + } + + for year in 2000..2401 { + for month in 1..13 { + for day in 1..32 { + let d = NaiveDate::from_ymd_opt(year, month, day); + if d.is_some() { + let d = d.unwrap(); + let w = d.iso_week(); + let d_ = NaiveDate::from_isoywd(w.year(), w.week(), d.weekday()); + assert_eq!(d, d_); + } + } + } + } + } + + #[test] + fn test_date_from_num_days_from_ce() { + let from_ndays_from_ce = |days| NaiveDate::from_num_days_from_ce_opt(days); + assert_eq!(from_ndays_from_ce(1), Some(NaiveDate::from_ymd(1, 1, 1))); + assert_eq!(from_ndays_from_ce(2), Some(NaiveDate::from_ymd(1, 1, 2))); + assert_eq!(from_ndays_from_ce(31), Some(NaiveDate::from_ymd(1, 1, 31))); + assert_eq!(from_ndays_from_ce(32), Some(NaiveDate::from_ymd(1, 2, 1))); + assert_eq!(from_ndays_from_ce(59), Some(NaiveDate::from_ymd(1, 2, 28))); + assert_eq!(from_ndays_from_ce(60), Some(NaiveDate::from_ymd(1, 3, 1))); + assert_eq!(from_ndays_from_ce(365), Some(NaiveDate::from_ymd(1, 12, 31))); + assert_eq!(from_ndays_from_ce(365 * 1 + 1), Some(NaiveDate::from_ymd(2, 1, 1))); + assert_eq!(from_ndays_from_ce(365 * 2 + 1), Some(NaiveDate::from_ymd(3, 1, 1))); + assert_eq!(from_ndays_from_ce(365 * 3 + 1), Some(NaiveDate::from_ymd(4, 1, 1))); + assert_eq!(from_ndays_from_ce(365 * 4 + 2), Some(NaiveDate::from_ymd(5, 1, 1))); + assert_eq!(from_ndays_from_ce(146097 + 1), Some(NaiveDate::from_ymd(401, 1, 1))); + assert_eq!(from_ndays_from_ce(146097 * 5 + 1), Some(NaiveDate::from_ymd(2001, 1, 1))); + assert_eq!(from_ndays_from_ce(719163), Some(NaiveDate::from_ymd(1970, 1, 1))); + assert_eq!(from_ndays_from_ce(0), Some(NaiveDate::from_ymd(0, 12, 31))); // 1 BCE + assert_eq!(from_ndays_from_ce(-365), Some(NaiveDate::from_ymd(0, 1, 1))); + assert_eq!(from_ndays_from_ce(-366), Some(NaiveDate::from_ymd(-1, 12, 31))); // 2 BCE + + for days in (-9999..10001).map(|x| x * 100) { + assert_eq!(from_ndays_from_ce(days).map(|d| d.num_days_from_ce()), Some(days)); + } + + 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] + fn test_date_from_weekday_of_month_opt() { + let ymwd = |y, m, w, n| NaiveDate::from_weekday_of_month_opt(y, m, w, n); + assert_eq!(ymwd(2018, 8, Weekday::Tue, 0), None); + assert_eq!(ymwd(2018, 8, Weekday::Wed, 1), Some(NaiveDate::from_ymd(2018, 8, 1))); + assert_eq!(ymwd(2018, 8, Weekday::Thu, 1), Some(NaiveDate::from_ymd(2018, 8, 2))); + assert_eq!(ymwd(2018, 8, Weekday::Sun, 1), Some(NaiveDate::from_ymd(2018, 8, 5))); + assert_eq!(ymwd(2018, 8, Weekday::Mon, 1), Some(NaiveDate::from_ymd(2018, 8, 6))); + assert_eq!(ymwd(2018, 8, Weekday::Tue, 1), Some(NaiveDate::from_ymd(2018, 8, 7))); + assert_eq!(ymwd(2018, 8, Weekday::Wed, 2), Some(NaiveDate::from_ymd(2018, 8, 8))); + assert_eq!(ymwd(2018, 8, Weekday::Sun, 2), Some(NaiveDate::from_ymd(2018, 8, 12))); + assert_eq!(ymwd(2018, 8, Weekday::Thu, 3), Some(NaiveDate::from_ymd(2018, 8, 16))); + assert_eq!(ymwd(2018, 8, Weekday::Thu, 4), Some(NaiveDate::from_ymd(2018, 8, 23))); + assert_eq!(ymwd(2018, 8, Weekday::Thu, 5), Some(NaiveDate::from_ymd(2018, 8, 30))); + assert_eq!(ymwd(2018, 8, Weekday::Fri, 5), Some(NaiveDate::from_ymd(2018, 8, 31))); + assert_eq!(ymwd(2018, 8, Weekday::Sat, 5), None); + } + + #[test] + fn test_date_fields() { + fn check(year: i32, month: u32, day: u32, ordinal: u32) { + let d1 = NaiveDate::from_ymd(year, month, day); + assert_eq!(d1.year(), year); + assert_eq!(d1.month(), month); + assert_eq!(d1.day(), day); + assert_eq!(d1.ordinal(), ordinal); + + let d2 = NaiveDate::from_yo(year, ordinal); + assert_eq!(d2.year(), year); + assert_eq!(d2.month(), month); + assert_eq!(d2.day(), day); + assert_eq!(d2.ordinal(), ordinal); + + assert_eq!(d1, d2); + } + + check(2012, 1, 1, 1); + check(2012, 1, 2, 2); + check(2012, 2, 1, 32); + check(2012, 2, 29, 60); + check(2012, 3, 1, 61); + check(2012, 4, 9, 100); + check(2012, 7, 18, 200); + check(2012, 10, 26, 300); + check(2012, 12, 31, 366); + + check(2014, 1, 1, 1); + check(2014, 1, 2, 2); + check(2014, 2, 1, 32); + check(2014, 2, 28, 59); + check(2014, 3, 1, 60); + check(2014, 4, 10, 100); + check(2014, 7, 19, 200); + check(2014, 10, 27, 300); + check(2014, 12, 31, 365); + } + + #[test] + fn test_date_weekday() { + assert_eq!(NaiveDate::from_ymd(1582, 10, 15).weekday(), Weekday::Fri); + // May 20, 1875 = ISO 8601 reference date + assert_eq!(NaiveDate::from_ymd(1875, 5, 20).weekday(), Weekday::Thu); + assert_eq!(NaiveDate::from_ymd(2000, 1, 1).weekday(), Weekday::Sat); + } + + #[test] + fn test_date_with_fields() { + let d = NaiveDate::from_ymd(2000, 2, 29); + assert_eq!(d.with_year(-400), Some(NaiveDate::from_ymd(-400, 2, 29))); + assert_eq!(d.with_year(-100), None); + assert_eq!(d.with_year(1600), Some(NaiveDate::from_ymd(1600, 2, 29))); + assert_eq!(d.with_year(1900), None); + assert_eq!(d.with_year(2000), Some(NaiveDate::from_ymd(2000, 2, 29))); + assert_eq!(d.with_year(2001), None); + assert_eq!(d.with_year(2004), Some(NaiveDate::from_ymd(2004, 2, 29))); + assert_eq!(d.with_year(i32::MAX), None); + + let d = NaiveDate::from_ymd(2000, 4, 30); + assert_eq!(d.with_month(0), None); + assert_eq!(d.with_month(1), Some(NaiveDate::from_ymd(2000, 1, 30))); + assert_eq!(d.with_month(2), None); + assert_eq!(d.with_month(3), Some(NaiveDate::from_ymd(2000, 3, 30))); + assert_eq!(d.with_month(4), Some(NaiveDate::from_ymd(2000, 4, 30))); + assert_eq!(d.with_month(12), Some(NaiveDate::from_ymd(2000, 12, 30))); + assert_eq!(d.with_month(13), None); + assert_eq!(d.with_month(u32::MAX), None); + + let d = NaiveDate::from_ymd(2000, 2, 8); + assert_eq!(d.with_day(0), None); + assert_eq!(d.with_day(1), Some(NaiveDate::from_ymd(2000, 2, 1))); + assert_eq!(d.with_day(29), Some(NaiveDate::from_ymd(2000, 2, 29))); + assert_eq!(d.with_day(30), None); + assert_eq!(d.with_day(u32::MAX), None); + + let d = NaiveDate::from_ymd(2000, 5, 5); + assert_eq!(d.with_ordinal(0), None); + assert_eq!(d.with_ordinal(1), Some(NaiveDate::from_ymd(2000, 1, 1))); + assert_eq!(d.with_ordinal(60), Some(NaiveDate::from_ymd(2000, 2, 29))); + assert_eq!(d.with_ordinal(61), Some(NaiveDate::from_ymd(2000, 3, 1))); + assert_eq!(d.with_ordinal(366), Some(NaiveDate::from_ymd(2000, 12, 31))); + assert_eq!(d.with_ordinal(367), None); + assert_eq!(d.with_ordinal(u32::MAX), None); + } + + #[test] + fn test_date_num_days_from_ce() { + assert_eq!(NaiveDate::from_ymd(1, 1, 1).num_days_from_ce(), 1); + + for year in -9999..10001 { + assert_eq!( + NaiveDate::from_ymd(year, 1, 1).num_days_from_ce(), + NaiveDate::from_ymd(year - 1, 12, 31).num_days_from_ce() + 1 + ); + } + } + + #[test] + fn test_date_succ() { + let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + assert_eq!(ymd(2014, 5, 6).succ_opt(), Some(ymd(2014, 5, 7))); + 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_DATE.year(), 12, 31).succ_opt(), None); + } + + #[test] + fn test_date_pred() { + let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + assert_eq!(ymd(2016, 3, 1).pred_opt(), Some(ymd(2016, 2, 29))); + 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_DATE.year(), 1, 1).pred_opt(), None); + } + + #[test] + fn test_date_add() { + 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_signed(rhs), sum); + assert_eq!(lhs.checked_sub_signed(-rhs), sum); + } + + check((2014, 1, 1), Duration::zero(), Some((2014, 1, 1))); + check((2014, 1, 1), Duration::seconds(86399), Some((2014, 1, 1))); + // always round towards zero + check((2014, 1, 1), Duration::seconds(-86399), Some((2014, 1, 1))); + check((2014, 1, 1), Duration::days(1), Some((2014, 1, 2))); + check((2014, 1, 1), Duration::days(-1), Some((2013, 12, 31))); + check((2014, 1, 1), Duration::days(364), Some((2014, 12, 31))); + check((2014, 1, 1), Duration::days(365 * 4 + 1), Some((2018, 1, 1))); + check((2014, 1, 1), Duration::days(365 * 400 + 97), Some((2414, 1, 1))); + + check((-7, 1, 1), Duration::days(365 * 12 + 3), Some((5, 1, 1))); + + // overflow check + check((0, 1, 1), Duration::days(MAX_DAYS_FROM_YEAR_0 as i64), Some((MAX_YEAR, 12, 31))); + check((0, 1, 1), Duration::days(MAX_DAYS_FROM_YEAR_0 as i64 + 1), None); + check((0, 1, 1), Duration::max_value(), None); + check((0, 1, 1), Duration::days(MIN_DAYS_FROM_YEAR_0 as i64), Some((MIN_YEAR, 1, 1))); + check((0, 1, 1), Duration::days(MIN_DAYS_FROM_YEAR_0 as i64 - 1), None); + check((0, 1, 1), Duration::min_value(), None); + } + + #[test] + fn test_date_sub() { + 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.signed_duration_since(rhs), diff); + assert_eq!(rhs.signed_duration_since(lhs), -diff); + } + + check((2014, 1, 1), (2014, 1, 1), Duration::zero()); + check((2014, 1, 2), (2014, 1, 1), Duration::days(1)); + check((2014, 12, 31), (2014, 1, 1), Duration::days(364)); + check((2015, 1, 3), (2014, 1, 1), Duration::days(365 + 2)); + check((2018, 1, 1), (2014, 1, 1), Duration::days(365 * 4 + 1)); + check((2414, 1, 1), (2014, 1, 1), Duration::days(365 * 400 + 97)); + + check((MAX_YEAR, 12, 31), (0, 1, 1), Duration::days(MAX_DAYS_FROM_YEAR_0 as i64)); + check((MIN_YEAR, 1, 1), (0, 1, 1), Duration::days(MIN_DAYS_FROM_YEAR_0 as i64)); + } + + #[test] + fn test_date_addassignment() { + let ymd = NaiveDate::from_ymd; + let mut date = ymd(2016, 10, 1); + date += Duration::days(10); + assert_eq!(date, ymd(2016, 10, 11)); + date += Duration::days(30); + assert_eq!(date, ymd(2016, 11, 10)); + } + + #[test] + fn test_date_subassignment() { + let ymd = NaiveDate::from_ymd; + let mut date = ymd(2016, 10, 11); + date -= Duration::days(10); + assert_eq!(date, ymd(2016, 10, 1)); + date -= Duration::days(2); + assert_eq!(date, ymd(2016, 9, 29)); + } + + #[test] + fn test_date_fmt() { + assert_eq!(format!("{:?}", NaiveDate::from_ymd(2012, 3, 4)), "2012-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd(0, 3, 4)), "0000-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd(-307, 3, 4)), "-0307-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd(12345, 3, 4)), "+12345-03-04"); + + assert_eq!(NaiveDate::from_ymd(2012, 3, 4).to_string(), "2012-03-04"); + assert_eq!(NaiveDate::from_ymd(0, 3, 4).to_string(), "0000-03-04"); + assert_eq!(NaiveDate::from_ymd(-307, 3, 4).to_string(), "-0307-03-04"); + assert_eq!(NaiveDate::from_ymd(12345, 3, 4).to_string(), "+12345-03-04"); + + // the format specifier should have no effect on `NaiveTime` + assert_eq!(format!("{:+30?}", NaiveDate::from_ymd(1234, 5, 6)), "1234-05-06"); + assert_eq!(format!("{:30?}", NaiveDate::from_ymd(12345, 6, 7)), "+12345-06-07"); + } + + #[test] + fn test_date_from_str() { + // valid cases + let valid = [ + "-0000000123456-1-2", + " -123456 - 1 - 2 ", + "-12345-1-2", + "-1234-12-31", + "-7-6-5", + "350-2-28", + "360-02-29", + "0360-02-29", + "2015-2 -18", + "+70-2-18", + "+70000-2-18", + "+00007-2-18", + ]; + for &s in &valid { + let d = match s.parse::() { + Ok(d) => d, + Err(e) => panic!("parsing `{}` has failed: {}", s, e), + }; + let s_ = format!("{:?}", d); + // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same + let d_ = match s_.parse::() { + Ok(d) => d, + Err(e) => { + panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + } + }; + assert!( + d == d_, + "`{}` is parsed into `{:?}`, but reparsed result \ + `{:?}` does not match", + s, + d, + d_ + ); + } + + // some invalid cases + // since `ParseErrorKind` is private, all we can do is to check if there was an error + assert!("".parse::().is_err()); + assert!("x".parse::().is_err()); + assert!("2014".parse::().is_err()); + assert!("2014-01".parse::().is_err()); + assert!("2014-01-00".parse::().is_err()); + assert!("2014-13-57".parse::().is_err()); + assert!("9999999-9-9".parse::().is_err()); // out-of-bounds + } + + #[test] + fn test_date_parse_from_str() { + let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + assert_eq!( + NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(ymd(2014, 5, 7)) + ); // ignore time and offset + assert_eq!( + NaiveDate::parse_from_str("2015-W06-1=2015-033", "%G-W%V-%u = %Y-%j"), + Ok(ymd(2015, 2, 2)) + ); + assert_eq!( + NaiveDate::parse_from_str("Fri, 09 Aug 13", "%a, %d %b %y"), + Ok(ymd(2013, 8, 9)) + ); + assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err()); + assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err()); + assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient + } + + #[test] + fn test_date_format() { + let d = NaiveDate::from_ymd(2012, 3, 4); + assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12"); + assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March"); + assert_eq!(d.format("%d,%e").to_string(), "04, 4"); + assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09"); + assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7"); + assert_eq!(d.format("%j").to_string(), "064"); // since 2012 is a leap year + assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12"); + assert_eq!(d.format("%F").to_string(), "2012-03-04"); + assert_eq!(d.format("%v").to_string(), " 4-Mar-2012"); + assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); + + // non-four-digit years + assert_eq!(NaiveDate::from_ymd(12345, 1, 1).format("%Y").to_string(), "+12345"); + assert_eq!(NaiveDate::from_ymd(1234, 1, 1).format("%Y").to_string(), "1234"); + assert_eq!(NaiveDate::from_ymd(123, 1, 1).format("%Y").to_string(), "0123"); + assert_eq!(NaiveDate::from_ymd(12, 1, 1).format("%Y").to_string(), "0012"); + assert_eq!(NaiveDate::from_ymd(1, 1, 1).format("%Y").to_string(), "0001"); + assert_eq!(NaiveDate::from_ymd(0, 1, 1).format("%Y").to_string(), "0000"); + assert_eq!(NaiveDate::from_ymd(-1, 1, 1).format("%Y").to_string(), "-0001"); + assert_eq!(NaiveDate::from_ymd(-12, 1, 1).format("%Y").to_string(), "-0012"); + assert_eq!(NaiveDate::from_ymd(-123, 1, 1).format("%Y").to_string(), "-0123"); + assert_eq!(NaiveDate::from_ymd(-1234, 1, 1).format("%Y").to_string(), "-1234"); + assert_eq!(NaiveDate::from_ymd(-12345, 1, 1).format("%Y").to_string(), "-12345"); + + // corner cases + assert_eq!( + NaiveDate::from_ymd(2007, 12, 31).format("%G,%g,%U,%W,%V").to_string(), + "2008,08,53,53,01" + ); + assert_eq!( + NaiveDate::from_ymd(2010, 1, 3).format("%G,%g,%U,%W,%V").to_string(), + "2009,09,01,00,53" + ); + } + + #[test] + fn test_day_iterator_limit() { + assert_eq!( + NaiveDate::from_ymd(262143, 12, 29).iter_days().take(4).collect::>().len(), + 2 + ); + } + + #[test] + fn test_week_iterator_limit() { + assert_eq!( + NaiveDate::from_ymd(262143, 12, 12).iter_weeks().take(4).collect::>().len(), + 2 + ); + } +} diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs new file mode 100644 index 0000000..92d6c28 --- /dev/null +++ b/src/naive/datetime.rs @@ -0,0 +1,2507 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 date and time without timezone. + +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::{fmt, hash, str}; +use num_traits::ToPrimitive; +use oldtime::Duration as OldDuration; + +use div::div_mod_floor; +#[cfg(any(feature = "alloc", feature = "std", test))] +use format::DelayedFormat; +use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use format::{Fixed, Item, Numeric, Pad}; +use naive::date::{MAX_DATE, MIN_DATE}; +use naive::time::{MAX_TIME, MIN_TIME}; +use naive::{IsoWeek, NaiveDate, NaiveTime}; +use {Datelike, Timelike, Weekday}; + +/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS` +/// will always overflow the addition with any date and time type. +/// +/// So why is this needed? `Duration::seconds(rhs)` may overflow, and we don't have +/// an alternative returning `Option` or `Result`. Thus we need some early bound to avoid +/// touching that call when we are already sure that it WILL overflow... +const MAX_SECS_BITS: usize = 44; + +/// The minimum possible `NaiveDateTime`. +pub const MIN_DATETIME: NaiveDateTime = NaiveDateTime { date: MIN_DATE, time: MIN_TIME }; +/// The maximum possible `NaiveDateTime`. +pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime { date: MAX_DATE, time: MAX_TIME }; + +/// ISO 8601 combined date and time without timezone. +/// +/// # Example +/// +/// `NaiveDateTime` is commonly created from [`NaiveDate`](./struct.NaiveDate.html). +/// +/// ~~~~ +/// use chrono::{NaiveDate, NaiveDateTime}; +/// +/// let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); +/// # let _ = dt; +/// ~~~~ +/// +/// You can use typical [date-like](../trait.Datelike.html) and +/// [time-like](../trait.Timelike.html) methods, +/// provided that relevant traits are in the scope. +/// +/// ~~~~ +/// # use chrono::{NaiveDate, NaiveDateTime}; +/// # let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); +/// use chrono::{Datelike, Timelike, Weekday}; +/// +/// assert_eq!(dt.weekday(), Weekday::Fri); +/// assert_eq!(dt.num_seconds_from_midnight(), 33011); +/// ~~~~ +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +pub struct NaiveDateTime { + date: NaiveDate, + time: NaiveTime, +} + +impl NaiveDateTime { + /// Makes a new `NaiveDateTime` from date and time components. + /// Equivalent to [`date.and_time(time)`](./struct.NaiveDate.html#method.and_time) + /// and many other helper constructors on `NaiveDate`. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveTime, NaiveDateTime}; + /// + /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let t = NaiveTime::from_hms_milli(12, 34, 56, 789); + /// + /// let dt = NaiveDateTime::new(d, t); + /// assert_eq!(dt.date(), d); + /// assert_eq!(dt.time(), t); + /// ~~~~ + #[inline] + pub fn new(date: NaiveDate, time: NaiveTime) -> NaiveDateTime { + NaiveDateTime { date: date, time: time } + } + + /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, + /// from the number of non-leap seconds + /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") + /// 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). + /// + /// 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 + /// timestamp" cannot represent a leap second unambiguously.) + /// + /// Panics on the out-of-range number of seconds and/or invalid nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDateTime, NaiveDate}; + /// + /// let dt = NaiveDateTime::from_timestamp(0, 42_000_000); + /// assert_eq!(dt, NaiveDate::from_ymd(1970, 1, 1).and_hms_milli(0, 0, 0, 42)); + /// + /// let dt = NaiveDateTime::from_timestamp(1_000_000_000, 0); + /// assert_eq!(dt, NaiveDate::from_ymd(2001, 9, 9).and_hms(1, 46, 40)); + /// ~~~~ + #[inline] + pub fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime { + let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs); + datetime.expect("invalid or out-of-range datetime") + } + + /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, + /// from the number of non-leap seconds + /// 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](./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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDateTime, NaiveDate}; + /// use std::i64; + /// + /// let from_timestamp_opt = NaiveDateTime::from_timestamp_opt; + /// + /// assert!(from_timestamp_opt(0, 0).is_some()); + /// assert!(from_timestamp_opt(0, 999_999_999).is_some()); + /// assert!(from_timestamp_opt(0, 1_500_000_000).is_some()); // leap second + /// assert!(from_timestamp_opt(0, 2_000_000_000).is_none()); + /// assert!(from_timestamp_opt(i64::MAX, 0).is_none()); + /// ~~~~ + #[inline] + pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option { + let (days, secs) = div_mod_floor(secs, 86_400); + let date = days + .to_i32() + .and_then(|days| days.checked_add(719_163)) + .and_then(NaiveDate::from_num_days_from_ce_opt); + let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs); + match (date, time) { + (Some(date), Some(time)) => Some(NaiveDateTime { date: date, time: time }), + (_, _) => None, + } + } + + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDateTime, NaiveDate}; + /// + /// let parse_from_str = NaiveDateTime::parse_from_str; + /// + /// assert_eq!(parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S"), + /// Ok(NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4))); + /// assert_eq!(parse_from_str("5sep2015pm012345.6789", "%d%b%Y%p%I%M%S%.f"), + /// Ok(NaiveDate::from_ymd(2015, 9, 5).and_hms_micro(13, 23, 45, 678_900))); + /// ~~~~ + /// + /// Offset is ignored for the purpose of parsing. + /// + /// ~~~~ + /// # use chrono::{NaiveDateTime, NaiveDate}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + /// Ok(NaiveDate::from_ymd(2014, 5, 17).and_hms(12, 34, 56))); + /// ~~~~ + /// + /// [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.) + /// + /// ~~~~ + /// # use chrono::{NaiveDateTime, NaiveDate}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// assert_eq!(parse_from_str("2015-07-01 08:59:60.123", "%Y-%m-%d %H:%M:%S%.f"), + /// Ok(NaiveDate::from_ymd(2015, 7, 1).and_hms_milli(8, 59, 59, 1_123))); + /// ~~~~ + /// + /// Missing seconds are assumed to be zero, + /// but out-of-bound times or insufficient fields are errors otherwise. + /// + /// ~~~~ + /// # use chrono::{NaiveDateTime, NaiveDate}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// assert_eq!(parse_from_str("94/9/4 7:15", "%y/%m/%d %H:%M"), + /// Ok(NaiveDate::from_ymd(1994, 9, 4).and_hms(7, 15, 0))); + /// + /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); + /// assert!(parse_from_str("94/9/4 12", "%y/%m/%d %H").is_err()); + /// assert!(parse_from_str("94/9/4 17:60", "%y/%m/%d %H:%M").is_err()); + /// assert!(parse_from_str("94/9/4 24:00:00", "%y/%m/%d %H:%M:%S").is_err()); + /// ~~~~ + /// + /// All parsed fields should be consistent to each other, otherwise it's an error. + /// + /// ~~~~ + /// # use chrono::NaiveDateTime; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// let fmt = "%Y-%m-%d %H:%M:%S = UNIX timestamp %s"; + /// assert!(parse_from_str("2001-09-09 01:46:39 = UNIX timestamp 999999999", fmt).is_ok()); + /// assert!(parse_from_str("1970-01-01 00:00:00 = UNIX timestamp 1", fmt).is_err()); + /// ~~~~ + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_datetime_with_offset(0) // no offset adjustment + } + + /// Retrieves a date component. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); + /// assert_eq!(dt.date(), NaiveDate::from_ymd(2016, 7, 8)); + /// ~~~~ + #[inline] + pub fn date(&self) -> NaiveDate { + self.date + } + + /// Retrieves a time component. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveTime}; + /// + /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); + /// assert_eq!(dt.time(), NaiveTime::from_hms(9, 10, 11)); + /// ~~~~ + #[inline] + pub fn time(&self) -> NaiveTime { + self.time + } + + /// Returns the number of non-leap seconds since the midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 980); + /// assert_eq!(dt.timestamp(), 1); + /// + /// let dt = NaiveDate::from_ymd(2001, 9, 9).and_hms(1, 46, 40); + /// assert_eq!(dt.timestamp(), 1_000_000_000); + /// + /// let dt = NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59); + /// assert_eq!(dt.timestamp(), -1); + /// + /// let dt = NaiveDate::from_ymd(-1, 1, 1).and_hms(0, 0, 0); + /// assert_eq!(dt.timestamp(), -62198755200); + /// ~~~~ + #[inline] + pub fn timestamp(&self) -> i64 { + const UNIX_EPOCH_DAY: i64 = 719_163; + let gregorian_day = i64::from(self.date.num_days_from_ce()); + let seconds_from_midnight = i64::from(self.time.num_seconds_from_midnight()); + (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight + } + + /// Returns the number of non-leap *milliseconds* since midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// Note also that this does reduce the number of years that can be + /// represented from ~584 Billion to ~584 Million. (If this is a problem, + /// please file an issue to let me know what domain needs millisecond + /// precision over billions of years, I'm curious.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 444); + /// assert_eq!(dt.timestamp_millis(), 1_444); + /// + /// let dt = NaiveDate::from_ymd(2001, 9, 9).and_hms_milli(1, 46, 40, 555); + /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); + /// + /// let dt = NaiveDate::from_ymd(1969, 12, 31).and_hms_milli(23, 59, 59, 100); + /// assert_eq!(dt.timestamp_millis(), -900); + /// ~~~~ + #[inline] + pub fn timestamp_millis(&self) -> i64 { + let as_ms = self.timestamp() * 1000; + as_ms + i64::from(self.timestamp_subsec_millis()) + } + + /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// # Panics + /// + /// Note also that this does reduce the number of years that can be + /// represented from ~584 Billion to ~584 years. The dates that can be + /// represented as nanoseconds are between 1677-09-21T00:12:44.0 and + /// 2262-04-11T23:47:16.854775804. + /// + /// (If this is a problem, please file an issue to let me know what domain + /// needs nanosecond precision over millennia, I'm curious.) + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime}; + /// + /// let dt = NaiveDate::from_ymd(1970, 1, 1).and_hms_nano(0, 0, 1, 444); + /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); + /// + /// let dt = NaiveDate::from_ymd(2001, 9, 9).and_hms_nano(1, 46, 40, 555); + /// + /// const A_BILLION: i64 = 1_000_000_000; + /// let nanos = dt.timestamp_nanos(); + /// assert_eq!(nanos, 1_000_000_000_000_000_555); + /// assert_eq!( + /// dt, + /// NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) + /// ); + /// ~~~~ + #[inline] + pub fn timestamp_nanos(&self) -> i64 { + let as_ns = self.timestamp() * 1_000_000_000; + as_ns + i64::from(self.timestamp_subsec_nanos()) + } + + /// Returns the number of milliseconds since the last whole non-leap second. + /// + /// The return value ranges from 0 to 999, + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_nano(9, 10, 11, 123_456_789); + /// assert_eq!(dt.timestamp_subsec_millis(), 123); + /// + /// let dt = NaiveDate::from_ymd(2015, 7, 1).and_hms_nano(8, 59, 59, 1_234_567_890); + /// assert_eq!(dt.timestamp_subsec_millis(), 1_234); + /// ~~~~ + #[inline] + pub fn timestamp_subsec_millis(&self) -> u32 { + self.timestamp_subsec_nanos() / 1_000_000 + } + + /// 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](./struct.NaiveTime.html#leap-second-handling), to 1,999,999. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_nano(9, 10, 11, 123_456_789); + /// assert_eq!(dt.timestamp_subsec_micros(), 123_456); + /// + /// let dt = NaiveDate::from_ymd(2015, 7, 1).and_hms_nano(8, 59, 59, 1_234_567_890); + /// assert_eq!(dt.timestamp_subsec_micros(), 1_234_567); + /// ~~~~ + #[inline] + pub fn timestamp_subsec_micros(&self) -> u32 { + self.timestamp_subsec_nanos() / 1_000 + } + + /// 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](./struct.NaiveTime.html#leap-second-handling), to 1,999,999,999. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_nano(9, 10, 11, 123_456_789); + /// assert_eq!(dt.timestamp_subsec_nanos(), 123_456_789); + /// + /// let dt = NaiveDate::from_ymd(2015, 7, 1).and_hms_nano(8, 59, 59, 1_234_567_890); + /// assert_eq!(dt.timestamp_subsec_nanos(), 1_234_567_890); + /// ~~~~ + #[inline] + pub fn timestamp_subsec_nanos(&self) -> u32 { + self.time.nanosecond() + } + + /// Adds given `Duration` to the current date and time. + /// + /// 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**. + /// + /// Returns `None` when it will result in overflow. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveDate}; + /// + /// 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_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(86_400)), + /// 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_signed(Duration::milliseconds(450)), + /// Some(hmsm(3, 5, 8, 430))); + /// # } + /// ~~~~ + /// + /// Overflow returns `None`. + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// # use chrono::{Duration, NaiveDate}; + /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); + /// 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. + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// # use chrono::{Duration, NaiveDate}; + /// # 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_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_signed(self, rhs: OldDuration) -> Option { + let (time, rhs) = self.time.overflowing_add_signed(rhs); + + // 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_signed(OldDuration::seconds(rhs))); + Some(NaiveDateTime { date: date, time: time }) + } + + /// Subtracts given `Duration` from the current date and time. + /// + /// 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**. + /// + /// Returns `None` when it will result in overflow. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveDate}; + /// + /// 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_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(86_400)), + /// 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_signed(Duration::milliseconds(670)), + /// Some(hmsm(3, 5, 6, 780))); + /// # } + /// ~~~~ + /// + /// Overflow returns `None`. + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// # use chrono::{Duration, NaiveDate}; + /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); + /// 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. + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// # use chrono::{Duration, NaiveDate}; + /// # 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_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_signed(self, rhs: OldDuration) -> Option { + let (time, rhs) = self.time.overflowing_sub_signed(rhs); + + // 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_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](./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 + /// **there are exactly one (or two) leap second(s) ever**. + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveDate}; + /// + /// 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 * 86_400 + 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; fn main() { + /// # use chrono::{Duration, NaiveDate}; + /// # 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) + } + + /// Formats the combined date and time with the specified formatting items. + /// Otherwise it is the same as the ordinary [`format`](#method.format) method. + /// + /// The `Iterator` of items should be `Clone`able, + /// since the resulting `DelayedFormat` value may be formatted multiple times. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// use chrono::format::strftime::StrftimeItems; + /// + /// let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S"); + /// let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); + /// assert_eq!(dt.format_with_items(fmt.clone()).to_string(), "2015-09-05 23:56:04"); + /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); + /// ~~~~ + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # use chrono::format::strftime::StrftimeItems; + /// # let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S").clone(); + /// # let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); + /// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04"); + /// ~~~~ + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + DelayedFormat::new(Some(self.date), Some(self.time), items) + } + + /// Formats the combined date and time with the specified format string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// This returns a `DelayedFormat`, + /// which gets converted to a string only when actual formatting happens. + /// You may use the `to_string` method to get a `String`, + /// or just feed it into `print!` and other formatting macros. + /// (In this way it avoids the redundant memory allocation.) + /// + /// A wrong format string does *not* issue an error immediately. + /// Rather, converting or formatting the `DelayedFormat` fails. + /// You are recommended to immediately use `DelayedFormat` for this reason. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); + /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); + /// assert_eq!(dt.format("around %l %p on %b %-d").to_string(), "around 11 PM on Sep 5"); + /// ~~~~ + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ~~~~ + /// # use chrono::NaiveDate; + /// # let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); + /// assert_eq!(format!("{}", dt.format("%Y-%m-%d %H:%M:%S")), "2015-09-05 23:56:04"); + /// assert_eq!(format!("{}", dt.format("around %l %p on %b %-d")), "around 11 PM on Sep 5"); + /// ~~~~ + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { + self.format_with_items(StrftimeItems::new(fmt)) + } +} + +impl Datelike for NaiveDateTime { + /// Returns the year number in the [calendar date](./index.html#calendar-date). + /// + /// See also the [`NaiveDate::year`](./struct.NaiveDate.html#method.year) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.year(), 2015); + /// ~~~~ + #[inline] + fn year(&self) -> i32 { + self.date.year() + } + + /// Returns the month number starting from 1. + /// + /// The return value ranges from 1 to 12. + /// + /// See also the [`NaiveDate::month`](./struct.NaiveDate.html#method.month) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.month(), 9); + /// ~~~~ + #[inline] + fn month(&self) -> u32 { + self.date.month() + } + + /// Returns the month number starting from 0. + /// + /// The return value ranges from 0 to 11. + /// + /// See also the [`NaiveDate::month0`](./struct.NaiveDate.html#method.month0) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.month0(), 8); + /// ~~~~ + #[inline] + fn month0(&self) -> u32 { + self.date.month0() + } + + /// Returns the day of month starting from 1. + /// + /// The return value ranges from 1 to 31. (The last day of month differs by months.) + /// + /// See also the [`NaiveDate::day`](./struct.NaiveDate.html#method.day) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.day(), 25); + /// ~~~~ + #[inline] + fn day(&self) -> u32 { + self.date.day() + } + + /// Returns the day of month starting from 0. + /// + /// The return value ranges from 0 to 30. (The last day of month differs by months.) + /// + /// See also the [`NaiveDate::day0`](./struct.NaiveDate.html#method.day0) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.day0(), 24); + /// ~~~~ + #[inline] + fn day0(&self) -> u32 { + self.date.day0() + } + + /// Returns the day of year starting from 1. + /// + /// The return value ranges from 1 to 366. (The last day of year differs by years.) + /// + /// See also the [`NaiveDate::ordinal`](./struct.NaiveDate.html#method.ordinal) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.ordinal(), 268); + /// ~~~~ + #[inline] + fn ordinal(&self) -> u32 { + self.date.ordinal() + } + + /// Returns the day of year starting from 0. + /// + /// The return value ranges from 0 to 365. (The last day of year differs by years.) + /// + /// See also the [`NaiveDate::ordinal0`](./struct.NaiveDate.html#method.ordinal0) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.ordinal0(), 267); + /// ~~~~ + #[inline] + fn ordinal0(&self) -> u32 { + self.date.ordinal0() + } + + /// Returns the day of week. + /// + /// See also the [`NaiveDate::weekday`](./struct.NaiveDate.html#method.weekday) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Weekday}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.weekday(), Weekday::Fri); + /// ~~~~ + #[inline] + fn weekday(&self) -> Weekday { + self.date.weekday() + } + + #[inline] + fn iso_week(&self) -> IsoWeek { + self.date.iso_week() + } + + /// Makes a new `NaiveDateTime` with the year number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_year`](./struct.NaiveDate.html#method.with_year) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); + /// assert_eq!(dt.with_year(2016), Some(NaiveDate::from_ymd(2016, 9, 25).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_year(-308), Some(NaiveDate::from_ymd(-308, 9, 25).and_hms(12, 34, 56))); + /// ~~~~ + #[inline] + fn with_year(&self, year: i32) -> Option { + self.date.with_year(year).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the month number (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_month`](./struct.NaiveDate.html#method.with_month) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56); + /// assert_eq!(dt.with_month(10), Some(NaiveDate::from_ymd(2015, 10, 30).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_month(13), None); // no month 13 + /// assert_eq!(dt.with_month(2), None); // no February 30 + /// ~~~~ + #[inline] + fn with_month(&self, month: u32) -> Option { + self.date.with_month(month).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the month number (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_month0`](./struct.NaiveDate.html#method.with_month0) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56); + /// assert_eq!(dt.with_month0(9), Some(NaiveDate::from_ymd(2015, 10, 30).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_month0(12), None); // no month 13 + /// assert_eq!(dt.with_month0(1), None); // no February 30 + /// ~~~~ + #[inline] + fn with_month0(&self, month0: u32) -> Option { + self.date.with_month0(month0).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of month (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_day`](./struct.NaiveDate.html#method.with_day) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); + /// assert_eq!(dt.with_day(30), Some(NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_day(31), None); // no September 31 + /// ~~~~ + #[inline] + fn with_day(&self, day: u32) -> Option { + self.date.with_day(day).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of month (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_day0`](./struct.NaiveDate.html#method.with_day0) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); + /// assert_eq!(dt.with_day0(29), Some(NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_day0(30), None); // no September 31 + /// ~~~~ + #[inline] + fn with_day0(&self, day0: u32) -> Option { + self.date.with_day0(day0).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of year (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_ordinal`](./struct.NaiveDate.html#method.with_ordinal) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); + /// assert_eq!(dt.with_ordinal(60), + /// Some(NaiveDate::from_ymd(2015, 3, 1).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_ordinal(366), None); // 2015 had only 365 days + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 9, 8).and_hms(12, 34, 56); + /// assert_eq!(dt.with_ordinal(60), + /// Some(NaiveDate::from_ymd(2016, 2, 29).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_ordinal(366), + /// Some(NaiveDate::from_ymd(2016, 12, 31).and_hms(12, 34, 56))); + /// ~~~~ + #[inline] + fn with_ordinal(&self, ordinal: u32) -> Option { + self.date.with_ordinal(ordinal).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of year (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveDate::with_ordinal0`](./struct.NaiveDate.html#method.with_ordinal0) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); + /// assert_eq!(dt.with_ordinal0(59), + /// Some(NaiveDate::from_ymd(2015, 3, 1).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_ordinal0(365), None); // 2015 had only 365 days + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 9, 8).and_hms(12, 34, 56); + /// assert_eq!(dt.with_ordinal0(59), + /// Some(NaiveDate::from_ymd(2016, 2, 29).and_hms(12, 34, 56))); + /// assert_eq!(dt.with_ordinal0(365), + /// Some(NaiveDate::from_ymd(2016, 12, 31).and_hms(12, 34, 56))); + /// ~~~~ + #[inline] + fn with_ordinal0(&self, ordinal0: u32) -> Option { + self.date.with_ordinal0(ordinal0).map(|d| NaiveDateTime { date: d, ..*self }) + } +} + +impl Timelike for NaiveDateTime { + /// Returns the hour number from 0 to 23. + /// + /// See also the [`NaiveTime::hour`](./struct.NaiveTime.html#method.hour) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.hour(), 12); + /// ~~~~ + #[inline] + fn hour(&self) -> u32 { + self.time.hour() + } + + /// Returns the minute number from 0 to 59. + /// + /// See also the [`NaiveTime::minute`](./struct.NaiveTime.html#method.minute) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.minute(), 34); + /// ~~~~ + #[inline] + fn minute(&self) -> u32 { + self.time.minute() + } + + /// Returns the second number from 0 to 59. + /// + /// See also the [`NaiveTime::second`](./struct.NaiveTime.html#method.second) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.second(), 56); + /// ~~~~ + #[inline] + fn second(&self) -> u32 { + self.time.second() + } + + /// 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](./struct.NaiveTime.html#leap-second-handling). + /// + /// See also the + /// [`NaiveTime::nanosecond`](./struct.NaiveTime.html#method.nanosecond) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.nanosecond(), 789_000_000); + /// ~~~~ + #[inline] + fn nanosecond(&self) -> u32 { + self.time.nanosecond() + } + + /// Makes a new `NaiveDateTime` with the hour number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveTime::with_hour`](./struct.NaiveTime.html#method.with_hour) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.with_hour(7), + /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(7, 34, 56, 789))); + /// assert_eq!(dt.with_hour(24), None); + /// ~~~~ + #[inline] + fn with_hour(&self, hour: u32) -> Option { + self.time.with_hour(hour).map(|t| NaiveDateTime { time: t, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the minute number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveTime::with_minute`](./struct.NaiveTime.html#method.with_minute) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.with_minute(45), + /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 45, 56, 789))); + /// assert_eq!(dt.with_minute(60), None); + /// ~~~~ + #[inline] + fn with_minute(&self, min: u32) -> Option { + self.time.with_minute(min).map(|t| NaiveDateTime { time: t, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the second number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// As with the [`second`](#method.second) method, + /// the input range is restricted to 0 through 59. + /// + /// See also the + /// [`NaiveTime::with_second`](./struct.NaiveTime.html#method.with_second) method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.with_second(17), + /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 17, 789))); + /// assert_eq!(dt.with_second(60), None); + /// ~~~~ + #[inline] + fn with_second(&self, sec: u32) -> Option { + self.time.with_second(sec).map(|t| NaiveDateTime { time: t, ..*self }) + } + + /// Makes a new `NaiveDateTime` with nanoseconds since the whole non-leap second changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// As with the [`nanosecond`](#method.nanosecond) method, + /// the input range can exceed 1,000,000,000 for leap seconds. + /// + /// See also the + /// [`NaiveTime::with_nanosecond`](./struct.NaiveTime.html#method.with_nanosecond) + /// method. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); + /// assert_eq!(dt.with_nanosecond(333_333_333), + /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_nano(12, 34, 56, 333_333_333))); + /// assert_eq!(dt.with_nanosecond(1_333_333_333), // leap second + /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_nano(12, 34, 56, 1_333_333_333))); + /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); + /// ~~~~ + #[inline] + fn with_nanosecond(&self, nano: u32) -> Option { + self.time.with_nanosecond(nano).map(|t| NaiveDateTime { time: t, ..*self }) + } +} + +/// `NaiveDateTime` can be used as a key to the hash maps (in principle). +/// +/// Practically this also takes account of fractional seconds, so it is not recommended. +/// (For the obvious reason this also distinguishes leap seconds from non-leap seconds.) +impl hash::Hash for NaiveDateTime { + fn hash(&self, state: &mut H) { + self.date.hash(state); + self.time.hash(state); + } +} + +/// An addition of `Duration` to `NaiveDateTime` yields another `NaiveDateTime`. +/// +/// 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**. +/// +/// Panics on underflow or overflow. +/// Use [`NaiveDateTime::checked_add_signed`](#method.checked_add_signed) to detect that. +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveDate}; +/// +/// 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) + Duration::zero(), hms(3, 5, 7)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(1), hms(3, 5, 8)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(-1), hms(3, 5, 6)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(3600 + 60), hms(4, 6, 7)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(86_400), +/// from_ymd(2016, 7, 9).and_hms(3, 5, 7)); +/// assert_eq!(hms(3, 5, 7) + Duration::days(365), +/// from_ymd(2017, 7, 8).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) + Duration::milliseconds(450), hmsm(3, 5, 8, 430)); +/// # } +/// ~~~~ +/// +/// Leap seconds are handled, +/// but the addition assumes that it is the only leap second happened. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveDate}; +/// # 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 + Duration::zero(), hmsm(3, 5, 59, 1_300)); +/// assert_eq!(leap + Duration::milliseconds(-500), hmsm(3, 5, 59, 800)); +/// assert_eq!(leap + Duration::milliseconds(500), hmsm(3, 5, 59, 1_800)); +/// assert_eq!(leap + Duration::milliseconds(800), hmsm(3, 6, 0, 100)); +/// assert_eq!(leap + Duration::seconds(10), hmsm(3, 6, 9, 300)); +/// 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 { + type Output = NaiveDateTime; + + #[inline] + fn add(self, rhs: OldDuration) -> NaiveDateTime { + self.checked_add_signed(rhs).expect("`NaiveDateTime + Duration` overflowed") + } +} + +impl AddAssign for NaiveDateTime { + #[inline] + fn add_assign(&mut self, rhs: OldDuration) { + *self = self.add(rhs); + } +} + +/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`. +/// It is the same as the addition with a negated `Duration`. +/// +/// 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**. +/// +/// Panics on underflow or overflow. +/// Use [`NaiveDateTime::checked_sub_signed`](#method.checked_sub_signed) to detect that. +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveDate}; +/// +/// 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) - Duration::zero(), hms(3, 5, 7)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(1), hms(3, 5, 6)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(-1), hms(3, 5, 8)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(3600 + 60), hms(2, 4, 7)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(86_400), +/// from_ymd(2016, 7, 7).and_hms(3, 5, 7)); +/// assert_eq!(hms(3, 5, 7) - Duration::days(365), +/// from_ymd(2015, 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, 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. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveDate}; +/// # 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 - Duration::zero(), hmsm(3, 5, 59, 1_300)); +/// assert_eq!(leap - Duration::milliseconds(200), hmsm(3, 5, 59, 1_100)); +/// assert_eq!(leap - Duration::milliseconds(500), hmsm(3, 5, 59, 800)); +/// 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 { + type Output = NaiveDateTime; + + #[inline] + fn sub(self, rhs: OldDuration) -> NaiveDateTime { + self.checked_sub_signed(rhs).expect("`NaiveDateTime - Duration` overflowed") + } +} + +impl SubAssign for NaiveDateTime { + #[inline] + fn sub_assign(&mut self, rhs: OldDuration) { + *self = self.sub(rhs); + } +} + +/// 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](./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 +/// **there are exactly one (or two) leap second(s) ever**. +/// +/// The implementation is a wrapper around +/// [`NaiveDateTime::signed_duration_since`](#method.signed_duration_since). +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveDate}; +/// +/// 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 * 86_400 + 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; fn main() { +/// # use chrono::{Duration, NaiveDate}; +/// # 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)); +/// # } +/// ~~~~ +impl Sub for NaiveDateTime { + type Output = OldDuration; + + #[inline] + fn sub(self, rhs: NaiveDateTime) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +/// The `Debug` output of the naive date and time `dt` is the same as +/// [`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`. +/// +/// It should be noted that, for leap seconds not on the minute boundary, +/// it may print a representation not distinguishable from non-leap seconds. +/// This doesn't matter in practice, since such leap seconds never happened. +/// (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.) +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveDate; +/// +/// let dt = NaiveDate::from_ymd(2016, 11, 15).and_hms(7, 39, 24); +/// assert_eq!(format!("{:?}", dt), "2016-11-15T07:39:24"); +/// ~~~~ +/// +/// Leap seconds may also be used. +/// +/// ~~~~ +/// # use chrono::NaiveDate; +/// let dt = NaiveDate::from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); +/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60.500"); +/// ~~~~ +impl fmt::Debug for NaiveDateTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}T{:?}", self.date, self.time) + } +} + +/// The `Display` output of the naive date and time `dt` is the same as +/// [`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. +/// This doesn't matter in practice, since such leap seconds never happened. +/// (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.) +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveDate; +/// +/// let dt = NaiveDate::from_ymd(2016, 11, 15).and_hms(7, 39, 24); +/// assert_eq!(format!("{}", dt), "2016-11-15 07:39:24"); +/// ~~~~ +/// +/// Leap seconds may also be used. +/// +/// ~~~~ +/// # use chrono::NaiveDate; +/// let dt = NaiveDate::from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); +/// assert_eq!(format!("{}", dt), "2015-06-30 23:59:60.500"); +/// ~~~~ +impl fmt::Display for NaiveDateTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.date, self.time) + } +} + +/// Parsing a `str` into a `NaiveDateTime` uses the same format, +/// [`%Y-%m-%dT%H:%M:%S%.f`](../format/strftime/index.html), as in `Debug`. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::{NaiveDateTime, NaiveDate}; +/// +/// let dt = NaiveDate::from_ymd(2015, 9, 18).and_hms(23, 56, 4); +/// assert_eq!("2015-09-18T23:56:04".parse::(), Ok(dt)); +/// +/// let dt = NaiveDate::from_ymd(12345, 6, 7).and_hms_milli(7, 59, 59, 1_500); // leap second +/// assert_eq!("+12345-6-7T7:59:60.5".parse::(), Ok(dt)); +/// +/// assert!("foo".parse::().is_err()); +/// ~~~~ +impl str::FromStr for NaiveDateTime { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult { + const ITEMS: &'static [Item<'static>] = &[ + Item::Numeric(Numeric::Year, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Month, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Day, Pad::Zero), + Item::Space(""), + Item::Literal("T"), // XXX shouldn't this be case-insensitive? + Item::Numeric(Numeric::Hour, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Minute, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Second, Pad::Zero), + Item::Fixed(Fixed::Nanosecond), + Item::Space(""), + ]; + + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_naive_datetime_with_offset(0) + } +} + +#[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::{MAX_DATE, MIN_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(&MIN_DATE.and_hms(0, 0, 0)).ok(), + Some(r#""-262144-01-01T00:00:00""#.into()) + ); + assert_eq!( + to_string(&MAX_DATE.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::{MAX_DATE, MIN_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(MIN_DATE.and_hms(0, 0, 0))); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), + 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(MAX_DATE.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#"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(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")] +pub mod rustc_serialize { + use super::NaiveDateTime; + use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + use std::ops::Deref; + + impl Encodable for NaiveDateTime { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } + } + + impl Decodable for NaiveDateTime { + fn decode(d: &mut D) -> Result { + d.read_str()?.parse().map_err(|_| d.error("invalid date time string")) + } + } + + /// A `DateTime` that can be deserialized from a seconds-based timestamp + #[derive(Debug)] + #[deprecated( + since = "1.4.2", + note = "RustcSerialize will be removed before chrono 1.0, use Serde instead" + )] + pub struct TsSeconds(NaiveDateTime); + + #[allow(deprecated)] + impl From for NaiveDateTime { + /// Pull the internal NaiveDateTime out + #[allow(deprecated)] + fn from(obj: TsSeconds) -> NaiveDateTime { + obj.0 + } + } + + #[allow(deprecated)] + impl Deref for TsSeconds { + type Target = NaiveDateTime; + + #[allow(deprecated)] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + #[allow(deprecated)] + impl Decodable for TsSeconds { + #[allow(deprecated)] + fn decode(d: &mut D) -> Result { + Ok(TsSeconds( + NaiveDateTime::from_timestamp_opt(d.read_i64()?, 0) + .ok_or_else(|| d.error("invalid timestamp"))?, + )) + } + } + + #[cfg(test)] + use rustc_serialize::json; + + #[test] + fn test_encodable() { + super::test_encodable_json(json::encode); + } + + #[test] + fn test_decodable() { + super::test_decodable_json(json::decode); + } + + #[test] + fn test_decodable_timestamps() { + super::test_decodable_json_timestamp(json::decode); + } +} + +/// Tools to help serializing/deserializing `NaiveDateTime`s +#[cfg(feature = "serde")] +pub mod serde { + use super::NaiveDateTime; + use core::fmt; + use serdelib::{de, ser}; + + /// Serialize a `NaiveDateTime` as an RFC 3339 string + /// + /// See [the `serde` module](./serde/index.html) for alternate + /// serialization formats. + impl ser::Serialize for NaiveDateTime { + 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) + } + } + + serializer.collect_str(&FormatWrapped { inner: &self }) + } + } + + struct NaiveDateTimeVisitor; + + impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor { + type Value = NaiveDateTime; + + 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(E::custom) + } + } + + impl<'de> de::Deserialize<'de> for NaiveDateTime { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(NaiveDateTimeVisitor) + } + } + + /// Used to serialize/deserialize from nanosecond-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::serde::ts_nanoseconds; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_nanoseconds")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let time = NaiveDate::from_ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// 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_nanoseconds { + use core::fmt; + use serdelib::{de, ser}; + + use {ne_timestamp, NaiveDateTime}; + + /// Serialize a UTC datetime into an integer number of nanoseconds 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::serde::ts_nanoseconds::serialize as to_nano_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_ts")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: NaiveDate::from_ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_nanos()) + } + + /// Deserialize a `DateTime` from a nanoseconds 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::serde::ts_nanoseconds::deserialize as from_nano_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_ts")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result + where + D: de::Deserializer<'de>, + { + Ok(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)?) + } + + struct NaiveDateTimeFromNanoSecondsVisitor; + + impl<'de> de::Visitor<'de> for NaiveDateTimeFromNanoSecondsVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp") + } + + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + value / 1_000_000_000, + (value % 1_000_000_000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + value as i64 / 1_000_000_000, + (value as i64 % 1_000_000_000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + } + } + + /// Used to serialize/deserialize from millisecond-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::serde::ts_milliseconds; + /// #[derive(Deserialize, Serialize)] + /// struct S { + /// #[serde(with = "ts_milliseconds")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let time = NaiveDate::from_ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918); + /// let my_s = S { + /// time: time.clone(), + /// }; + /// + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// 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_milliseconds { + use core::fmt; + use serdelib::{de, ser}; + + use {ne_timestamp, NaiveDateTime}; + + /// Serialize a UTC datetime into an integer number of milliseconds 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::serde::ts_milliseconds::serialize as to_milli_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_ts")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s = S { + /// time: NaiveDate::from_ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok(as_string) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_millis()) + } + + /// Deserialize a `DateTime` from a milliseconds 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::serde::ts_milliseconds::deserialize as from_milli_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_ts")] + /// time: NaiveDateTime + /// } + /// + /// # fn example() -> Result { + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; + /// # Ok(my_s) + /// # } + /// # fn main() { example().unwrap(); } + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result + where + D: de::Deserializer<'de>, + { + Ok(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)?) + } + + struct NaiveDateTimeFromMilliSecondsVisitor; + + impl<'de> de::Visitor<'de> for NaiveDateTimeFromMilliSecondsVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp") + } + + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt(value / 1000, ((value % 1000) * 1_000_000) as u32) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + (value / 1000) as i64, + ((value % 1000) * 1_000_000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + } + } + + /// 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::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 core::fmt; + use serdelib::{de, ser}; + + use {ne_timestamp, NaiveDateTime}; + + /// 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::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()) + } + + /// 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::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(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)?) + } + + struct NaiveDateTimeFromSecondsVisitor; + + impl<'de> de::Visitor<'de> for NaiveDateTimeFromSecondsVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("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(ne_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(ne_timestamp(value))) + } + } + } + + #[cfg(test)] + extern crate bincode; + #[cfg(test)] + extern crate serde_derive; + #[cfg(test)] + extern crate serde_json; + + #[test] + fn test_serde_serialize() { + super::test_encodable_json(self::serde_json::to_string); + } + + #[test] + fn test_serde_deserialize() { + super::test_decodable_json(|input| self::serde_json::from_str(&input)); + } + + // Bincode is relevant to test separately from JSON because + // it is not self-describing. + #[test] + fn test_serde_bincode() { + use self::bincode::{deserialize, serialize, Infinite}; + use naive::NaiveDate; + + let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90); + let encoded = serialize(&dt, Infinite).unwrap(); + let decoded: NaiveDateTime = deserialize(&encoded).unwrap(); + assert_eq!(dt, decoded); + } + + #[test] + fn test_serde_bincode_optional() { + use self::bincode::{deserialize, serialize, Infinite}; + use self::serde_derive::{Deserialize, Serialize}; + use prelude::*; + use serde::ts_nanoseconds_option; + + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Test { + one: Option, + #[serde(with = "ts_nanoseconds_option")] + two: Option>, + } + + let expected = Test { one: Some(1), two: Some(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1)) }; + let bytes: Vec = serialize(&expected, Infinite).unwrap(); + let actual = deserialize::(&(bytes)).unwrap(); + + assert_eq!(expected, actual); + } +} + +#[cfg(test)] +mod tests { + use super::NaiveDateTime; + use naive::{NaiveDate, MAX_DATE, MIN_DATE}; + use oldtime::Duration; + use std::i64; + use Datelike; + + #[test] + fn test_datetime_from_timestamp() { + let from_timestamp = |secs| NaiveDateTime::from_timestamp_opt(secs, 0); + let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); + assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); + assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); + assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); + assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); + assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); + assert_eq!(from_timestamp(i64::MIN), None); + assert_eq!(from_timestamp(i64::MAX), None); + } + + #[test] + fn test_datetime_add() { + fn check( + (y, m, d, h, n, s): (i32, u32, u32, u32, u32, u32), + rhs: Duration, + 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_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)), + ); + check( + (2014, 5, 6, 7, 8, 9), + Duration::seconds(-(3600 + 60 + 1)), + Some((2014, 5, 6, 6, 7, 8)), + ); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(86399), Some((2014, 5, 7, 7, 8, 8))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(-86_400 * 10), Some((2014, 4, 26, 7, 8, 9))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); + + // 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 = 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((MAX_DATE.year(), 12, 31, 23, 59, 59)), + ); + check((0, 1, 1, 0, 0, 0), max_days_from_year_0 + Duration::seconds(86_400), None); + check((0, 1, 1, 0, 0, 0), Duration::max_value(), None); + + 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); + } + + #[test] + fn test_datetime_sub() { + let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); + 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!( + since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), + Duration::seconds(-1) + ); + assert_eq!( + since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), + Duration::seconds(86399) + ); + assert_eq!( + since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)), + Duration::seconds(999_999_999) + ); + } + + #[test] + fn test_datetime_addassignment() { + let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); + let mut date = ymdhms(2016, 10, 1, 10, 10, 10); + date += Duration::minutes(10_000_000); + assert_eq!(date, ymdhms(2035, 10, 6, 20, 50, 10)); + date += Duration::days(10); + assert_eq!(date, ymdhms(2035, 10, 16, 20, 50, 10)); + } + + #[test] + fn test_datetime_subassignment() { + let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); + let mut date = ymdhms(2016, 10, 1, 10, 10, 10); + date -= Duration::minutes(10_000_000); + assert_eq!(date, ymdhms(1997, 9, 26, 23, 30, 10)); + date -= Duration::days(10); + assert_eq!(date, ymdhms(1997, 9, 16, 23, 30, 10)); + } + + #[test] + fn test_datetime_timestamp() { + let to_timestamp = + |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s).timestamp(); + assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1); + assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0); + assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1); + assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000); + assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); + } + + #[test] + fn test_datetime_from_str() { + // valid cases + let valid = [ + "2015-2-18T23:16:9.15", + "-77-02-18T23:16:09", + " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", + ]; + for &s in &valid { + let d = match s.parse::() { + Ok(d) => d, + Err(e) => panic!("parsing `{}` has failed: {}", s, e), + }; + let s_ = format!("{:?}", d); + // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same + let d_ = match s_.parse::() { + Ok(d) => d, + Err(e) => { + panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + } + }; + assert!( + d == d_, + "`{}` is parsed into `{:?}`, but reparsed result \ + `{:?}` does not match", + s, + d, + d_ + ); + } + + // some invalid cases + // since `ParseErrorKind` is private, all we can do is to check if there was an error + assert!("".parse::().is_err()); + assert!("x".parse::().is_err()); + assert!("15".parse::().is_err()); + assert!("15:8:9".parse::().is_err()); + assert!("15-8-9".parse::().is_err()); + assert!("2015-15-15T15:15:15".parse::().is_err()); + assert!("2012-12-12T12:12:12x".parse::().is_err()); + assert!("2012-123-12T12:12:12".parse::().is_err()); + assert!("+ 82701-123-12T12:12:12".parse::().is_err()); + assert!("+802701-123-12T12:12:12".parse::().is_err()); // out-of-bound + } + + #[test] + fn test_datetime_parse_from_str() { + let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); + let ymdhmsn = + |y, m, d, h, n, s, nano| NaiveDate::from_ymd(y, m, d).and_hms_nano(h, n, s, nano); + assert_eq!( + NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(ymdhms(2014, 5, 7, 12, 34, 56)) + ); // ignore offset + assert_eq!( + NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"), + Ok(ymdhms(2015, 2, 2, 0, 0, 0)) + ); + assert_eq!( + NaiveDateTime::parse_from_str( + "Fri, 09 Aug 2013 23:54:35 GMT", + "%a, %d %b %Y %H:%M:%S GMT" + ), + Ok(ymdhms(2013, 8, 9, 23, 54, 35)) + ); + assert!(NaiveDateTime::parse_from_str( + "Sat, 09 Aug 2013 23:54:35 GMT", + "%a, %d %b %Y %H:%M:%S GMT" + ) + .is_err()); + assert!(NaiveDateTime::parse_from_str("2014-5-7 12:3456", "%Y-%m-%d %H:%M:%S").is_err()); + assert!(NaiveDateTime::parse_from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient + assert_eq!( + NaiveDateTime::parse_from_str("1441497364", "%s"), + Ok(ymdhms(2015, 9, 5, 23, 56, 4)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1283929614.1234", "%s.%f"), + Ok(ymdhmsn(2010, 9, 8, 7, 6, 54, 1234)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"), + Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"), + Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"), + Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645)) + ); + } + + #[test] + fn test_datetime_format() { + let dt = NaiveDate::from_ymd(2010, 9, 8).and_hms_milli(7, 6, 54, 321); + assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010"); + assert_eq!(dt.format("%s").to_string(), "1283929614"); + assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); + + // a horror of leap second: coming near to you. + let dt = NaiveDate::from_ymd(2012, 6, 30).and_hms_milli(23, 59, 59, 1_000); + assert_eq!(dt.format("%c").to_string(), "Sat Jun 30 23:59:60 2012"); + assert_eq!(dt.format("%s").to_string(), "1341100799"); // not 1341100800, it's intentional. + } + + #[test] + fn test_datetime_add_sub_invariant() { + // issue #37 + 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.signed_duration_since(base).num_microseconds().unwrap()); + } + + #[test] + fn test_nanosecond_range() { + const A_BILLION: i64 = 1_000_000_000; + let maximum = "2262-04-11T23:47:16.854775804"; + let parsed: NaiveDateTime = maximum.parse().unwrap(); + let nanos = parsed.timestamp_nanos(); + assert_eq!( + parsed, + NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) + ); + + let minimum = "1677-09-21T00:12:44.000000000"; + let parsed: NaiveDateTime = minimum.parse().unwrap(); + let nanos = parsed.timestamp_nanos(); + assert_eq!( + parsed, + NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) + ); + } +} diff --git a/src/naive/internals.rs b/src/naive/internals.rs new file mode 100644 index 0000000..346063c --- /dev/null +++ b/src/naive/internals.rs @@ -0,0 +1,815 @@ +// 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 +#![cfg_attr(feature = "__internal_bench", allow(missing_docs))] + +use core::{fmt, i32}; +use div::{div_rem, mod_floor}; +use num_traits::FromPrimitive; +use Weekday; + +/// 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 = u32::from(YEAR_DELTAS[year_mod_400 as usize]); + if ordinal0 < delta { + year_mod_400 -= 1; + ordinal0 += 365 - u32::from(YEAR_DELTAS[year_mod_400 as usize]); + } else { + ordinal0 -= delta; + } + (year_mod_400, ordinal0 + 1) +} + +pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { + year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + 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 - u32::from(flags >> 3) + } + + #[inline] + pub fn isoweek_delta(&self) -> u32 { + let YearFlags(flags) = *self; + let mut delta = u32::from(flags) & 0b0111; + if delta < 3 { + delta += 7; + } + delta + } + + #[inline] + pub fn nisoweeks(&self) -> u32 { + let YearFlags(flags) = *self; + 52 + ((0b0000_0100_0000_0110 >> 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) | u32::from(flags)) + } + + #[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((i32::from(v) 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) | u32::from(flags)) + } + + #[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) | u32::from(flags)) + } + + #[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 + (u32::from(v) << 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 & 0b1_1111_1111) | (month << 9)) + } + + #[inline] + pub fn day(&self) -> u32 { + let Mdf(mdf) = *self; + (mdf >> 4) & 0b1_1111 + } + + #[inline] + pub fn with_day(&self, day: u32) -> Mdf { + let day = Mdf::clamp_day(day); + let Mdf(mdf) = *self; + Mdf((mdf & !0b1_1111_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) | u32::from(flags)) + } + + #[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) & 0b1_1111, + mdf & 0b1111, + YearFlags((mdf & 0b1111) as u8) + ) + } +} + +#[cfg(test)] +mod tests { + #[cfg(test)] + extern crate num_iter; + + use self::num_iter::range_inclusive; + use super::{Mdf, Of}; + use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF}; + use std::u32; + use Weekday; + + 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); + } + + #[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/isoweek.rs b/src/naive/isoweek.rs new file mode 100644 index 0000000..ece10f2 --- /dev/null +++ b/src/naive/isoweek.rs @@ -0,0 +1,163 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 week. + +use core::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 | DateImpl::from(of.flags().0) } +} + +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 the same as +/// [`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, MAX_DATE, MIN_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()); + } +} diff --git a/src/naive/time.rs b/src/naive/time.rs new file mode 100644 index 0000000..1ddc9fb --- /dev/null +++ b/src/naive/time.rs @@ -0,0 +1,1814 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 time without timezone. + +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::{fmt, hash, str}; +use oldtime::Duration as OldDuration; + +use div::div_mod_floor; +#[cfg(any(feature = "alloc", feature = "std", test))] +use format::DelayedFormat; +use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use format::{Fixed, Item, Numeric, Pad}; +use Timelike; + +pub const MIN_TIME: NaiveTime = NaiveTime { secs: 0, frac: 0 }; +pub const MAX_TIME: NaiveTime = NaiveTime { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 }; + +/// ISO 8601 time without timezone. +/// Allows for the nanosecond precision and optional leap second representation. +/// +/// # 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 does not yet provide such implementation, but it is planned. +/// +/// ## 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. +/// We will also use some intuitive concise notations for the explanation. +/// +/// `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`. +/// - `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` +/// (short for [`NaiveTime::overflowing_sub_signed`](#method.overflowing_sub_signed)): +/// +/// - `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` +/// (short for [`NaiveTime::signed_duration_since`](#method.signed_duration_since)): +/// +/// - `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, + frac: u32, +} + +impl NaiveTime { + /// Makes a new `NaiveTime` from hour, minute and second. + /// + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let t = NaiveTime::from_hms(23, 56, 4); + /// assert_eq!(t.hour(), 23); + /// assert_eq!(t.minute(), 56); + /// assert_eq!(t.second(), 4); + /// assert_eq!(t.nanosecond(), 0); + /// ~~~~ + #[inline] + pub fn from_hms(hour: u32, min: u32, sec: u32) -> NaiveTime { + NaiveTime::from_hms_opt(hour, min, sec).expect("invalid time") + } + + /// Makes a new `NaiveTime` from hour, minute and second. + /// + /// 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. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let from_hms_opt = NaiveTime::from_hms_opt; + /// + /// assert!(from_hms_opt(0, 0, 0).is_some()); + /// assert!(from_hms_opt(23, 59, 59).is_some()); + /// assert!(from_hms_opt(24, 0, 0).is_none()); + /// assert!(from_hms_opt(23, 60, 0).is_none()); + /// assert!(from_hms_opt(23, 59, 60).is_none()); + /// ~~~~ + #[inline] + pub fn from_hms_opt(hour: u32, min: u32, sec: u32) -> Option { + NaiveTime::from_hms_nano_opt(hour, min, sec, 0) + } + + /// Makes a new `NaiveTime` from hour, minute, second and millisecond. + /// + /// The millisecond part can exceed 1,000 + /// in order to represent the [leap second](#leap-second-handling). + /// + /// Panics on invalid hour, minute, second and/or millisecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let t = NaiveTime::from_hms_milli(23, 56, 4, 12); + /// assert_eq!(t.hour(), 23); + /// assert_eq!(t.minute(), 56); + /// assert_eq!(t.second(), 4); + /// assert_eq!(t.nanosecond(), 12_000_000); + /// ~~~~ + #[inline] + pub fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> NaiveTime { + NaiveTime::from_hms_milli_opt(hour, min, sec, milli).expect("invalid time") + } + + /// Makes a new `NaiveTime` from hour, minute, second and millisecond. + /// + /// The millisecond part can exceed 1,000 + /// in order to represent the [leap second](#leap-second-handling). + /// + /// Returns `None` on invalid hour, minute, second and/or millisecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let from_hmsm_opt = NaiveTime::from_hms_milli_opt; + /// + /// assert!(from_hmsm_opt(0, 0, 0, 0).is_some()); + /// assert!(from_hmsm_opt(23, 59, 59, 999).is_some()); + /// assert!(from_hmsm_opt(23, 59, 59, 1_999).is_some()); // a leap second after 23:59:59 + /// assert!(from_hmsm_opt(24, 0, 0, 0).is_none()); + /// assert!(from_hmsm_opt(23, 60, 0, 0).is_none()); + /// assert!(from_hmsm_opt(23, 59, 60, 0).is_none()); + /// assert!(from_hmsm_opt(23, 59, 59, 2_000).is_none()); + /// ~~~~ + #[inline] + pub fn from_hms_milli_opt(hour: u32, min: u32, sec: u32, milli: u32) -> Option { + milli + .checked_mul(1_000_000) + .and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano)) + } + + /// 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](#leap-second-handling). + /// + /// Panics on invalid hour, minute, second and/or microsecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let t = NaiveTime::from_hms_micro(23, 56, 4, 12_345); + /// assert_eq!(t.hour(), 23); + /// assert_eq!(t.minute(), 56); + /// assert_eq!(t.second(), 4); + /// assert_eq!(t.nanosecond(), 12_345_000); + /// ~~~~ + #[inline] + pub fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> NaiveTime { + NaiveTime::from_hms_micro_opt(hour, min, sec, micro).expect("invalid time") + } + + /// 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](#leap-second-handling). + /// + /// Returns `None` on invalid hour, minute, second and/or microsecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let from_hmsu_opt = NaiveTime::from_hms_micro_opt; + /// + /// assert!(from_hmsu_opt(0, 0, 0, 0).is_some()); + /// assert!(from_hmsu_opt(23, 59, 59, 999_999).is_some()); + /// assert!(from_hmsu_opt(23, 59, 59, 1_999_999).is_some()); // a leap second after 23:59:59 + /// assert!(from_hmsu_opt(24, 0, 0, 0).is_none()); + /// assert!(from_hmsu_opt(23, 60, 0, 0).is_none()); + /// assert!(from_hmsu_opt(23, 59, 60, 0).is_none()); + /// assert!(from_hmsu_opt(23, 59, 59, 2_000_000).is_none()); + /// ~~~~ + #[inline] + pub fn from_hms_micro_opt(hour: u32, min: u32, sec: u32, micro: u32) -> Option { + micro.checked_mul(1_000).and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano)) + } + + /// 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](#leap-second-handling). + /// + /// Panics on invalid hour, minute, second and/or nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(t.hour(), 23); + /// assert_eq!(t.minute(), 56); + /// assert_eq!(t.second(), 4); + /// assert_eq!(t.nanosecond(), 12_345_678); + /// ~~~~ + #[inline] + pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> NaiveTime { + NaiveTime::from_hms_nano_opt(hour, min, sec, nano).expect("invalid time") + } + + /// 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](#leap-second-handling). + /// + /// Returns `None` on invalid hour, minute, second and/or nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let from_hmsn_opt = NaiveTime::from_hms_nano_opt; + /// + /// assert!(from_hmsn_opt(0, 0, 0, 0).is_some()); + /// assert!(from_hmsn_opt(23, 59, 59, 999_999_999).is_some()); + /// assert!(from_hmsn_opt(23, 59, 59, 1_999_999_999).is_some()); // a leap second after 23:59:59 + /// assert!(from_hmsn_opt(24, 0, 0, 0).is_none()); + /// assert!(from_hmsn_opt(23, 60, 0, 0).is_none()); + /// assert!(from_hmsn_opt(23, 59, 60, 0).is_none()); + /// assert!(from_hmsn_opt(23, 59, 59, 2_000_000_000).is_none()); + /// ~~~~ + #[inline] + pub fn from_hms_nano_opt(hour: u32, min: u32, sec: u32, nano: u32) -> Option { + if hour >= 24 || min >= 60 || sec >= 60 || nano >= 2_000_000_000 { + return None; + } + let secs = hour * 3600 + min * 60 + sec; + Some(NaiveTime { secs: secs, frac: nano }) + } + + /// 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](#leap-second-handling). + /// + /// Panics on invalid number of seconds and/or nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let t = NaiveTime::from_num_seconds_from_midnight(86164, 12_345_678); + /// assert_eq!(t.hour(), 23); + /// assert_eq!(t.minute(), 56); + /// assert_eq!(t.second(), 4); + /// assert_eq!(t.nanosecond(), 12_345_678); + /// ~~~~ + #[inline] + pub fn from_num_seconds_from_midnight(secs: u32, nano: u32) -> NaiveTime { + NaiveTime::from_num_seconds_from_midnight_opt(secs, nano).expect("invalid time") + } + + /// 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](#leap-second-handling). + /// + /// Returns `None` on invalid number of seconds and/or nanosecond. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let from_nsecs_opt = NaiveTime::from_num_seconds_from_midnight_opt; + /// + /// assert!(from_nsecs_opt(0, 0).is_some()); + /// assert!(from_nsecs_opt(86399, 999_999_999).is_some()); + /// assert!(from_nsecs_opt(86399, 1_999_999_999).is_some()); // a leap second after 23:59:59 + /// assert!(from_nsecs_opt(86_400, 0).is_none()); + /// assert!(from_nsecs_opt(86399, 2_000_000_000).is_none()); + /// ~~~~ + #[inline] + pub fn from_num_seconds_from_midnight_opt(secs: u32, nano: u32) -> Option { + if secs >= 86_400 || nano >= 2_000_000_000 { + return None; + } + Some(NaiveTime { secs: secs, frac: nano }) + } + + /// Parses a string with the specified format string and returns a new `NaiveTime`. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let parse_from_str = NaiveTime::parse_from_str; + /// + /// assert_eq!(parse_from_str("23:56:04", "%H:%M:%S"), + /// Ok(NaiveTime::from_hms(23, 56, 4))); + /// assert_eq!(parse_from_str("pm012345.6789", "%p%I%M%S%.f"), + /// Ok(NaiveTime::from_hms_micro(13, 23, 45, 678_900))); + /// ~~~~ + /// + /// Date and offset is ignored for the purpose of parsing. + /// + /// ~~~~ + /// # use chrono::NaiveTime; + /// # let parse_from_str = NaiveTime::parse_from_str; + /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + /// Ok(NaiveTime::from_hms(12, 34, 56))); + /// ~~~~ + /// + /// [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.) + /// + /// ~~~~ + /// # use chrono::NaiveTime; + /// # let parse_from_str = NaiveTime::parse_from_str; + /// assert_eq!(parse_from_str("08:59:60.123", "%H:%M:%S%.f"), + /// Ok(NaiveTime::from_hms_milli(8, 59, 59, 1_123))); + /// ~~~~ + /// + /// Missing seconds are assumed to be zero, + /// but out-of-bound times or insufficient fields are errors otherwise. + /// + /// ~~~~ + /// # use chrono::NaiveTime; + /// # let parse_from_str = NaiveTime::parse_from_str; + /// assert_eq!(parse_from_str("7:15", "%H:%M"), + /// Ok(NaiveTime::from_hms(7, 15, 0))); + /// + /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); + /// assert!(parse_from_str("12", "%H").is_err()); + /// assert!(parse_from_str("17:60", "%H:%M").is_err()); + /// assert!(parse_from_str("24:00:00", "%H:%M:%S").is_err()); + /// ~~~~ + /// + /// All parsed fields should be consistent to each other, otherwise it's an error. + /// Here `%H` is for 24-hour clocks, unlike `%I`, + /// and thus can be independently determined without AM/PM. + /// + /// ~~~~ + /// # use chrono::NaiveTime; + /// # let parse_from_str = NaiveTime::parse_from_str; + /// assert!(parse_from_str("13:07 AM", "%H:%M %p").is_err()); + /// ~~~~ + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_time() + } + + /// Adds given `Duration` to the current time, + /// and also returns the number of *seconds* + /// in the integral number of days ignored from the addition. + /// (We cannot return `Duration` because it is subject to overflow or underflow.) + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveTime}; + /// + /// let from_hms = NaiveTime::from_hms; + /// + /// 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_signed(Duration::hours(23)), + /// (from_hms(2, 4, 5), 86_400)); + /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(-7)), + /// (from_hms(20, 4, 5), -86_400)); + /// # } + /// ~~~~ + #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] + pub fn overflowing_add_signed(&self, mut rhs: OldDuration) -> (NaiveTime, i64) { + let mut secs = self.secs; + let mut frac = self.frac; + + // check if `self` is a leap second and adding `rhs` would escape that leap second. + // if it's the case, update `self` and `rhs` to involve no leap second; + // otherwise the addition immediately finishes. + if frac >= 1_000_000_000 { + let rfrac = 2_000_000_000 - frac; + if rhs >= OldDuration::nanoseconds(i64::from(rfrac)) { + rhs = rhs - OldDuration::nanoseconds(i64::from(rfrac)); + secs += 1; + frac = 0; + } else if rhs < OldDuration::nanoseconds(-i64::from(frac)) { + rhs = rhs + OldDuration::nanoseconds(i64::from(frac)); + frac = 0; + } else { + frac = (i64::from(frac) + rhs.num_nanoseconds().unwrap()) as u32; + debug_assert!(frac < 2_000_000_000); + return (NaiveTime { secs: secs, frac: frac }, 0); + } + } + debug_assert!(secs <= 86_400); + debug_assert!(frac < 1_000_000_000); + + let rhssecs = rhs.num_seconds(); + let rhsfrac = (rhs - OldDuration::seconds(rhssecs)).num_nanoseconds().unwrap(); + debug_assert_eq!(OldDuration::seconds(rhssecs) + OldDuration::nanoseconds(rhsfrac), rhs); + let rhssecsinday = rhssecs % 86_400; + let mut morerhssecs = rhssecs - rhssecsinday; + let rhssecs = rhssecsinday as i32; + let rhsfrac = rhsfrac as i32; + debug_assert!(-86_400 < rhssecs && rhssecs < 86_400); + debug_assert_eq!(morerhssecs % 86_400, 0); + debug_assert!(-1_000_000_000 < rhsfrac && rhsfrac < 1_000_000_000); + + let mut secs = secs as i32 + rhssecs; + let mut frac = frac as i32 + rhsfrac; + debug_assert!(-86_400 < secs && secs < 2 * 86_400); + debug_assert!(-1_000_000_000 < frac && frac < 2_000_000_000); + + if frac < 0 { + frac += 1_000_000_000; + secs -= 1; + } else if frac >= 1_000_000_000 { + frac -= 1_000_000_000; + secs += 1; + } + debug_assert!(-86_400 <= secs && secs < 2 * 86_400); + debug_assert!(0 <= frac && frac < 1_000_000_000); + + if secs < 0 { + secs += 86_400; + morerhssecs -= 86_400; + } else if secs >= 86_400 { + secs -= 86_400; + morerhssecs += 86_400; + } + debug_assert!(0 <= secs && secs < 86_400); + + (NaiveTime { secs: secs as u32, frac: frac as u32 }, morerhssecs) + } + + /// Subtracts given `Duration` from the current time, + /// and also returns the number of *seconds* + /// in the integral number of days ignored from the subtraction. + /// (We cannot return `Duration` because it is subject to overflow or underflow.) + /// + /// # Example + /// + /// ~~~~ + /// # extern crate chrono; fn main() { + /// use chrono::{Duration, NaiveTime}; + /// + /// let from_hms = NaiveTime::from_hms; + /// + /// 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_signed(Duration::hours(17)), + /// (from_hms(10, 4, 5), 86_400)); + /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(-22)), + /// (from_hms(1, 4, 5), -86_400)); + /// # } + /// ~~~~ + #[inline] + 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](#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; fn main() { + /// use chrono::{Duration, NaiveTime}; + /// + /// 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; fn main() { + /// # use chrono::{Duration, NaiveTime}; + /// # 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 core::cmp::Ordering; + + let secs = i64::from(self.secs) - i64::from(rhs.secs); + let frac = i64::from(self.frac) - i64::from(rhs.frac); + + // `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) + } + + /// Formats the time with the specified formatting items. + /// Otherwise it is the same as the ordinary [`format`](#method.format) method. + /// + /// The `Iterator` of items should be `Clone`able, + /// since the resulting `DelayedFormat` value may be formatted multiple times. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// use chrono::format::strftime::StrftimeItems; + /// + /// let fmt = StrftimeItems::new("%H:%M:%S"); + /// let t = NaiveTime::from_hms(23, 56, 4); + /// assert_eq!(t.format_with_items(fmt.clone()).to_string(), "23:56:04"); + /// assert_eq!(t.format("%H:%M:%S").to_string(), "23:56:04"); + /// ~~~~ + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ~~~~ + /// # use chrono::NaiveTime; + /// # use chrono::format::strftime::StrftimeItems; + /// # let fmt = StrftimeItems::new("%H:%M:%S").clone(); + /// # let t = NaiveTime::from_hms(23, 56, 4); + /// assert_eq!(format!("{}", t.format_with_items(fmt)), "23:56:04"); + /// ~~~~ + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat + where + I: Iterator + Clone, + B: Borrow>, + { + DelayedFormat::new(None, Some(*self), items) + } + + /// Formats the time with the specified format string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// This returns a `DelayedFormat`, + /// which gets converted to a string only when actual formatting happens. + /// You may use the `to_string` method to get a `String`, + /// or just feed it into `print!` and other formatting macros. + /// (In this way it avoids the redundant memory allocation.) + /// + /// A wrong format string does *not* issue an error immediately. + /// Rather, converting or formatting the `DelayedFormat` fails. + /// You are recommended to immediately use `DelayedFormat` for this reason. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::NaiveTime; + /// + /// let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(t.format("%H:%M:%S").to_string(), "23:56:04"); + /// assert_eq!(t.format("%H:%M:%S%.6f").to_string(), "23:56:04.012345"); + /// assert_eq!(t.format("%-I:%M %p").to_string(), "11:56 PM"); + /// ~~~~ + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ~~~~ + /// # use chrono::NaiveTime; + /// # let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(format!("{}", t.format("%H:%M:%S")), "23:56:04"); + /// assert_eq!(format!("{}", t.format("%H:%M:%S%.6f")), "23:56:04.012345"); + /// assert_eq!(format!("{}", t.format("%-I:%M %p")), "11:56 PM"); + /// ~~~~ + #[cfg(any(feature = "alloc", feature = "std", test))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { + self.format_with_items(StrftimeItems::new(fmt)) + } + + /// Returns a triple of the hour, minute and second numbers. + fn hms(&self) -> (u32, u32, u32) { + let (mins, sec) = div_mod_floor(self.secs, 60); + let (hour, min) = div_mod_floor(mins, 60); + (hour, min, sec) + } +} + +impl Timelike for NaiveTime { + /// Returns the hour number from 0 to 23. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// assert_eq!(NaiveTime::from_hms(0, 0, 0).hour(), 0); + /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).hour(), 23); + /// ~~~~ + #[inline] + fn hour(&self) -> u32 { + self.hms().0 + } + + /// Returns the minute number from 0 to 59. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// assert_eq!(NaiveTime::from_hms(0, 0, 0).minute(), 0); + /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).minute(), 56); + /// ~~~~ + #[inline] + fn minute(&self) -> u32 { + self.hms().1 + } + + /// Returns the second number from 0 to 59. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// assert_eq!(NaiveTime::from_hms(0, 0, 0).second(), 0); + /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).second(), 4); + /// ~~~~ + /// + /// This method never returns 60 even when it is a leap second. + /// ([Why?](#leap-second-handling)) + /// Use the proper [formatting method](#method.format) to get a human-readable representation. + /// + /// ~~~~ + /// # use chrono::{NaiveTime, Timelike}; + /// let leap = NaiveTime::from_hms_milli(23, 59, 59, 1_000); + /// assert_eq!(leap.second(), 59); + /// assert_eq!(leap.format("%H:%M:%S").to_string(), "23:59:60"); + /// ~~~~ + #[inline] + fn second(&self) -> u32 { + self.hms().2 + } + + /// 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](#leap-second-handling). + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// assert_eq!(NaiveTime::from_hms(0, 0, 0).nanosecond(), 0); + /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).nanosecond(), 12_345_678); + /// ~~~~ + /// + /// Leap seconds may have seemingly out-of-range return values. + /// You can reduce the range with `time.nanosecond() % 1_000_000_000`, or + /// use the proper [formatting method](#method.format) to get a human-readable representation. + /// + /// ~~~~ + /// # use chrono::{NaiveTime, Timelike}; + /// let leap = NaiveTime::from_hms_milli(23, 59, 59, 1_000); + /// assert_eq!(leap.nanosecond(), 1_000_000_000); + /// assert_eq!(leap.format("%H:%M:%S%.9f").to_string(), "23:59:60.000000000"); + /// ~~~~ + #[inline] + fn nanosecond(&self) -> u32 { + self.frac + } + + /// Makes a new `NaiveTime` with the hour number changed. + /// + /// Returns `None` when the resulting `NaiveTime` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(dt.with_hour(7), Some(NaiveTime::from_hms_nano(7, 56, 4, 12_345_678))); + /// assert_eq!(dt.with_hour(24), None); + /// ~~~~ + #[inline] + fn with_hour(&self, hour: u32) -> Option { + if hour >= 24 { + return None; + } + let secs = hour * 3600 + self.secs % 3600; + Some(NaiveTime { secs: secs, ..*self }) + } + + /// Makes a new `NaiveTime` with the minute number changed. + /// + /// Returns `None` when the resulting `NaiveTime` would be invalid. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(dt.with_minute(45), Some(NaiveTime::from_hms_nano(23, 45, 4, 12_345_678))); + /// assert_eq!(dt.with_minute(60), None); + /// ~~~~ + #[inline] + fn with_minute(&self, min: u32) -> Option { + if min >= 60 { + return None; + } + let secs = self.secs / 3600 * 3600 + min * 60 + self.secs % 60; + Some(NaiveTime { secs: secs, ..*self }) + } + + /// Makes a new `NaiveTime` with the second number changed. + /// + /// Returns `None` when the resulting `NaiveTime` would be invalid. + /// As with the [`second`](#method.second) method, + /// the input range is restricted to 0 through 59. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(dt.with_second(17), Some(NaiveTime::from_hms_nano(23, 56, 17, 12_345_678))); + /// assert_eq!(dt.with_second(60), None); + /// ~~~~ + #[inline] + fn with_second(&self, sec: u32) -> Option { + if sec >= 60 { + return None; + } + let secs = self.secs / 60 * 60 + sec; + Some(NaiveTime { secs: secs, ..*self }) + } + + /// Makes a new `NaiveTime` with nanoseconds since the whole non-leap second changed. + /// + /// Returns `None` when the resulting `NaiveTime` would be invalid. + /// As with the [`nanosecond`](#method.nanosecond) method, + /// the input range can exceed 1,000,000,000 for leap seconds. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(dt.with_nanosecond(333_333_333), + /// Some(NaiveTime::from_hms_nano(23, 56, 4, 333_333_333))); + /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); + /// ~~~~ + /// + /// Leap seconds can theoretically follow *any* whole second. + /// The following would be a proper leap second at the time zone offset of UTC-00:03:57 + /// (there are several historical examples comparable to this "non-sense" offset), + /// and therefore is allowed. + /// + /// ~~~~ + /// # use chrono::{NaiveTime, Timelike}; + /// # let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// assert_eq!(dt.with_nanosecond(1_333_333_333), + /// Some(NaiveTime::from_hms_nano(23, 56, 4, 1_333_333_333))); + /// ~~~~ + #[inline] + fn with_nanosecond(&self, nano: u32) -> Option { + if nano >= 2_000_000_000 { + return None; + } + Some(NaiveTime { frac: nano, ..*self }) + } + + /// Returns the number of non-leap seconds past the last midnight. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{NaiveTime, Timelike}; + /// + /// assert_eq!(NaiveTime::from_hms(1, 2, 3).num_seconds_from_midnight(), + /// 3723); + /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).num_seconds_from_midnight(), + /// 86164); + /// assert_eq!(NaiveTime::from_hms_milli(23, 59, 59, 1_000).num_seconds_from_midnight(), + /// 86399); + /// ~~~~ + #[inline] + fn num_seconds_from_midnight(&self) -> u32 { + self.secs // do not repeat the calculation! + } +} + +/// `NaiveTime` can be used as a key to the hash maps (in principle). +/// +/// Practically this also takes account of fractional seconds, so it is not recommended. +/// (For the obvious reason this also distinguishes leap seconds from non-leap seconds.) +impl hash::Hash for NaiveTime { + fn hash(&self, state: &mut H) { + self.secs.hash(state); + self.frac.hash(state); + } +} + +/// 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](#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**. +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveTime}; +/// +/// let from_hmsm = NaiveTime::from_hms_milli; +/// +/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::zero(), from_hmsm(3, 5, 7, 0)); +/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(1), from_hmsm(3, 5, 8, 0)); +/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(-1), from_hmsm(3, 5, 6, 0)); +/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(60 + 4), from_hmsm(3, 6, 11, 0)); +/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(7*60*60 - 6*60), from_hmsm(9, 59, 7, 0)); +/// 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. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveTime}; +/// # 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. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveTime}; +/// # 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)); +/// assert_eq!(leap + Duration::milliseconds(-500), from_hmsm(3, 5, 59, 800)); +/// assert_eq!(leap + Duration::milliseconds(500), from_hmsm(3, 5, 59, 1_800)); +/// assert_eq!(leap + Duration::milliseconds(800), from_hmsm(3, 6, 0, 100)); +/// 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 { + type Output = NaiveTime; + + #[inline] + fn add(self, rhs: OldDuration) -> NaiveTime { + self.overflowing_add_signed(rhs).0 + } +} + +impl AddAssign for NaiveTime { + #[inline] + fn add_assign(&mut self, rhs: OldDuration) { + *self = self.add(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 the same as the addition with a negated `Duration`. +/// +/// 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**. +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveTime}; +/// +/// let from_hmsm = NaiveTime::from_hms_milli; +/// +/// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::zero(), from_hmsm(3, 5, 7, 0)); +/// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(1), from_hmsm(3, 5, 6, 0)); +/// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(60 + 5), from_hmsm(3, 4, 2, 0)); +/// 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. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveTime}; +/// # 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. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveTime}; +/// # 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)); +/// assert_eq!(leap - Duration::milliseconds(200), from_hmsm(3, 5, 59, 1_100)); +/// 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 { + type Output = NaiveTime; + + #[inline] + fn sub(self, rhs: OldDuration) -> NaiveTime { + self.overflowing_sub_signed(rhs).0 + } +} + +impl SubAssign for NaiveTime { + #[inline] + fn sub_assign(&mut self, rhs: OldDuration) { + *self = self.sub(rhs); + } +} + +/// 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](#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**. +/// +/// The implementation is a wrapper around +/// [`NaiveTime::signed_duration_since`](#method.signed_duration_since). +/// +/// # Example +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// use chrono::{Duration, NaiveTime}; +/// +/// 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. +/// +/// ~~~~ +/// # extern crate chrono; fn main() { +/// # use chrono::{Duration, NaiveTime}; +/// # 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)); +/// # } +/// ~~~~ +impl Sub for NaiveTime { + type Output = OldDuration; + + #[inline] + fn sub(self, rhs: NaiveTime) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +/// The `Debug` output of the naive time `t` is the same as +/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html). +/// +/// The string printed can be readily parsed via the `parse` method on `str`. +/// +/// It should be noted that, for leap seconds not on the minute boundary, +/// it may print a representation not distinguishable from non-leap seconds. +/// This doesn't matter in practice, since such leap seconds never happened. +/// (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.) +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveTime; +/// +/// assert_eq!(format!("{:?}", NaiveTime::from_hms(23, 56, 4)), "23:56:04"); +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_milli(23, 56, 4, 12)), "23:56:04.012"); +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_micro(23, 56, 4, 1234)), "23:56:04.001234"); +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_nano(23, 56, 4, 123456)), "23:56:04.000123456"); +/// ~~~~ +/// +/// Leap seconds may also be used. +/// +/// ~~~~ +/// # use chrono::NaiveTime; +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_milli(6, 59, 59, 1_500)), "06:59:60.500"); +/// ~~~~ +impl fmt::Debug for NaiveTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (hour, min, sec) = self.hms(); + let (sec, nano) = if self.frac >= 1_000_000_000 { + (sec + 1, self.frac - 1_000_000_000) + } else { + (sec, self.frac) + }; + + write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?; + if nano == 0 { + Ok(()) + } else if nano % 1_000_000 == 0 { + write!(f, ".{:03}", nano / 1_000_000) + } else if nano % 1_000 == 0 { + write!(f, ".{:06}", nano / 1_000) + } else { + write!(f, ".{:09}", nano) + } + } +} + +/// The `Display` output of the naive time `t` is the same as +/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html). +/// +/// The string printed can be readily parsed via the `parse` method on `str`. +/// +/// It should be noted that, for leap seconds not on the minute boundary, +/// it may print a representation not distinguishable from non-leap seconds. +/// This doesn't matter in practice, since such leap seconds never happened. +/// (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.) +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveTime; +/// +/// assert_eq!(format!("{}", NaiveTime::from_hms(23, 56, 4)), "23:56:04"); +/// assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 56, 4, 12)), "23:56:04.012"); +/// assert_eq!(format!("{}", NaiveTime::from_hms_micro(23, 56, 4, 1234)), "23:56:04.001234"); +/// assert_eq!(format!("{}", NaiveTime::from_hms_nano(23, 56, 4, 123456)), "23:56:04.000123456"); +/// ~~~~ +/// +/// Leap seconds may also be used. +/// +/// ~~~~ +/// # use chrono::NaiveTime; +/// assert_eq!(format!("{}", NaiveTime::from_hms_milli(6, 59, 59, 1_500)), "06:59:60.500"); +/// ~~~~ +impl fmt::Display for NaiveTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +/// Parsing a `str` into a `NaiveTime` uses the same format, +/// [`%H:%M:%S%.f`](../format/strftime/index.html), as in `Debug` and `Display`. +/// +/// # Example +/// +/// ~~~~ +/// use chrono::NaiveTime; +/// +/// let t = NaiveTime::from_hms(23, 56, 4); +/// assert_eq!("23:56:04".parse::(), Ok(t)); +/// +/// let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); +/// assert_eq!("23:56:4.012345678".parse::(), Ok(t)); +/// +/// let t = NaiveTime::from_hms_nano(23, 59, 59, 1_234_567_890); // leap second +/// assert_eq!("23:59:60.23456789".parse::(), Ok(t)); +/// +/// assert!("foo".parse::().is_err()); +/// ~~~~ +impl str::FromStr for NaiveTime { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult { + const ITEMS: &'static [Item<'static>] = &[ + Item::Numeric(Numeric::Hour, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Minute, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Second, Pad::Zero), + Item::Fixed(Fixed::Nanosecond), + Item::Space(""), + ]; + + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_naive_time() + } +} + +#[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::{Decodable, Decoder, Encodable, Encoder}; + + impl Encodable for NaiveTime { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } + } + + impl Decodable for NaiveTime { + fn decode(d: &mut D) -> Result { + d.read_str()?.parse().map_err(|_| d.error("invalid time")) + } + } + + #[cfg(test)] + use rustc_serialize::json; + + #[test] + fn test_encodable() { + super::test_encodable_json(json::encode); + } + + #[test] + fn test_decodable() { + super::test_decodable_json(json::decode); + } +} + +#[cfg(feature = "serde")] +mod serde { + use super::NaiveTime; + use core::fmt; + use serdelib::{de, ser}; + + // 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) + + impl ser::Serialize for NaiveTime { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.collect_str(&self) + } + } + + struct NaiveTimeVisitor; + + impl<'de> de::Visitor<'de> for NaiveTimeVisitor { + type Value = NaiveTime; + + 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(E::custom) + } + } + + impl<'de> de::Deserialize<'de> for NaiveTime { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(NaiveTimeVisitor) + } + } + + #[cfg(test)] + extern crate bincode; + #[cfg(test)] + extern crate serde_json; + + #[test] + fn test_serde_serialize() { + super::test_encodable_json(self::serde_json::to_string); + } + + #[test] + fn test_serde_deserialize() { + super::test_decodable_json(|input| self::serde_json::from_str(&input)); + } + + #[test] + fn test_serde_bincode() { + // Bincode is relevant to test separately from JSON because + // it is not self-describing. + use self::bincode::{deserialize, serialize, Infinite}; + + let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); + let encoded = serialize(&t, Infinite).unwrap(); + let decoded: NaiveTime = deserialize(&encoded).unwrap(); + assert_eq!(t, decoded); + } +} + +#[cfg(test)] +mod tests { + use super::NaiveTime; + use oldtime::Duration; + use std::u32; + use Timelike; + + #[test] + fn test_time_from_hms_milli() { + assert_eq!( + NaiveTime::from_hms_milli_opt(3, 5, 7, 0), + Some(NaiveTime::from_hms_nano(3, 5, 7, 0)) + ); + assert_eq!( + NaiveTime::from_hms_milli_opt(3, 5, 7, 777), + Some(NaiveTime::from_hms_nano(3, 5, 7, 777_000_000)) + ); + assert_eq!( + NaiveTime::from_hms_milli_opt(3, 5, 7, 1_999), + Some(NaiveTime::from_hms_nano(3, 5, 7, 1_999_000_000)) + ); + assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, 2_000), None); + assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, 5_000), None); // overflow check + assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, u32::MAX), None); + } + + #[test] + fn test_time_from_hms_micro() { + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 0), + Some(NaiveTime::from_hms_nano(3, 5, 7, 0)) + ); + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 333), + Some(NaiveTime::from_hms_nano(3, 5, 7, 333_000)) + ); + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 777_777), + Some(NaiveTime::from_hms_nano(3, 5, 7, 777_777_000)) + ); + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 1_999_999), + Some(NaiveTime::from_hms_nano(3, 5, 7, 1_999_999_000)) + ); + assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, 2_000_000), None); + assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, 5_000_000), None); // overflow check + assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, u32::MAX), None); + } + + #[test] + fn test_time_hms() { + assert_eq!(NaiveTime::from_hms(3, 5, 7).hour(), 3); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(0), Some(NaiveTime::from_hms(0, 5, 7))); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(23), Some(NaiveTime::from_hms(23, 5, 7))); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(24), None); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(u32::MAX), None); + + assert_eq!(NaiveTime::from_hms(3, 5, 7).minute(), 5); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_minute(0), Some(NaiveTime::from_hms(3, 0, 7))); + assert_eq!( + NaiveTime::from_hms(3, 5, 7).with_minute(59), + Some(NaiveTime::from_hms(3, 59, 7)) + ); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_minute(60), None); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_minute(u32::MAX), None); + + assert_eq!(NaiveTime::from_hms(3, 5, 7).second(), 7); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_second(0), Some(NaiveTime::from_hms(3, 5, 0))); + assert_eq!( + NaiveTime::from_hms(3, 5, 7).with_second(59), + Some(NaiveTime::from_hms(3, 5, 59)) + ); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_second(60), None); + assert_eq!(NaiveTime::from_hms(3, 5, 7).with_second(u32::MAX), None); + } + + #[test] + fn test_time_add() { + macro_rules! check { + ($lhs:expr, $rhs:expr, $sum:expr) => {{ + assert_eq!($lhs + $rhs, $sum); + //assert_eq!($rhs + $lhs, $sum); + }}; + } + + let hmsm = |h, m, s, mi| NaiveTime::from_hms_milli(h, m, s, mi); + + check!(hmsm(3, 5, 7, 900), Duration::zero(), hmsm(3, 5, 7, 900)); + check!(hmsm(3, 5, 7, 900), Duration::milliseconds(100), hmsm(3, 5, 8, 0)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-1800), hmsm(3, 5, 6, 500)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-800), hmsm(3, 5, 7, 500)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-100), hmsm(3, 5, 7, 1_200)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(100), hmsm(3, 5, 7, 1_400)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(800), hmsm(3, 5, 8, 100)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(1800), hmsm(3, 5, 9, 100)); + check!(hmsm(3, 5, 7, 900), Duration::seconds(86399), hmsm(3, 5, 6, 900)); // overwrap + check!(hmsm(3, 5, 7, 900), Duration::seconds(-86399), hmsm(3, 5, 8, 900)); + check!(hmsm(3, 5, 7, 900), Duration::days(12345), hmsm(3, 5, 7, 900)); + check!(hmsm(3, 5, 7, 1_300), Duration::days(1), hmsm(3, 5, 7, 300)); + check!(hmsm(3, 5, 7, 1_300), Duration::days(-1), hmsm(3, 5, 8, 300)); + + // regression tests for #37 + check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-990), hmsm(23, 59, 59, 10)); + check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-9990), hmsm(23, 59, 50, 10)); + } + + #[test] + fn test_time_overflowing_add() { + let hmsm = NaiveTime::from_hms_milli; + + 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_signed(Duration::hours(23)), + (hmsm(2, 4, 5, 678), 86_400) + ); + assert_eq!( + hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(-7)), + (hmsm(20, 4, 5, 678), -86_400) + ); + + // 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), 86_400) + ); + assert_eq!( + hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(-1)), + (hmsm(3, 4, 6, 678), -86_400) + ); + } + + #[test] + fn test_time_addassignment() { + let hms = NaiveTime::from_hms; + let mut time = hms(12, 12, 12); + time += Duration::hours(10); + assert_eq!(time, hms(22, 12, 12)); + time += Duration::hours(10); + assert_eq!(time, hms(8, 12, 12)); + } + + #[test] + fn test_time_subassignment() { + let hms = NaiveTime::from_hms; + let mut time = hms(12, 12, 12); + time -= Duration::hours(10); + assert_eq!(time, hms(2, 12, 12)); + time -= Duration::hours(10); + assert_eq!(time, hms(16, 12, 12)); + } + + #[test] + fn test_time_sub() { + macro_rules! check { + ($lhs:expr, $rhs:expr, $diff:expr) => {{ + // `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration` + assert_eq!($lhs.signed_duration_since($rhs), $diff); + assert_eq!($rhs.signed_duration_since($lhs), -$diff); + }}; + } + + let hmsm = |h, m, s, mi| NaiveTime::from_hms_milli(h, m, s, mi); + + check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), Duration::zero()); + check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), Duration::milliseconds(300)); + check!(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), Duration::seconds(3600 + 60 + 1)); + check!( + hmsm(3, 5, 7, 200), + hmsm(2, 4, 6, 300), + Duration::seconds(3600 + 60) + Duration::milliseconds(900) + ); + + // treats the leap second as if it coincides with the prior non-leap second, + // as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence. + check!(hmsm(3, 5, 7, 200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(400)); + check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(1400)); + check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), Duration::milliseconds(1400)); + + // additional equality: `time1 + duration = time2` is equivalent to + // `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second. + assert_eq!(hmsm(3, 5, 6, 800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200)); + assert_eq!(hmsm(3, 5, 6, 1_800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200)); + } + + #[test] + fn test_time_fmt() { + assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 999)), "23:59:59.999"); + assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 1_000)), "23:59:60"); + assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 1_001)), "23:59:60.001"); + assert_eq!(format!("{}", NaiveTime::from_hms_micro(0, 0, 0, 43210)), "00:00:00.043210"); + assert_eq!(format!("{}", NaiveTime::from_hms_nano(0, 0, 0, 6543210)), "00:00:00.006543210"); + + // the format specifier should have no effect on `NaiveTime` + assert_eq!(format!("{:30}", NaiveTime::from_hms_milli(3, 5, 7, 9)), "03:05:07.009"); + } + + #[test] + fn test_date_from_str() { + // valid cases + let valid = [ + "0:0:0", + "0:0:0.0000000", + "0:0:0.0000003", + " 4 : 3 : 2.1 ", + " 09:08:07 ", + " 9:8:07 ", + "23:59:60.373929310237", + ]; + for &s in &valid { + let d = match s.parse::() { + Ok(d) => d, + Err(e) => panic!("parsing `{}` has failed: {}", s, e), + }; + let s_ = format!("{:?}", d); + // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same + let d_ = match s_.parse::() { + Ok(d) => d, + Err(e) => { + panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + } + }; + assert!( + d == d_, + "`{}` is parsed into `{:?}`, but reparsed result \ + `{:?}` does not match", + s, + d, + d_ + ); + } + + // some invalid cases + // since `ParseErrorKind` is private, all we can do is to check if there was an error + assert!("".parse::().is_err()); + assert!("x".parse::().is_err()); + assert!("15".parse::().is_err()); + assert!("15:8".parse::().is_err()); + assert!("15:8:x".parse::().is_err()); + assert!("15:8:9x".parse::().is_err()); + assert!("23:59:61".parse::().is_err()); + assert!("12:34:56.x".parse::().is_err()); + assert!("12:34:56. 0".parse::().is_err()); + } + + #[test] + fn test_time_parse_from_str() { + let hms = |h, m, s| NaiveTime::from_hms(h, m, s); + assert_eq!( + NaiveTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(hms(12, 34, 56)) + ); // ignore date and offset + assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0))); + assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err()); + } + + #[test] + fn test_time_format() { + let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); + assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM"); + assert_eq!(t.format("%M").to_string(), "05"); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,098765432,.098765432"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".098,.098765,.098765432"); + assert_eq!(t.format("%R").to_string(), "03:05"); + assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07"); + assert_eq!(t.format("%r").to_string(), "03:05:07 AM"); + assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); + + let t = NaiveTime::from_hms_micro(3, 5, 7, 432100); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,432100000,.432100"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".432,.432100,.432100000"); + + let t = NaiveTime::from_hms_milli(3, 5, 7, 210); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,210000000,.210"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".210,.210000,.210000000"); + + let t = NaiveTime::from_hms(3, 5, 7); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,000000000,"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".000,.000000,.000000000"); + + // corner cases + assert_eq!(NaiveTime::from_hms(13, 57, 9).format("%r").to_string(), "01:57:09 PM"); + assert_eq!( + NaiveTime::from_hms_milli(23, 59, 59, 1_000).format("%X").to_string(), + "23:59:60" + ); + } +} diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs new file mode 100644 index 0000000..83f42a1 --- /dev/null +++ b/src/offset/fixed.rs @@ -0,0 +1,244 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! The time zone which has a fixed offset from UTC. + +use core::fmt; +use core::ops::{Add, Sub}; +use oldtime::Duration as OldDuration; + +use super::{LocalResult, Offset, TimeZone}; +use div::div_mod_floor; +use naive::{NaiveDate, NaiveDateTime, NaiveTime}; +use DateTime; +use Timelike; + +/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. +/// +/// 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. +#[derive(PartialEq, Eq, Hash, Copy, Clone)] +pub struct FixedOffset { + local_minus_utc: i32, +} + +impl FixedOffset { + /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference. + /// 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") + } + + /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference. + /// The negative `secs` means the Western Hemisphere. + /// + /// Returns `None` on the out-of-bound `secs`. + pub fn east_opt(secs: i32) -> Option { + if -86_400 < secs && secs < 86_400 { + Some(FixedOffset { local_minus_utc: secs }) + } else { + None + } + } + + /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference. + /// 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") + } + + /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference. + /// The negative `secs` means the Eastern Hemisphere. + /// + /// Returns `None` on the out-of-bound `secs`. + pub fn west_opt(secs: i32) -> Option { + if -86_400 < secs && secs < 86_400 { + Some(FixedOffset { local_minus_utc: -secs }) + } else { + None + } + } + + /// Returns the number of seconds to add to convert from UTC to the local time. + #[inline] + 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. + #[inline] + pub fn utc_minus_local(&self) -> i32 { + -self.local_minus_utc + } +} + +impl TimeZone for FixedOffset { + type Offset = FixedOffset; + + fn from_offset(offset: &FixedOffset) -> FixedOffset { + *offset + } + + fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult { + LocalResult::Single(*self) + } + fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult { + LocalResult::Single(*self) + } + + fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { + *self + } + fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { + *self + } +} + +impl Offset for FixedOffset { + fn fix(&self) -> FixedOffset { + *self + } +} + +impl fmt::Debug for FixedOffset { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let offset = self.local_minus_utc; + let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) }; + let (mins, sec) = div_mod_floor(offset, 60); + let (hour, min) = div_mod_floor(mins, 60); + if sec == 0 { + write!(f, "{}{:02}:{:02}", sign, hour, min) + } else { + write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec) + } + } +} + +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 the same as +// 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(i64::from(rhs))).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 super::FixedOffset; + use offset::TimeZone; + + #[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 new file mode 100644 index 0000000..1abb3a9 --- /dev/null +++ b/src/offset/local.rs @@ -0,0 +1,227 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! The local (system) time zone. + +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] +use sys::{self, Timespec}; + +use super::fixed::FixedOffset; +use super::{LocalResult, TimeZone}; +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] +use naive::NaiveTime; +use naive::{NaiveDate, NaiveDateTime}; +use {Date, DateTime}; +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] +use {Datelike, Timelike}; + +/// Converts a `time::Tm` struct into the timezone-aware `DateTime`. +/// This assumes that `time` is working correctly, i.e. any error is fatal. +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] +fn tm_to_datetime(mut tm: sys::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: &sys::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: &sys::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) + } + + let date = tm_to_naive_date(&tm); + 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) - offset, offset) +} + +/// Converts a local `NaiveDateTime` to the `time::Timespec`. +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] +fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> sys::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 = sys::Tm { + tm_sec: d.second() as i32, + tm_min: d.minute() as i32, + tm_hour: d.hour() as i32, + tm_mday: d.day() as i32, + tm_mon: d.month0() as i32, // yes, C is that strange... + tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`. + tm_wday: 0, // to_local ignores this + tm_yday: 0, // and this + tm_isdst: -1, + tm_utcoff: tm_utcoff, + // do not set this, OS APIs are heavily inconsistent in terms of leap second handling + tm_nsec: 0, + }; + + tm.to_timespec() +} + +/// The local timescale. This is implemented via the standard `time` crate. +/// +/// Using the [`TimeZone`](./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, Debug)] +pub struct Local; + +impl Local { + /// Returns a `Date` which corresponds to the current date. + pub fn today() -> Date { + Local::now().date() + } + + /// Returns a `DateTime` which corresponds to the current date. + #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] + pub fn now() -> DateTime { + tm_to_datetime(Timespec::now().local()) + } + + /// Returns a `DateTime` which corresponds to the current date. + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] + pub fn now() -> DateTime { + use super::Utc; + let now: DateTime = super::Utc::now(); + + // Workaround missing timezone logic in `time` crate + let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60); + DateTime::from_utc(now.naive_utc(), offset) + } +} + +impl TimeZone for Local { + type Offset = FixedOffset; + + fn from_offset(_offset: &FixedOffset) -> Local { + Local + } + + // they are easier to define in terms of the finished date and time unlike other offsets + 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()) + } + + 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() + } + + // override them for avoiding redundant works + fn from_local_date(&self, local: &NaiveDate) -> LocalResult> { + // this sounds very strange, but required for keeping `TimeZone::ymd` sane. + // in the other words, we use the offset at the local midnight + // but keep the actual date unaltered (much like `FixedOffset`). + let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0)); + midnight.map(|datetime| Date::from_utc(*local, *datetime.offset())) + } + + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] + fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult> { + let mut local = local.clone(); + // Get the offset from the js runtime + let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60); + local -= ::Duration::seconds(offset.local_minus_utc() as i64); + LocalResult::Single(DateTime::from_utc(local, offset)) + } + + #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] + fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult> { + let timespec = datetime_to_timespec(local, true); + + // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them + let mut tm = timespec.local(); + 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()) + } + + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] + fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime { + // Get the offset from the js runtime + let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60); + DateTime::from_utc(*utc, offset) + } + + #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] + fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime { + let timespec = datetime_to_timespec(utc, false); + + // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them + let mut tm = timespec.local(); + assert_eq!(tm.tm_nsec, 0); + tm.tm_nsec = utc.nanosecond() as i32; + + tm_to_datetime(tm) + } +} + +#[cfg(test)] +mod tests { + use super::Local; + use offset::TimeZone; + use Datelike; + + #[test] + 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); + + 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 + ); + } +} diff --git a/src/offset/mod.rs b/src/offset/mod.rs new file mode 100644 index 0000000..0da6bfb --- /dev/null +++ b/src/offset/mod.rs @@ -0,0 +1,531 @@ +// 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. + +use core::fmt; + +use format::{parse, ParseResult, Parsed, StrftimeItems}; +use naive::{NaiveDate, NaiveDateTime, NaiveTime}; +use Weekday; +use {Date, DateTime}; + +/// The conversion result from the local time to the timezone-aware datetime types. +#[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)] +pub enum LocalResult { + /// Given local time representation is invalid. + /// This can occur when, for example, the positive timezone transition. + None, + /// Given local time representation has a single unique result. + Single(T), + /// Given local time representation has multiple results and thus ambiguous. + /// This can occur when, for example, the negative timezone transition. + Ambiguous(T /*min*/, T /*max*/), +} + +impl LocalResult { + /// Returns `Some` only when the conversion result is unique, or `None` otherwise. + pub fn single(self) -> Option { + match self { + LocalResult::Single(t) => Some(t), + _ => None, + } + } + + /// Returns `Some` for the earliest possible conversion result, or `None` if none. + pub fn earliest(self) -> Option { + match self { + LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t), + _ => None, + } + } + + /// Returns `Some` for the latest possible conversion result, or `None` if none. + pub fn latest(self) -> Option { + match self { + LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t), + _ => None, + } + } + + /// Maps a `LocalResult` into `LocalResult` with given function. + pub fn map U>(self, mut f: F) -> LocalResult { + match self { + LocalResult::None => LocalResult::None, + LocalResult::Single(v) => LocalResult::Single(f(v)), + LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)), + } + } +} + +impl LocalResult> { + /// Makes a new `DateTime` from the current date and given `NaiveTime`. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_time(self, time: NaiveTime) -> LocalResult> { + match self { + LocalResult::Single(d) => { + d.and_time(time).map_or(LocalResult::None, LocalResult::Single) + } + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute and second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult> { + match self { + LocalResult::Single(d) => { + d.and_hms_opt(hour, min, sec).map_or(LocalResult::None, LocalResult::Single) + } + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. + /// The millisecond part can exceed 1,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_milli_opt( + self, + hour: u32, + min: u32, + sec: u32, + milli: u32, + ) -> LocalResult> { + match self { + LocalResult::Single(d) => d + .and_hms_milli_opt(hour, min, sec, milli) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. + /// The microsecond part can exceed 1,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_micro_opt( + self, + hour: u32, + min: u32, + sec: u32, + micro: u32, + ) -> LocalResult> { + match self { + LocalResult::Single(d) => d + .and_hms_micro_opt(hour, min, sec, micro) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. + /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. + /// The offset in the current date is preserved. + /// + /// Propagates any error. Ambiguous result would be discarded. + #[inline] + pub fn and_hms_nano_opt( + self, + hour: u32, + min: u32, + sec: u32, + nano: u32, + ) -> LocalResult> { + match self { + LocalResult::Single(d) => d + .and_hms_nano_opt(hour, min, sec, nano) + .map_or(LocalResult::None, LocalResult::Single), + _ => LocalResult::None, + } + } +} + +impl LocalResult { + /// Returns the single unique conversion result, or panics accordingly. + pub fn unwrap(self) -> T { + match self { + LocalResult::None => panic!("No such local time"), + LocalResult::Single(t) => t, + LocalResult::Ambiguous(t1, t2) => { + panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2) + } + } + } +} + +/// The offset from the local time to UTC. +pub trait Offset: Sized + Clone + fmt::Debug { + /// Returns the fixed offset from UTC to the local time stored. + fn fix(&self) -> FixedOffset; +} + +/// The time zone. +/// +/// 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. + /// The original `TimeZone` value can be recovered via `TimeZone::from_offset`. + type Offset: Offset; + + /// Makes a new `Date` from year, month, day and the current time zone. + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// + /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), + /// 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() + } + + /// Makes a new `Date` from year, month, day and the current time zone. + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// + /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), + /// 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), + None => LocalResult::None, + } + } + + /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone. + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// + /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), + /// 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() + } + + /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone. + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// + /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), + /// but it will propagate to the `DateTime` values constructed via this date. + /// + /// Returns `None` on the out-of-range date and/or invalid DOY. + fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult> { + match NaiveDate::from_yo_opt(year, ordinal) { + Some(d) => self.from_local_date(&d), + None => LocalResult::None, + } + } + + /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and + /// the current time zone. + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// The resulting `Date` may have a different year from the input year. + /// + /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), + /// 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() + } + + /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and + /// the current time zone. + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// The resulting `Date` may have a different year from the input year. + /// + /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), + /// but it will propagate to the `DateTime` values constructed via this date. + /// + /// Returns `None` on the out-of-range date and/or invalid week number. + fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult> { + match NaiveDate::from_isoywd_opt(year, week, weekday) { + Some(d) => self.from_local_date(&d), + None => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the number of non-leap seconds + /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") + /// 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, + /// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt). + /// + /// # 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() + } + + /// Makes a new `DateTime` from the number of non-leap seconds + /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") + /// and the number of nanoseconds since the last whole non-leap second. + /// + /// Returns `LocalResult::None` on out-of-range number of seconds and/or + /// invalid nanosecond, otherwise always returns `LocalResult::Single`. + fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult> { + match NaiveDateTime::from_timestamp_opt(secs, nsecs) { + Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)), + None => LocalResult::None, + } + } + + /// Makes a new `DateTime` from the number of non-leap milliseconds + /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). + /// + /// Panics on out-of-range number of milliseconds for a non-panicking + /// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt). + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{Utc, TimeZone}; + /// + /// assert_eq!(Utc.timestamp_millis(1431648000).timestamp(), 1431648); + /// ~~~~ + fn timestamp_millis(&self, millis: i64) -> DateTime { + self.timestamp_millis_opt(millis).unwrap() + } + + /// Makes a new `DateTime` from the number of non-leap milliseconds + /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). + /// + /// + /// Returns `LocalResult::None` on out-of-range number of milliseconds + /// and/or invalid nanosecond, otherwise always returns + /// `LocalResult::Single`. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{Utc, TimeZone, LocalResult}; + /// match Utc.timestamp_millis_opt(1431648000) { + /// LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648), + /// _ => panic!("Incorrect timestamp_millis"), + /// }; + /// ~~~~ + fn timestamp_millis_opt(&self, millis: i64) -> LocalResult> { + let (mut secs, mut millis) = (millis / 1000, millis % 1000); + if millis < 0 { + secs -= 1; + millis += 1000; + } + self.timestamp_opt(secs, millis as u32 * 1_000_000) + } + + /// Makes a new `DateTime` from the number of non-leap nanoseconds + /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). + /// + /// Unlike [`timestamp_millis`](#method.timestamp_millis), this never + /// panics. + /// + /// # Example + /// + /// ~~~~ + /// use chrono::{Utc, TimeZone}; + /// + /// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648); + /// ~~~~ + fn timestamp_nanos(&self, nanos: i64) -> DateTime { + let (mut secs, mut nanos) = (nanos / 1_000_000_000, nanos % 1_000_000_000); + if nanos < 0 { + secs -= 1; + nanos += 1_000_000_000; + } + self.timestamp_opt(secs, nanos as u32).unwrap() + } + + /// 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) + /// on the supported escape sequences. + /// + /// If the format does not include offsets, the current offset is assumed; + /// otherwise the input should have a matching UTC offset. + /// + /// See also `DateTime::parse_from_str` which gives a local `DateTime` + /// with parsed `FixedOffset`. + fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult> { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_datetime_with_timezone(self) + } + + /// Reconstructs the time zone from the offset. + fn from_offset(offset: &Self::Offset) -> Self; + + /// Creates the offset(s) for given local `NaiveDate` if possible. + fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult; + + /// Creates the offset(s) for given local `NaiveDateTime` if possible. + fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult; + + /// 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| { + // 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| DateTime::from_utc(*local - offset.fix(), offset)) + } + + /// Creates the offset for given UTC `NaiveDate`. This cannot fail. + fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset; + + /// Creates the offset for given UTC `NaiveDateTime`. This cannot fail. + fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset; + + /// Converts the UTC `NaiveDate` to the local time. + /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). + fn from_utc_date(&self, utc: &NaiveDate) -> Date { + Date::from_utc(*utc, self.offset_from_utc_date(utc)) + } + + /// Converts the UTC `NaiveDateTime` to the local time. + /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). + fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime { + DateTime::from_utc(*utc, self.offset_from_utc_datetime(utc)) + } +} + +mod fixed; +#[cfg(feature = "clock")] +mod local; +mod utc; + +pub use self::fixed::FixedOffset; +#[cfg(feature = "clock")] +pub use self::local::Local; +pub use self::utc::Utc; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_negative_millis() { + let dt = Utc.timestamp_millis(-1000); + assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC"); + let dt = Utc.timestamp_millis(-7000); + assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC"); + let dt = Utc.timestamp_millis(-7001); + assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC"); + let dt = Utc.timestamp_millis(-7003); + assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC"); + let dt = Utc.timestamp_millis(-999); + assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC"); + let dt = Utc.timestamp_millis(-1); + assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC"); + let dt = Utc.timestamp_millis(-60000); + assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC"); + let dt = Utc.timestamp_millis(-3600000); + assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC"); + + for (millis, expected) in &[ + (-7000, "1969-12-31 23:59:53 UTC"), + (-7001, "1969-12-31 23:59:52.999 UTC"), + (-7003, "1969-12-31 23:59:52.997 UTC"), + ] { + match Utc.timestamp_millis_opt(*millis) { + LocalResult::Single(dt) => { + assert_eq!(dt.to_string(), *expected); + } + e => panic!("Got {:?} instead of an okay answer", e), + } + } + } + + #[test] + fn test_negative_nanos() { + let dt = Utc.timestamp_nanos(-1_000_000_000); + assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC"); + let dt = Utc.timestamp_nanos(-999_999_999); + assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000000001 UTC"); + let dt = Utc.timestamp_nanos(-1); + assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999999 UTC"); + let dt = Utc.timestamp_nanos(-60_000_000_000); + assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC"); + let dt = Utc.timestamp_nanos(-3_600_000_000_000); + assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC"); + } + + #[test] + fn test_nanos_never_panics() { + Utc.timestamp_nanos(i64::max_value()); + Utc.timestamp_nanos(i64::default()); + Utc.timestamp_nanos(i64::min_value()); + } +} diff --git a/src/offset/utc.rs b/src/offset/utc.rs new file mode 100644 index 0000000..aec6667 --- /dev/null +++ b/src/offset/utc.rs @@ -0,0 +1,100 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! The UTC (Coordinated Universal Time) time zone. + +use core::fmt; + +use super::{FixedOffset, LocalResult, Offset, TimeZone}; +use naive::{NaiveDate, NaiveDateTime}; +#[cfg(all( + feature = "clock", + not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")) +))] +use std::time::{SystemTime, UNIX_EPOCH}; +#[cfg(feature = "clock")] +use {Date, DateTime}; + +/// 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`](./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)] +pub struct Utc; + +#[cfg(feature = "clock")] +impl Utc { + /// Returns a `Date` which corresponds to the current date. + pub fn today() -> Date { + Utc::now().date() + } + + /// Returns a `DateTime` which corresponds to the current date. + #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] + pub fn now() -> DateTime { + let now = + SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); + let naive = NaiveDateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos() as u32); + DateTime::from_utc(naive, Utc) + } + + /// Returns a `DateTime` which corresponds to the current date. + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] + pub fn now() -> DateTime { + let now = js_sys::Date::new_0(); + DateTime::::from(now) + } +} + +impl TimeZone for Utc { + type Offset = Utc; + + fn from_offset(_state: &Utc) -> Utc { + 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_utc_date(&self, _utc: &NaiveDate) -> Utc { + Utc + } + fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc { + Utc + } +} + +impl Offset for Utc { + fn fix(&self) -> FixedOffset { + FixedOffset::east(0) + } +} + +impl fmt::Debug for Utc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Z") + } +} + +impl fmt::Display for Utc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UTC") + } +} diff --git a/src/oldtime.rs b/src/oldtime.rs new file mode 100644 index 0000000..8656769 --- /dev/null +++ b/src/oldtime.rs @@ -0,0 +1,684 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Temporal quantification + +use core::ops::{Add, Div, Mul, Neg, Sub}; +use core::time::Duration as StdDuration; +use core::{fmt, i64}; +#[cfg(any(feature = "std", test))] +use std::error::Error; + +/// The number of nanoseconds in a microsecond. +const NANOS_PER_MICRO: i32 = 1000; +/// The number of nanoseconds in a millisecond. +const NANOS_PER_MILLI: i32 = 1000_000; +/// The number of nanoseconds in seconds. +const NANOS_PER_SEC: i32 = 1_000_000_000; +/// The number of microseconds per second. +const MICROS_PER_SEC: i64 = 1000_000; +/// The number of milliseconds per second. +const MILLIS_PER_SEC: i64 = 1000; +/// The number of seconds in a minute. +const SECS_PER_MINUTE: i64 = 60; +/// The number of seconds in an hour. +const SECS_PER_HOUR: i64 = 3600; +/// The number of (non-leap) seconds in days. +const SECS_PER_DAY: i64 = 86400; +/// The number of (non-leap) seconds in a week. +const SECS_PER_WEEK: i64 = 604800; + +macro_rules! try_opt { + ($e:expr) => { + match $e { + Some(v) => v, + None => return None, + } + }; +} + +/// ISO 8601 time duration with nanosecond precision. +/// This also allows for the negative duration; see individual methods for details. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Duration { + secs: i64, + nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC +} + +/// The minimum possible `Duration`: `i64::MIN` milliseconds. +pub const MIN: Duration = Duration { + secs: i64::MIN / MILLIS_PER_SEC - 1, + nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI, +}; + +/// The maximum possible `Duration`: `i64::MAX` milliseconds. +pub const MAX: Duration = Duration { + secs: i64::MAX / MILLIS_PER_SEC, + nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI, +}; + +impl Duration { + /// Makes a new `Duration` with given number of weeks. + /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn weeks(weeks: i64) -> Duration { + let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of days. + /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn days(days: i64) -> Duration { + let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of hours. + /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn hours(hours: i64) -> Duration { + let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of minutes. + /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn minutes(minutes: i64) -> Duration { + let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of seconds. + /// Panics when the duration is more than `i64::MAX` seconds + /// or less than `i64::MIN` seconds. + #[inline] + pub fn seconds(seconds: i64) -> Duration { + let d = Duration { secs: seconds, nanos: 0 }; + if d < MIN || d > MAX { + panic!("Duration::seconds out of bounds"); + } + d + } + + /// Makes a new `Duration` with given number of milliseconds. + #[inline] + pub fn milliseconds(milliseconds: i64) -> Duration { + let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); + let nanos = millis as i32 * NANOS_PER_MILLI; + Duration { secs: secs, nanos: nanos } + } + + /// Makes a new `Duration` with given number of microseconds. + #[inline] + pub fn microseconds(microseconds: i64) -> Duration { + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + let nanos = micros as i32 * NANOS_PER_MICRO; + Duration { secs: secs, nanos: nanos } + } + + /// Makes a new `Duration` with given number of nanoseconds. + #[inline] + pub fn nanoseconds(nanos: i64) -> Duration { + let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); + Duration { secs: secs, nanos: nanos as i32 } + } + + /// Returns the total number of whole weeks in the duration. + #[inline] + pub fn num_weeks(&self) -> i64 { + self.num_days() / 7 + } + + /// Returns the total number of whole days in the duration. + pub fn num_days(&self) -> i64 { + self.num_seconds() / SECS_PER_DAY + } + + /// Returns the total number of whole hours in the duration. + #[inline] + pub fn num_hours(&self) -> i64 { + self.num_seconds() / SECS_PER_HOUR + } + + /// Returns the total number of whole minutes in the duration. + #[inline] + pub fn num_minutes(&self) -> i64 { + self.num_seconds() / SECS_PER_MINUTE + } + + /// Returns the total number of whole seconds in the duration. + pub fn num_seconds(&self) -> i64 { + // If secs is negative, nanos should be subtracted from the duration. + if self.secs < 0 && self.nanos > 0 { + self.secs + 1 + } else { + self.secs + } + } + + /// Returns the number of nanoseconds such that + /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of + /// nanoseconds in the duration. + fn nanos_mod_sec(&self) -> i32 { + if self.secs < 0 && self.nanos > 0 { + self.nanos - NANOS_PER_SEC + } else { + self.nanos + } + } + + /// Returns the total number of whole milliseconds in the duration, + pub fn num_milliseconds(&self) -> i64 { + // A proper Duration will not overflow, because MIN and MAX are defined + // such that the range is exactly i64 milliseconds. + let secs_part = self.num_seconds() * MILLIS_PER_SEC; + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; + secs_part + nanos_part as i64 + } + + /// Returns the total number of whole microseconds in the duration, + /// or `None` on overflow (exceeding 2^63 microseconds in either direction). + pub fn num_microseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; + secs_part.checked_add(nanos_part as i64) + } + + /// Returns the total number of whole nanoseconds in the duration, + /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). + pub fn num_nanoseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); + let nanos_part = self.nanos_mod_sec(); + secs_part.checked_add(nanos_part as i64) + } + + /// Add two durations, returning `None` if overflow occurred. + pub fn checked_add(&self, rhs: &Duration) -> Option { + let mut secs = try_opt!(self.secs.checked_add(rhs.secs)); + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs = try_opt!(secs.checked_add(1)); + } + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { + None + } else { + Some(d) + } + } + + /// Subtract two durations, returning `None` if overflow occurred. + pub fn checked_sub(&self, rhs: &Duration) -> Option { + let mut secs = try_opt!(self.secs.checked_sub(rhs.secs)); + let mut nanos = self.nanos - rhs.nanos; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs = try_opt!(secs.checked_sub(1)); + } + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { + None + } else { + Some(d) + } + } + + /// Returns the duration as an absolute (non-negative) value. + #[inline] + pub fn abs(&self) -> Duration { + Duration { secs: self.secs.abs(), nanos: self.nanos } + } + + /// The minimum possible `Duration`: `i64::MIN` milliseconds. + #[inline] + pub fn min_value() -> Duration { + MIN + } + + /// The maximum possible `Duration`: `i64::MAX` milliseconds. + #[inline] + pub fn max_value() -> Duration { + MAX + } + + /// A duration where the stored seconds and nanoseconds are equal to zero. + #[inline] + pub fn zero() -> Duration { + Duration { secs: 0, nanos: 0 } + } + + /// Returns `true` if the duration equals `Duration::zero()`. + #[inline] + pub fn is_zero(&self) -> bool { + self.secs == 0 && self.nanos == 0 + } + + /// Creates a `time::Duration` object from `std::time::Duration` + /// + /// This function errors when original duration is larger than the maximum + /// value supported for this type. + pub fn from_std(duration: StdDuration) -> Result { + // We need to check secs as u64 before coercing to i64 + if duration.as_secs() > MAX.secs as u64 { + return Err(OutOfRangeError(())); + } + let d = Duration { secs: duration.as_secs() as i64, nanos: duration.subsec_nanos() as i32 }; + if d > MAX { + return Err(OutOfRangeError(())); + } + Ok(d) + } + + /// Creates a `std::time::Duration` object from `time::Duration` + /// + /// This function errors when duration is less than zero. As standard + /// library implementation is limited to non-negative values. + pub fn to_std(&self) -> Result { + if self.secs < 0 { + return Err(OutOfRangeError(())); + } + Ok(StdDuration::new(self.secs as u64, self.nanos as u32)) + } +} + +impl Neg for Duration { + type Output = Duration; + + #[inline] + fn neg(self) -> Duration { + if self.nanos == 0 { + Duration { secs: -self.secs, nanos: 0 } + } else { + Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } + } + } +} + +impl Add for Duration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Duration { + let mut secs = self.secs + rhs.secs; + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl Sub for Duration { + type Output = Duration; + + fn sub(self, rhs: Duration) -> Duration { + let mut secs = self.secs - rhs.secs; + let mut nanos = self.nanos - rhs.nanos; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl Mul for Duration { + type Output = Duration; + + fn mul(self, rhs: i32) -> Duration { + // Multiply nanoseconds as i64, because it cannot overflow that way. + let total_nanos = self.nanos as i64 * rhs as i64; + let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); + let secs = self.secs * rhs as i64 + extra_secs; + Duration { secs: secs, nanos: nanos as i32 } + } +} + +impl Div for Duration { + type Output = Duration; + + fn div(self, rhs: i32) -> Duration { + let mut secs = self.secs / rhs as i64; + let carry = self.secs - secs * rhs as i64; + let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; + let mut nanos = self.nanos / rhs + extra_nanos as i32; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // technically speaking, negative duration is not valid ISO 8601, + // but we need to print it anyway. + let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; + + let days = abs.secs / SECS_PER_DAY; + let secs = abs.secs - days * SECS_PER_DAY; + let hasdate = days != 0; + let hastime = (secs != 0 || abs.nanos != 0) || !hasdate; + + write!(f, "{}P", sign)?; + + if hasdate { + write!(f, "{}D", days)?; + } + if hastime { + if abs.nanos == 0 { + write!(f, "T{}S", secs)?; + } else if abs.nanos % NANOS_PER_MILLI == 0 { + write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?; + } else if abs.nanos % NANOS_PER_MICRO == 0 { + write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?; + } else { + write!(f, "T{}.{:09}S", secs, abs.nanos)?; + } + } + Ok(()) + } +} + +/// Represents error when converting `Duration` to/from a standard library +/// implementation +/// +/// The `std::time::Duration` supports a range from zero to `u64::MAX` +/// *seconds*, while this module supports signed range of up to +/// `i64::MAX` of *milliseconds*. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OutOfRangeError(()); + +impl fmt::Display for OutOfRangeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Source duration value is out of range for the target type") + } +} + +#[cfg(any(feature = "std", test))] +impl Error for OutOfRangeError { + #[allow(deprecated)] + fn description(&self) -> &str { + "out of range error" + } +} + +// Copied from libnum +#[inline] +fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { + (div_floor_64(this, other), mod_floor_64(this, other)) +} + +#[inline] +fn div_floor_64(this: i64, other: i64) -> i64 { + match div_rem_64(this, other) { + (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1, + (d, _) => d, + } +} + +#[inline] +fn mod_floor_64(this: i64, other: i64) -> i64 { + match this % other { + r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other, + r => r, + } +} + +#[inline] +fn div_rem_64(this: i64, other: i64) -> (i64, i64) { + (this / other, this % other) +} + +#[cfg(test)] +mod tests { + use super::{Duration, OutOfRangeError, MAX, MIN}; + use std::time::Duration as StdDuration; + use std::{i32, i64}; + + #[test] + fn test_duration() { + assert!(Duration::seconds(1) != Duration::zero()); + assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3)); + assert_eq!( + Duration::seconds(86399) + Duration::seconds(4), + Duration::days(1) + Duration::seconds(3) + ); + assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000)); + assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000)); + assert_eq!( + Duration::days(2) + Duration::seconds(86399) + Duration::nanoseconds(1234567890), + Duration::days(3) + Duration::nanoseconds(234567890) + ); + assert_eq!(-Duration::days(3), Duration::days(-3)); + assert_eq!( + -(Duration::days(3) + Duration::seconds(70)), + Duration::days(-4) + Duration::seconds(86400 - 70) + ); + } + + #[test] + fn test_duration_num_days() { + assert_eq!(Duration::zero().num_days(), 0); + assert_eq!(Duration::days(1).num_days(), 1); + assert_eq!(Duration::days(-1).num_days(), -1); + assert_eq!(Duration::seconds(86399).num_days(), 0); + assert_eq!(Duration::seconds(86401).num_days(), 1); + assert_eq!(Duration::seconds(-86399).num_days(), 0); + assert_eq!(Duration::seconds(-86401).num_days(), -1); + assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64); + assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64); + } + + #[test] + fn test_duration_num_seconds() { + assert_eq!(Duration::zero().num_seconds(), 0); + assert_eq!(Duration::seconds(1).num_seconds(), 1); + assert_eq!(Duration::seconds(-1).num_seconds(), -1); + assert_eq!(Duration::milliseconds(999).num_seconds(), 0); + assert_eq!(Duration::milliseconds(1001).num_seconds(), 1); + assert_eq!(Duration::milliseconds(-999).num_seconds(), 0); + assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1); + } + + #[test] + fn test_duration_num_milliseconds() { + assert_eq!(Duration::zero().num_milliseconds(), 0); + assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1); + assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1); + assert_eq!(Duration::microseconds(999).num_milliseconds(), 0); + assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1); + assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0); + assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1); + assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX); + assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN); + assert_eq!(MAX.num_milliseconds(), i64::MAX); + assert_eq!(MIN.num_milliseconds(), i64::MIN); + } + + #[test] + fn test_duration_num_microseconds() { + assert_eq!(Duration::zero().num_microseconds(), Some(0)); + assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1)); + assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1)); + assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0)); + assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1)); + assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0)); + assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1)); + assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX)); + assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_microseconds(), None); + assert_eq!(MIN.num_microseconds(), None); + + // overflow checks + const MICROS_PER_DAY: i64 = 86400_000_000; + assert_eq!( + Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(), + Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY) + ); + assert_eq!( + Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(), + Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY) + ); + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None); + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None); + } + + #[test] + fn test_duration_num_nanoseconds() { + assert_eq!(Duration::zero().num_nanoseconds(), Some(0)); + assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1)); + assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1)); + assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX)); + assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_nanoseconds(), None); + assert_eq!(MIN.num_nanoseconds(), None); + + // overflow checks + const NANOS_PER_DAY: i64 = 86400_000_000_000; + assert_eq!( + Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(), + Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY) + ); + assert_eq!( + Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(), + Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY) + ); + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None); + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None); + } + + #[test] + fn test_duration_checked_ops() { + assert_eq!( + Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), + Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999)) + ); + assert!(Duration::milliseconds(i64::MAX) + .checked_add(&Duration::microseconds(1000)) + .is_none()); + + assert_eq!( + Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), + Some(Duration::milliseconds(i64::MIN)) + ); + assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)).is_none()); + } + + #[test] + fn test_duration_mul() { + assert_eq!(Duration::zero() * i32::MAX, Duration::zero()); + assert_eq!(Duration::zero() * i32::MIN, Duration::zero()); + assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero()); + assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1)); + assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1)); + assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1)); + assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1)); + assert_eq!( + Duration::nanoseconds(30) * 333_333_333, + Duration::seconds(10) - Duration::nanoseconds(10) + ); + assert_eq!( + (Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3, + Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3) + ); + assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3)); + assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3)); + } + + #[test] + fn test_duration_div() { + assert_eq!(Duration::zero() / i32::MAX, Duration::zero()); + assert_eq!(Duration::zero() / i32::MIN, Duration::zero()); + assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); + assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); + assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333)); + assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333)); + assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500)); + assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333)); + assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333)); + } + + #[test] + fn test_duration_fmt() { + assert_eq!(Duration::zero().to_string(), "PT0S"); + assert_eq!(Duration::days(42).to_string(), "P42D"); + assert_eq!(Duration::days(-42).to_string(), "-P42D"); + assert_eq!(Duration::seconds(42).to_string(), "PT42S"); + assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S"); + assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S"); + assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S"); + assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), "P7DT6.543S"); + assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S"); + assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S"); + + // the format specifier should have no effect on `Duration` + assert_eq!( + format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), + "P1DT2.345S" + ); + } + + #[test] + fn test_to_std() { + assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0))); + assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0))); + assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000))); + assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000))); + assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777))); + assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000))); + assert_eq!(Duration::seconds(-1).to_std(), Err(OutOfRangeError(()))); + assert_eq!(Duration::milliseconds(-1).to_std(), Err(OutOfRangeError(()))); + } + + #[test] + fn test_from_std() { + assert_eq!(Ok(Duration::seconds(1)), Duration::from_std(StdDuration::new(1, 0))); + assert_eq!(Ok(Duration::seconds(86401)), Duration::from_std(StdDuration::new(86401, 0))); + assert_eq!( + Ok(Duration::milliseconds(123)), + Duration::from_std(StdDuration::new(0, 123000000)) + ); + assert_eq!( + Ok(Duration::milliseconds(123765)), + Duration::from_std(StdDuration::new(123, 765000000)) + ); + assert_eq!(Ok(Duration::nanoseconds(777)), Duration::from_std(StdDuration::new(0, 777))); + assert_eq!(Ok(MAX), Duration::from_std(StdDuration::new(9223372036854775, 807000000))); + assert_eq!( + Duration::from_std(StdDuration::new(9223372036854776, 0)), + Err(OutOfRangeError(())) + ); + assert_eq!( + Duration::from_std(StdDuration::new(9223372036854775, 807000001)), + Err(OutOfRangeError(())) + ); + } +} diff --git a/src/round.rs b/src/round.rs new file mode 100644 index 0000000..92d7c3a --- /dev/null +++ b/src/round.rs @@ -0,0 +1,456 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +use core::cmp::Ordering; +use core::fmt; +use core::marker::Sized; +use core::ops::{Add, Sub}; +use datetime::DateTime; +use oldtime::Duration; +#[cfg(any(feature = "std", test))] +use std; +use TimeZone; +use Timelike; + +/// Extension trait for subsecond rounding or truncation to a maximum number +/// of digits. Rounding can be used to decrease the error variance when +/// serializing/persisting to lower precision. Truncation is the default +/// behavior in Chrono display formatting. Either can be used to guarantee +/// equality (e.g. for testing) when round-tripping through a lower precision +/// format. +pub trait SubsecRound { + /// Return a copy rounded to the specified number of subsecond digits. With + /// 9 or more digits, self is returned unmodified. Halfway values are + /// rounded up (away from zero). + /// + /// # Example + /// ``` rust + /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc}; + /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000); + /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000); + /// ``` + fn round_subsecs(self, digits: u16) -> Self; + + /// Return a copy truncated to the specified number of subsecond + /// digits. With 9 or more digits, self is returned unmodified. + /// + /// # Example + /// ``` rust + /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc}; + /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000); + /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000); + /// ``` + fn trunc_subsecs(self, digits: u16) -> Self; +} + +impl SubsecRound for T +where + T: Timelike + Add + Sub, +{ + fn round_subsecs(self, digits: u16) -> T { + let span = span_for_digits(digits); + let delta_down = self.nanosecond() % span; + if delta_down > 0 { + let delta_up = span - delta_down; + if delta_up <= delta_down { + self + Duration::nanoseconds(delta_up.into()) + } else { + self - Duration::nanoseconds(delta_down.into()) + } + } else { + self // unchanged + } + } + + fn trunc_subsecs(self, digits: u16) -> T { + let span = span_for_digits(digits); + let delta_down = self.nanosecond() % span; + if delta_down > 0 { + self - Duration::nanoseconds(delta_down.into()) + } else { + self // unchanged + } + } +} + +// Return the maximum span in nanoseconds for the target number of digits. +fn span_for_digits(digits: u16) -> u32 { + // fast lookup form of: 10^(9-min(9,digits)) + match digits { + 0 => 1_000_000_000, + 1 => 100_000_000, + 2 => 10_000_000, + 3 => 1_000_000, + 4 => 100_000, + 5 => 10_000, + 6 => 1_000, + 7 => 100, + 8 => 10, + _ => 1, + } +} + +/// Extension trait for rounding or truncating a DateTime by a Duration. +/// +/// # Limitations +/// Both rounding and truncating are done via [`Duration::num_nanoseconds`] and +/// [`DateTime::timestamp_nanos`]. This means that they will fail if either the +/// `Duration` or the `DateTime` are too big to represented as nanoseconds. They +/// will also fail if the `Duration` is bigger than the timestamp. +pub trait DurationRound: Sized { + /// Error that can occur in rounding or truncating + #[cfg(any(feature = "std", test))] + type Err: std::error::Error; + + /// Error that can occur in rounding or truncating + #[cfg(not(any(feature = "std", test)))] + type Err: fmt::Debug + fmt::Display; + + /// Return a copy rounded by Duration. + /// + /// # Example + /// ``` rust + /// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc}; + /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// assert_eq!( + /// dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(), + /// "2018-01-11 12:00:00.150 UTC" + /// ); + /// assert_eq!( + /// dt.duration_round(Duration::days(1)).unwrap().to_string(), + /// "2018-01-12 00:00:00 UTC" + /// ); + /// ``` + fn duration_round(self, duration: Duration) -> Result; + + /// Return a copy truncated by Duration. + /// + /// # Example + /// ``` rust + /// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc}; + /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// assert_eq!( + /// dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(), + /// "2018-01-11 12:00:00.150 UTC" + /// ); + /// assert_eq!( + /// dt.duration_trunc(Duration::days(1)).unwrap().to_string(), + /// "2018-01-11 00:00:00 UTC" + /// ); + /// ``` + fn duration_trunc(self, duration: Duration) -> Result; +} + +/// The maximum number of seconds a DateTime can be to be represented as nanoseconds +const MAX_SECONDS_TIMESTAMP_FOR_NANOS: i64 = 9_223_372_036; + +impl DurationRound for DateTime { + type Err = RoundingError; + + fn duration_round(self, duration: Duration) -> Result { + if let Some(span) = duration.num_nanoseconds() { + if self.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { + return Err(RoundingError::TimestampExceedsLimit); + } + let stamp = self.timestamp_nanos(); + if span > stamp.abs() { + return Err(RoundingError::DurationExceedsTimestamp); + } + let delta_down = stamp % span; + if delta_down == 0 { + Ok(self) + } else { + let (delta_up, delta_down) = if delta_down < 0 { + (delta_down.abs(), span - delta_down.abs()) + } else { + (span - delta_down, delta_down) + }; + if delta_up <= delta_down { + Ok(self + Duration::nanoseconds(delta_up)) + } else { + Ok(self - Duration::nanoseconds(delta_down)) + } + } + } else { + Err(RoundingError::DurationExceedsLimit) + } + } + + fn duration_trunc(self, duration: Duration) -> Result { + if let Some(span) = duration.num_nanoseconds() { + if self.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { + return Err(RoundingError::TimestampExceedsLimit); + } + let stamp = self.timestamp_nanos(); + if span > stamp.abs() { + return Err(RoundingError::DurationExceedsTimestamp); + } + let delta_down = stamp % span; + match delta_down.cmp(&0) { + Ordering::Equal => Ok(self), + Ordering::Greater => Ok(self - Duration::nanoseconds(delta_down)), + Ordering::Less => Ok(self - Duration::nanoseconds(span - delta_down.abs())), + } + } else { + Err(RoundingError::DurationExceedsLimit) + } + } +} + +/// An error from rounding by `Duration` +/// +/// See: [`DurationRound`] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +pub enum RoundingError { + /// Error when the Duration exceeds the Duration from or until the Unix epoch. + /// + /// ``` rust + /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc}; + /// let dt = Utc.ymd(1970, 12, 12).and_hms(0, 0, 0); + /// + /// assert_eq!( + /// dt.duration_round(Duration::days(365)), + /// Err(RoundingError::DurationExceedsTimestamp), + /// ); + /// ``` + DurationExceedsTimestamp, + + /// Error when `Duration.num_nanoseconds` exceeds the limit. + /// + /// ``` rust + /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc}; + /// let dt = Utc.ymd(2260, 12, 31).and_hms_nano(23, 59, 59, 1_75_500_000); + /// + /// assert_eq!( + /// dt.duration_round(Duration::days(300 * 365)), + /// Err(RoundingError::DurationExceedsLimit) + /// ); + /// ``` + DurationExceedsLimit, + + /// Error when `DateTime.timestamp_nanos` exceeds the limit. + /// + /// ``` rust + /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc}; + /// let dt = Utc.ymd(2300, 12, 12).and_hms(0, 0, 0); + /// + /// assert_eq!(dt.duration_round(Duration::days(1)), Err(RoundingError::TimestampExceedsLimit),); + /// ``` + TimestampExceedsLimit, +} + +impl fmt::Display for RoundingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + RoundingError::DurationExceedsTimestamp => { + write!(f, "duration in nanoseconds exceeds timestamp") + } + RoundingError::DurationExceedsLimit => { + write!(f, "duration exceeds num_nanoseconds limit") + } + RoundingError::TimestampExceedsLimit => { + write!(f, "timestamp exceeds num_nanoseconds limit") + } + } + } +} + +#[cfg(any(feature = "std", test))] +impl std::error::Error for RoundingError { + #[allow(deprecated)] + fn description(&self) -> &str { + "error from rounding or truncating with DurationRound" + } +} + +#[cfg(test)] +mod tests { + use super::{Duration, DurationRound, SubsecRound}; + use offset::{FixedOffset, TimeZone, Utc}; + use Timelike; + + #[test] + fn test_round_subsecs() { + let pst = FixedOffset::east(8 * 60 * 60); + let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684); + + assert_eq!(dt.round_subsecs(10), dt); + assert_eq!(dt.round_subsecs(9), dt); + assert_eq!(dt.round_subsecs(8).nanosecond(), 084_660_680); + assert_eq!(dt.round_subsecs(7).nanosecond(), 084_660_700); + assert_eq!(dt.round_subsecs(6).nanosecond(), 084_661_000); + assert_eq!(dt.round_subsecs(5).nanosecond(), 084_660_000); + assert_eq!(dt.round_subsecs(4).nanosecond(), 084_700_000); + assert_eq!(dt.round_subsecs(3).nanosecond(), 085_000_000); + assert_eq!(dt.round_subsecs(2).nanosecond(), 080_000_000); + assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000); + + assert_eq!(dt.round_subsecs(0).nanosecond(), 0); + assert_eq!(dt.round_subsecs(0).second(), 13); + + let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000); + assert_eq!(dt.round_subsecs(9), dt); + assert_eq!(dt.round_subsecs(4), dt); + assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000); + assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000); + assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000); + + assert_eq!(dt.round_subsecs(0).nanosecond(), 0); + assert_eq!(dt.round_subsecs(0).second(), 28); + } + + #[test] + fn test_round_leap_nanos() { + let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000); + assert_eq!(dt.round_subsecs(9), dt); + assert_eq!(dt.round_subsecs(4), dt); + assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000); + assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000); + assert_eq!(dt.round_subsecs(1).second(), 59); + + assert_eq!(dt.round_subsecs(0).nanosecond(), 0); + assert_eq!(dt.round_subsecs(0).second(), 0); + } + + #[test] + fn test_trunc_subsecs() { + let pst = FixedOffset::east(8 * 60 * 60); + let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684); + + assert_eq!(dt.trunc_subsecs(10), dt); + assert_eq!(dt.trunc_subsecs(9), dt); + assert_eq!(dt.trunc_subsecs(8).nanosecond(), 084_660_680); + assert_eq!(dt.trunc_subsecs(7).nanosecond(), 084_660_600); + assert_eq!(dt.trunc_subsecs(6).nanosecond(), 084_660_000); + assert_eq!(dt.trunc_subsecs(5).nanosecond(), 084_660_000); + assert_eq!(dt.trunc_subsecs(4).nanosecond(), 084_600_000); + assert_eq!(dt.trunc_subsecs(3).nanosecond(), 084_000_000); + assert_eq!(dt.trunc_subsecs(2).nanosecond(), 080_000_000); + assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0); + + assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); + assert_eq!(dt.trunc_subsecs(0).second(), 13); + + let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000); + assert_eq!(dt.trunc_subsecs(9), dt); + assert_eq!(dt.trunc_subsecs(4), dt); + assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000); + assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000); + assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000); + + assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); + assert_eq!(dt.trunc_subsecs(0).second(), 27); + } + + #[test] + fn test_trunc_leap_nanos() { + let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000); + assert_eq!(dt.trunc_subsecs(9), dt); + assert_eq!(dt.trunc_subsecs(4), dt); + assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000); + assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000); + assert_eq!(dt.trunc_subsecs(1).second(), 59); + + assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000); + assert_eq!(dt.trunc_subsecs(0).second(), 59); + } + + #[test] + fn test_duration_round() { + let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 175_500_000); + + assert_eq!( + dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(), + "2016-12-31 23:59:59.180 UTC" + ); + + // round up + let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 30, 0); + assert_eq!( + dt.duration_round(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:25:00 UTC" + ); + // round down + let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 29, 999); + assert_eq!( + dt.duration_round(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:20:00 UTC" + ); + + assert_eq!( + dt.duration_round(Duration::minutes(10)).unwrap().to_string(), + "2012-12-12 18:20:00 UTC" + ); + assert_eq!( + dt.duration_round(Duration::minutes(30)).unwrap().to_string(), + "2012-12-12 18:30:00 UTC" + ); + assert_eq!( + dt.duration_round(Duration::hours(1)).unwrap().to_string(), + "2012-12-12 18:00:00 UTC" + ); + assert_eq!( + dt.duration_round(Duration::days(1)).unwrap().to_string(), + "2012-12-13 00:00:00 UTC" + ); + } + + #[test] + fn test_duration_round_pre_epoch() { + let dt = Utc.ymd(1969, 12, 12).and_hms(12, 12, 12); + assert_eq!( + dt.duration_round(Duration::minutes(10)).unwrap().to_string(), + "1969-12-12 12:10:00 UTC" + ); + } + + #[test] + fn test_duration_trunc() { + let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_75_500_000); + + assert_eq!( + dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(), + "2016-12-31 23:59:59.170 UTC" + ); + + // would round up + let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 30, 0); + assert_eq!( + dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:20:00 UTC" + ); + // would round down + let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 29, 999); + assert_eq!( + dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:20:00 UTC" + ); + assert_eq!( + dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(), + "2012-12-12 18:20:00 UTC" + ); + assert_eq!( + dt.duration_trunc(Duration::minutes(30)).unwrap().to_string(), + "2012-12-12 18:00:00 UTC" + ); + assert_eq!( + dt.duration_trunc(Duration::hours(1)).unwrap().to_string(), + "2012-12-12 18:00:00 UTC" + ); + assert_eq!( + dt.duration_trunc(Duration::days(1)).unwrap().to_string(), + "2012-12-12 00:00:00 UTC" + ); + } + + #[test] + fn test_duration_trunc_pre_epoch() { + let dt = Utc.ymd(1969, 12, 12).and_hms(12, 12, 12); + assert_eq!( + dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(), + "1969-12-12 12:10:00 UTC" + ); + } +} diff --git a/src/sys.rs b/src/sys.rs new file mode 100644 index 0000000..2e46b7e --- /dev/null +++ b/src/sys.rs @@ -0,0 +1,126 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Platform wrappers for converting UTC times to and from the local time zone. +//! +//! This code was rescued from v0.1 of the time crate, which is no longer +//! maintained. It has been substantially stripped down to the bare minimum +//! required by chrono. + +use std::time::{SystemTime, UNIX_EPOCH}; + +#[cfg(any(target_arch = "wasm32", target_env = "sgx"))] +#[path = "sys/stub.rs"] +mod inner; + +#[cfg(unix)] +#[path = "sys/unix.rs"] +mod inner; + +#[cfg(windows)] +#[path = "sys/windows.rs"] +mod inner; + +/// A record specifying a time value in seconds and nanoseconds, where +/// nanoseconds represent the offset from the given second. +/// +/// For example a timespec of 1.2 seconds after the beginning of the epoch would +/// be represented as {sec: 1, nsec: 200000000}. +pub struct Timespec { + pub sec: i64, + pub nsec: i32, +} + +impl Timespec { + /// Constructs a timespec representing the current time in UTC. + pub fn now() -> Timespec { + let st = + SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); + Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 } + } + + /// Converts this timespec into the system's local time. + pub fn local(self) -> Tm { + let mut tm = Tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_utcoff: 0, + tm_nsec: 0, + }; + inner::time_to_local_tm(self.sec, &mut tm); + tm.tm_nsec = self.nsec; + tm + } +} + +/// Holds a calendar date and time broken down into its components (year, month, +/// day, and so on), also called a broken-down time value. +// FIXME: use c_int instead of i32? +#[cfg(feature = "clock")] +#[repr(C)] +pub struct Tm { + /// Seconds after the minute - [0, 60] + pub tm_sec: i32, + + /// Minutes after the hour - [0, 59] + pub tm_min: i32, + + /// Hours after midnight - [0, 23] + pub tm_hour: i32, + + /// Day of the month - [1, 31] + pub tm_mday: i32, + + /// Months since January - [0, 11] + pub tm_mon: i32, + + /// Years since 1900 + pub tm_year: i32, + + /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday. + pub tm_wday: i32, + + /// Days since January 1 - [0, 365] + pub tm_yday: i32, + + /// Daylight Saving Time flag. + /// + /// This value is positive if Daylight Saving Time is in effect, zero if + /// Daylight Saving Time is not in effect, and negative if this information + /// is not available. + pub tm_isdst: i32, + + /// Identifies the time zone that was used to compute this broken-down time + /// value, including any adjustment for Daylight Saving Time. This is the + /// number of seconds east of UTC. For example, for U.S. Pacific Daylight + /// Time, the value is `-7*60*60 = -25200`. + pub tm_utcoff: i32, + + /// Nanoseconds after the second - [0, 109 - 1] + pub tm_nsec: i32, +} + +impl Tm { + /// Convert time to the seconds from January 1, 1970 + pub fn to_timespec(&self) -> Timespec { + let sec = match self.tm_utcoff { + 0 => inner::utc_tm_to_time(self), + _ => inner::local_tm_to_time(self), + }; + Timespec { sec: sec, nsec: self.tm_nsec } + } +} diff --git a/src/sys/stub.rs b/src/sys/stub.rs new file mode 100644 index 0000000..9172a85 --- /dev/null +++ b/src/sys/stub.rs @@ -0,0 +1,80 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::Tm; + +fn time_to_tm(ts: i64, tm: &mut Tm) { + let leapyear = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) }; + + static YTAB: [[i64; 12]; 2] = [ + [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + ]; + + let mut year = 1970; + + let dayclock = ts % 86400; + let mut dayno = ts / 86400; + + tm.tm_sec = (dayclock % 60) as i32; + tm.tm_min = ((dayclock % 3600) / 60) as i32; + tm.tm_hour = (dayclock / 3600) as i32; + tm.tm_wday = ((dayno + 4) % 7) as i32; + loop { + let yearsize = if leapyear(year) { 366 } else { 365 }; + if dayno >= yearsize { + dayno -= yearsize; + year += 1; + } else { + break; + } + } + tm.tm_year = (year - 1900) as i32; + tm.tm_yday = dayno as i32; + let mut mon = 0; + while dayno >= YTAB[if leapyear(year) { 1 } else { 0 }][mon] { + dayno -= YTAB[if leapyear(year) { 1 } else { 0 }][mon]; + mon += 1; + } + tm.tm_mon = mon as i32; + tm.tm_mday = dayno as i32 + 1; + tm.tm_isdst = 0; +} + +fn tm_to_time(tm: &Tm) -> i64 { + let mut y = tm.tm_year as i64 + 1900; + let mut m = tm.tm_mon as i64 + 1; + if m <= 2 { + y -= 1; + m += 12; + } + let d = tm.tm_mday as i64; + let h = tm.tm_hour as i64; + let mi = tm.tm_min as i64; + let s = tm.tm_sec as i64; + (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400 + + 3600 * h + + 60 * mi + + s +} + +pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); +} + +pub fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) +} + +pub fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) +} diff --git a/src/sys/unix.rs b/src/sys/unix.rs new file mode 100644 index 0000000..2f845e7 --- /dev/null +++ b/src/sys/unix.rs @@ -0,0 +1,126 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::Tm; +use libc::{self, time_t}; +use std::io; +use std::mem; + +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +extern "C" { + static timezone: time_t; + static altzone: time_t; +} + +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +fn tzset() { + extern "C" { + fn tzset(); + } + unsafe { tzset() } +} + +fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut libc::tm) { + tm.tm_sec = rust_tm.tm_sec; + tm.tm_min = rust_tm.tm_min; + tm.tm_hour = rust_tm.tm_hour; + tm.tm_mday = rust_tm.tm_mday; + tm.tm_mon = rust_tm.tm_mon; + tm.tm_year = rust_tm.tm_year; + tm.tm_wday = rust_tm.tm_wday; + tm.tm_yday = rust_tm.tm_yday; + tm.tm_isdst = rust_tm.tm_isdst; +} + +fn tm_to_rust_tm(tm: &libc::tm, utcoff: i32, rust_tm: &mut Tm) { + rust_tm.tm_sec = tm.tm_sec; + rust_tm.tm_min = tm.tm_min; + rust_tm.tm_hour = tm.tm_hour; + rust_tm.tm_mday = tm.tm_mday; + rust_tm.tm_mon = tm.tm_mon; + rust_tm.tm_year = tm.tm_year; + rust_tm.tm_wday = tm.tm_wday; + rust_tm.tm_yday = tm.tm_yday; + rust_tm.tm_isdst = tm.tm_isdst; + rust_tm.tm_utcoff = utcoff; +} + +#[cfg(any(target_os = "nacl", target_os = "solaris", target_os = "illumos"))] +unsafe fn timegm(tm: *mut libc::tm) -> time_t { + use std::env::{remove_var, set_var, var_os}; + extern "C" { + fn tzset(); + } + + let ret; + + let current_tz = var_os("TZ"); + set_var("TZ", "UTC"); + tzset(); + + ret = libc::mktime(tm); + + if let Some(tz) = current_tz { + set_var("TZ", tz); + } else { + remove_var("TZ"); + } + tzset(); + + ret +} + +pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + unsafe { + let sec = sec as time_t; + let mut out = mem::zeroed(); + if libc::localtime_r(&sec, &mut out).is_null() { + panic!("localtime_r failed: {}", io::Error::last_os_error()); + } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + let gmtoff = { + tzset(); + // < 0 means we don't know; assume we're not in DST. + if out.tm_isdst == 0 { + // timezone is seconds west of UTC, tm_gmtoff is seconds east + -timezone + } else if out.tm_isdst > 0 { + -altzone + } else { + -timezone + } + }; + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + let gmtoff = out.tm_gmtoff; + tm_to_rust_tm(&out, gmtoff as i32, tm); + } +} + +pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 { + #[cfg(not(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "nacl", + target_os = "solaris", + target_os = "illumos" + )))] + use libc::timegm; + #[cfg(all(target_os = "android", target_pointer_width = "32"))] + use libc::timegm64 as timegm; + + let mut tm = unsafe { mem::zeroed() }; + rust_tm_to_tm(rust_tm, &mut tm); + unsafe { timegm(&mut tm) as i64 } +} + +pub fn local_tm_to_time(rust_tm: &Tm) -> i64 { + let mut tm = unsafe { mem::zeroed() }; + rust_tm_to_tm(rust_tm, &mut tm); + unsafe { libc::mktime(&mut tm) as i64 } +} diff --git a/src/sys/windows.rs b/src/sys/windows.rs new file mode 100644 index 0000000..3f90338 --- /dev/null +++ b/src/sys/windows.rs @@ -0,0 +1,131 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::Tm; +use std::io; +use std::mem; + +use winapi::shared::minwindef::*; +use winapi::um::minwinbase::SYSTEMTIME; +use winapi::um::timezoneapi::*; + +const HECTONANOSECS_IN_SEC: i64 = 10_000_000; +const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; + +fn time_to_file_time(sec: i64) -> FILETIME { + let t = ((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH) as u64; + FILETIME { dwLowDateTime: t as DWORD, dwHighDateTime: (t >> 32) as DWORD } +} + +fn file_time_as_u64(ft: &FILETIME) -> u64 { + ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64) +} + +fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 { + let t = file_time_as_u64(ft) as i64; + ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64 +} + +fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME { + unsafe { + let mut ft = mem::zeroed(); + SystemTimeToFileTime(sys, &mut ft); + ft + } +} + +fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME { + let mut sys: SYSTEMTIME = unsafe { mem::zeroed() }; + sys.wSecond = tm.tm_sec as WORD; + sys.wMinute = tm.tm_min as WORD; + sys.wHour = tm.tm_hour as WORD; + sys.wDay = tm.tm_mday as WORD; + sys.wDayOfWeek = tm.tm_wday as WORD; + sys.wMonth = (tm.tm_mon + 1) as WORD; + sys.wYear = (tm.tm_year + 1900) as WORD; + sys +} + +fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) { + tm.tm_sec = sys.wSecond as i32; + tm.tm_min = sys.wMinute as i32; + tm.tm_hour = sys.wHour as i32; + tm.tm_mday = sys.wDay as i32; + tm.tm_wday = sys.wDayOfWeek as i32; + tm.tm_mon = (sys.wMonth - 1) as i32; + tm.tm_year = (sys.wYear - 1900) as i32; + tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday); + + fn yday(year: i32, month: i32, day: i32) -> i32 { + let leap = if month > 2 { + if year % 4 == 0 { + 1 + } else { + 2 + } + } else { + 0 + }; + let july = if month > 7 { 1 } else { 0 }; + + (month - 1) * 30 + month / 2 + (day - 1) - leap + july + } +} + +macro_rules! call { + ($name:ident($($arg:expr),*)) => { + if $name($($arg),*) == 0 { + panic!(concat!(stringify!($name), " failed with: {}"), + io::Error::last_os_error()); + } + } +} + +pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + let ft = time_to_file_time(sec); + unsafe { + let mut utc = mem::zeroed(); + let mut local = mem::zeroed(); + call!(FileTimeToSystemTime(&ft, &mut utc)); + call!(SystemTimeToTzSpecificLocalTime(0 as *const _, &mut utc, &mut local)); + system_time_to_tm(&local, tm); + + let local = system_time_to_file_time(&local); + let local_sec = file_time_to_unix_seconds(&local); + + let mut tz = mem::zeroed(); + GetTimeZoneInformation(&mut tz); + + // SystemTimeToTzSpecificLocalTime already applied the biases so + // check if it non standard + tm.tm_utcoff = (local_sec - sec) as i32; + tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { 0 } else { 1 }; + } +} + +pub fn utc_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let sys_time = tm_to_system_time(tm); + call!(SystemTimeToFileTime(&sys_time, &mut ft)); + file_time_to_unix_seconds(&ft) + } +} + +pub fn local_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let mut utc = mem::zeroed(); + let mut sys_time = tm_to_system_time(tm); + call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, &mut sys_time, &mut utc)); + call!(SystemTimeToFileTime(&utc, &mut ft)); + file_time_to_unix_seconds(&ft) + } +} -- cgit v1.2.3