#[cfg(feature = "alloc")]
use core::borrow::Borrow;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration;
use core::{fmt, str};
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
use rkyv::{Archive, Deserialize, Serialize};
#[cfg(feature = "alloc")]
use crate::format::DelayedFormat;
use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems};
use crate::format::{Fixed, Item, Numeric, Pad};
use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime};
use crate::offset::Utc;
use crate::time_delta::NANOS_PER_SEC;
use crate::{
expect, try_opt, DateTime, Datelike, FixedOffset, LocalResult, Months, TimeDelta, TimeZone,
Timelike, Weekday,
};
#[cfg(feature = "rustc-serialize")]
pub(super) mod rustc_serialize;
#[cfg(feature = "serde")]
pub(crate) mod serde;
#[cfg(test)]
mod tests;
const MAX_SECS_BITS: usize = 44;
#[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MIN instead")]
pub const MIN_DATETIME: NaiveDateTime = NaiveDateTime::MIN;
#[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MAX instead")]
pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime::MAX;
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)]
#[cfg_attr(
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
derive(Archive, Deserialize, Serialize),
archive(compare(PartialEq, PartialOrd)),
archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash))
)]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
pub struct NaiveDateTime {
date: NaiveDate,
time: NaiveTime,
}
impl NaiveDateTime {
#[inline]
pub const fn new(date: NaiveDate, time: NaiveTime) -> NaiveDateTime {
NaiveDateTime { date, time }
}
#[deprecated(since = "0.4.23", note = "use `from_timestamp_opt()` instead")]
#[inline]
#[must_use]
pub const fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime {
let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs);
expect!(datetime, "invalid or out-of-range datetime")
}
#[inline]
#[must_use]
pub const fn from_timestamp_millis(millis: i64) -> Option<NaiveDateTime> {
let secs = millis.div_euclid(1000);
let nsecs = millis.rem_euclid(1000) as u32 * 1_000_000;
NaiveDateTime::from_timestamp_opt(secs, nsecs)
}
#[inline]
#[must_use]
pub const fn from_timestamp_micros(micros: i64) -> Option<NaiveDateTime> {
let secs = micros.div_euclid(1_000_000);
let nsecs = micros.rem_euclid(1_000_000) as u32 * 1000;
NaiveDateTime::from_timestamp_opt(secs, nsecs)
}
#[inline]
#[must_use]
pub const fn from_timestamp_nanos(nanos: i64) -> Option<NaiveDateTime> {
let secs = nanos.div_euclid(NANOS_PER_SEC as i64);
let nsecs = nanos.rem_euclid(NANOS_PER_SEC as i64) as u32;
NaiveDateTime::from_timestamp_opt(secs, nsecs)
}
#[inline]
#[must_use]
pub const fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option<NaiveDateTime> {
let days = secs.div_euclid(86_400);
let secs = secs.rem_euclid(86_400);
if days < i32::MIN as i64 || days > i32::MAX as i64 {
return None;
}
let date =
NaiveDate::from_num_days_from_ce_opt(try_opt!((days as i32).checked_add(719_163)));
let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs);
match (date, time) {
(Some(date), Some(time)) => Some(NaiveDateTime { date, time }),
(_, _) => None,
}
}
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_naive_datetime_with_offset(0) }
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDateTime, &'a str)> {
let mut parsed = Parsed::new();
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_naive_datetime_with_offset(0).map(|d| (d, remainder)) }
#[inline]
pub const fn date(&self) -> NaiveDate {
self.date
}
#[inline]
pub const fn time(&self) -> NaiveTime {
self.time
}
#[inline]
#[must_use]
pub const fn timestamp(&self) -> i64 {
const UNIX_EPOCH_DAY: i64 = 719_163;
let gregorian_day = self.date.num_days_from_ce() as i64;
let seconds_from_midnight = self.time.num_seconds_from_midnight() as i64;
(gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight
}
#[inline]
#[must_use]
pub const fn timestamp_millis(&self) -> i64 {
let as_ms = self.timestamp() * 1000;
as_ms + self.timestamp_subsec_millis() as i64
}
#[inline]
#[must_use]
pub const fn timestamp_micros(&self) -> i64 {
let as_us = self.timestamp() * 1_000_000;
as_us + self.timestamp_subsec_micros() as i64
}
#[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")]
#[inline]
#[must_use]
pub const fn timestamp_nanos(&self) -> i64 {
expect!(
self.timestamp_nanos_opt(),
"value can not be represented in a timestamp with nanosecond precision."
)
}
#[inline]
#[must_use]
pub const fn timestamp_nanos_opt(&self) -> Option<i64> {
let mut timestamp = self.timestamp();
let mut timestamp_subsec_nanos = self.timestamp_subsec_nanos() as i64;
if timestamp < 0 && timestamp_subsec_nanos > 0 {
timestamp_subsec_nanos -= 1_000_000_000;
timestamp += 1;
}
try_opt!(timestamp.checked_mul(1_000_000_000)).checked_add(timestamp_subsec_nanos)
}
#[inline]
#[must_use]
pub const fn timestamp_subsec_millis(&self) -> u32 {
self.timestamp_subsec_nanos() / 1_000_000
}
#[inline]
#[must_use]
pub const fn timestamp_subsec_micros(&self) -> u32 {
self.timestamp_subsec_nanos() / 1_000
}
#[inline]
#[must_use]
pub const fn timestamp_subsec_nanos(&self) -> u32 {
self.time.nanosecond()
}
#[must_use]
pub const fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
let (time, rhs) = self.time.overflowing_add_signed(rhs);
if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) {
return None;
}
let date = try_opt!(self.date.checked_add_signed(TimeDelta::seconds(rhs)));
Some(NaiveDateTime { date, time })
}
#[must_use]
pub const fn checked_add_months(self, rhs: Months) -> Option<NaiveDateTime> {
Some(Self { date: try_opt!(self.date.checked_add_months(rhs)), time: self.time })
}
#[must_use]
pub const fn checked_add_offset(self, rhs: FixedOffset) -> Option<NaiveDateTime> {
let (time, days) = self.time.overflowing_add_offset(rhs);
let date = match days {
-1 => try_opt!(self.date.pred_opt()),
1 => try_opt!(self.date.succ_opt()),
_ => self.date,
};
Some(NaiveDateTime { date, time })
}
pub const fn checked_sub_offset(self, rhs: FixedOffset) -> Option<NaiveDateTime> {
let (time, days) = self.time.overflowing_sub_offset(rhs);
let date = match days {
-1 => try_opt!(self.date.pred_opt()),
1 => try_opt!(self.date.succ_opt()),
_ => self.date,
};
Some(NaiveDateTime { date, time })
}
#[must_use]
pub(crate) fn overflowing_add_offset(self, rhs: FixedOffset) -> NaiveDateTime {
let (time, days) = self.time.overflowing_add_offset(rhs);
let date = match days {
-1 => self.date.pred_opt().unwrap_or(NaiveDate::BEFORE_MIN),
1 => self.date.succ_opt().unwrap_or(NaiveDate::AFTER_MAX),
_ => self.date,
};
NaiveDateTime { date, time }
}
#[must_use]
#[allow(unused)] pub(crate) fn overflowing_sub_offset(self, rhs: FixedOffset) -> NaiveDateTime {
let (time, days) = self.time.overflowing_sub_offset(rhs);
let date = match days {
-1 => self.date.pred_opt().unwrap_or(NaiveDate::BEFORE_MIN),
1 => self.date.succ_opt().unwrap_or(NaiveDate::AFTER_MAX),
_ => self.date,
};
NaiveDateTime { date, time }
}
#[must_use]
pub const fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
let (time, rhs) = self.time.overflowing_sub_signed(rhs);
if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) {
return None;
}
let date = try_opt!(self.date.checked_sub_signed(TimeDelta::seconds(rhs)));
Some(NaiveDateTime { date, time })
}
#[must_use]
pub const fn checked_sub_months(self, rhs: Months) -> Option<NaiveDateTime> {
Some(Self { date: try_opt!(self.date.checked_sub_months(rhs)), time: self.time })
}
#[must_use]
pub const fn checked_add_days(self, days: Days) -> Option<Self> {
Some(Self { date: try_opt!(self.date.checked_add_days(days)), ..self })
}
#[must_use]
pub const fn checked_sub_days(self, days: Days) -> Option<Self> {
Some(Self { date: try_opt!(self.date.checked_sub_days(days)), ..self })
}
#[must_use]
pub const fn signed_duration_since(self, rhs: NaiveDateTime) -> TimeDelta {
expect!(
self.date
.signed_duration_since(rhs.date)
.checked_add(&self.time.signed_duration_since(rhs.time)),
"always in range"
)
}
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
B: Borrow<Item<'a>>,
{
DelayedFormat::new(Some(self.date), Some(self.time), items)
}
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}
#[must_use]
pub fn and_local_timezone<Tz: TimeZone>(&self, tz: Tz) -> LocalResult<DateTime<Tz>> {
tz.from_local_datetime(self)
}
#[must_use]
pub const fn and_utc(&self) -> DateTime<Utc> {
DateTime::from_naive_utc_and_offset(*self, Utc)
}
pub const MIN: Self = Self { date: NaiveDate::MIN, time: NaiveTime::MIN };
pub const MAX: Self = Self { date: NaiveDate::MAX, time: NaiveTime::MAX };
pub const UNIX_EPOCH: Self =
expect!(NaiveDate::from_ymd_opt(1970, 1, 1), "").and_time(NaiveTime::MIN);
}
impl From<NaiveDate> for NaiveDateTime {
fn from(date: NaiveDate) -> Self {
date.and_hms_opt(0, 0, 0).unwrap()
}
}
impl Datelike for NaiveDateTime {
#[inline]
fn year(&self) -> i32 {
self.date.year()
}
#[inline]
fn month(&self) -> u32 {
self.date.month()
}
#[inline]
fn month0(&self) -> u32 {
self.date.month0()
}
#[inline]
fn day(&self) -> u32 {
self.date.day()
}
#[inline]
fn day0(&self) -> u32 {
self.date.day0()
}
#[inline]
fn ordinal(&self) -> u32 {
self.date.ordinal()
}
#[inline]
fn ordinal0(&self) -> u32 {
self.date.ordinal0()
}
#[inline]
fn weekday(&self) -> Weekday {
self.date.weekday()
}
#[inline]
fn iso_week(&self) -> IsoWeek {
self.date.iso_week()
}
#[inline]
fn with_year(&self, year: i32) -> Option<NaiveDateTime> {
self.date.with_year(year).map(|d| NaiveDateTime { date: d, ..*self })
}
#[inline]
fn with_month(&self, month: u32) -> Option<NaiveDateTime> {
self.date.with_month(month).map(|d| NaiveDateTime { date: d, ..*self })
}
#[inline]
fn with_month0(&self, month0: u32) -> Option<NaiveDateTime> {
self.date.with_month0(month0).map(|d| NaiveDateTime { date: d, ..*self })
}
#[inline]
fn with_day(&self, day: u32) -> Option<NaiveDateTime> {
self.date.with_day(day).map(|d| NaiveDateTime { date: d, ..*self })
}
#[inline]
fn with_day0(&self, day0: u32) -> Option<NaiveDateTime> {
self.date.with_day0(day0).map(|d| NaiveDateTime { date: d, ..*self })
}
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDateTime> {
self.date.with_ordinal(ordinal).map(|d| NaiveDateTime { date: d, ..*self })
}
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDateTime> {
self.date.with_ordinal0(ordinal0).map(|d| NaiveDateTime { date: d, ..*self })
}
}
impl Timelike for NaiveDateTime {
#[inline]
fn hour(&self) -> u32 {
self.time.hour()
}
#[inline]
fn minute(&self) -> u32 {
self.time.minute()
}
#[inline]
fn second(&self) -> u32 {
self.time.second()
}
#[inline]
fn nanosecond(&self) -> u32 {
self.time.nanosecond()
}
#[inline]
fn with_hour(&self, hour: u32) -> Option<NaiveDateTime> {
self.time.with_hour(hour).map(|t| NaiveDateTime { time: t, ..*self })
}
#[inline]
fn with_minute(&self, min: u32) -> Option<NaiveDateTime> {
self.time.with_minute(min).map(|t| NaiveDateTime { time: t, ..*self })
}
#[inline]
fn with_second(&self, sec: u32) -> Option<NaiveDateTime> {
self.time.with_second(sec).map(|t| NaiveDateTime { time: t, ..*self })
}
#[inline]
fn with_nanosecond(&self, nano: u32) -> Option<NaiveDateTime> {
self.time.with_nanosecond(nano).map(|t| NaiveDateTime { time: t, ..*self })
}
}
impl Add<TimeDelta> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn add(self, rhs: TimeDelta) -> NaiveDateTime {
self.checked_add_signed(rhs).expect("`NaiveDateTime + TimeDelta` overflowed")
}
}
impl Add<Duration> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn add(self, rhs: Duration) -> NaiveDateTime {
let rhs = TimeDelta::from_std(rhs)
.expect("overflow converting from core::time::Duration to TimeDelta");
self.checked_add_signed(rhs).expect("`NaiveDateTime + TimeDelta` overflowed")
}
}
impl AddAssign<TimeDelta> for NaiveDateTime {
#[inline]
fn add_assign(&mut self, rhs: TimeDelta) {
*self = self.add(rhs);
}
}
impl AddAssign<Duration> for NaiveDateTime {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
*self = self.add(rhs);
}
}
impl Add<FixedOffset> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn add(self, rhs: FixedOffset) -> NaiveDateTime {
self.checked_add_offset(rhs).expect("`NaiveDateTime + FixedOffset` out of range")
}
}
impl Add<Months> for NaiveDateTime {
type Output = NaiveDateTime;
fn add(self, rhs: Months) -> Self::Output {
self.checked_add_months(rhs).expect("`NaiveDateTime + Months` out of range")
}
}
impl Sub<TimeDelta> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn sub(self, rhs: TimeDelta) -> NaiveDateTime {
self.checked_sub_signed(rhs).expect("`NaiveDateTime - TimeDelta` overflowed")
}
}
impl Sub<Duration> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn sub(self, rhs: Duration) -> NaiveDateTime {
let rhs = TimeDelta::from_std(rhs)
.expect("overflow converting from core::time::Duration to TimeDelta");
self.checked_sub_signed(rhs).expect("`NaiveDateTime - TimeDelta` overflowed")
}
}
impl SubAssign<TimeDelta> for NaiveDateTime {
#[inline]
fn sub_assign(&mut self, rhs: TimeDelta) {
*self = self.sub(rhs);
}
}
impl SubAssign<Duration> for NaiveDateTime {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
*self = self.sub(rhs);
}
}
impl Sub<FixedOffset> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
self.checked_sub_offset(rhs).expect("`NaiveDateTime - FixedOffset` out of range")
}
}
impl Sub<Months> for NaiveDateTime {
type Output = NaiveDateTime;
fn sub(self, rhs: Months) -> Self::Output {
self.checked_sub_months(rhs).expect("`NaiveDateTime - Months` out of range")
}
}
impl Sub<NaiveDateTime> for NaiveDateTime {
type Output = TimeDelta;
#[inline]
fn sub(self, rhs: NaiveDateTime) -> TimeDelta {
self.signed_duration_since(rhs)
}
}
impl Add<Days> for NaiveDateTime {
type Output = NaiveDateTime;
fn add(self, days: Days) -> Self::Output {
self.checked_add_days(days).expect("`NaiveDateTime + Days` out of range")
}
}
impl Sub<Days> for NaiveDateTime {
type Output = NaiveDateTime;
fn sub(self, days: Days) -> Self::Output {
self.checked_sub_days(days).expect("`NaiveDateTime - Days` out of range")
}
}
impl fmt::Debug for NaiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.date.fmt(f)?;
f.write_char('T')?;
self.time.fmt(f)
}
}
impl fmt::Display for NaiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.date.fmt(f)?;
f.write_char(' ')?;
self.time.fmt(f)
}
}
impl str::FromStr for NaiveDateTime {
type Err = ParseError;
fn from_str(s: &str) -> ParseResult<NaiveDateTime> {
const ITEMS: &[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"), 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)
}
}
impl Default for NaiveDateTime {
fn default() -> Self {
NaiveDateTime::from_timestamp_opt(0, 0).unwrap()
}
}
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<F, E>(to_string: F)
where
F: Fn(&NaiveDateTime) -> Result<String, E>,
E: ::std::fmt::Debug,
{
assert_eq!(
to_string(
&NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap()
)
.ok(),
Some(r#""2016-07-08T09:10:48.090""#.into())
);
assert_eq!(
to_string(&NaiveDate::from_ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap())
.ok(),
Some(r#""2014-07-24T12:34:06""#.into())
);
assert_eq!(
to_string(
&NaiveDate::from_ymd_opt(0, 1, 1).unwrap().and_hms_milli_opt(0, 0, 59, 1_000).unwrap()
)
.ok(),
Some(r#""0000-01-01T00:00:60""#.into())
);
assert_eq!(
to_string(
&NaiveDate::from_ymd_opt(-1, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 7).unwrap()
)
.ok(),
Some(r#""-0001-12-31T23:59:59.000000007""#.into())
);
assert_eq!(
to_string(&NaiveDate::MIN.and_hms_opt(0, 0, 0).unwrap()).ok(),
Some(r#""-262143-01-01T00:00:00""#.into())
);
assert_eq!(
to_string(&NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()).ok(),
Some(r#""+262142-12-31T23:59:60.999999999""#.into())
);
}
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_decodable_json<F, E>(from_str: F)
where
F: Fn(&str) -> Result<NaiveDateTime, E>,
E: ::std::fmt::Debug,
{
assert_eq!(
from_str(r#""2016-07-08T09:10:48.090""#).ok(),
Some(
NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap()
)
);
assert_eq!(
from_str(r#""2016-7-8T9:10:48.09""#).ok(),
Some(
NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap()
)
);
assert_eq!(
from_str(r#""2014-07-24T12:34:06""#).ok(),
Some(NaiveDate::from_ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap())
);
assert_eq!(
from_str(r#""0000-01-01T00:00:60""#).ok(),
Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().and_hms_milli_opt(0, 0, 59, 1_000).unwrap())
);
assert_eq!(
from_str(r#""0-1-1T0:0:60""#).ok(),
Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().and_hms_milli_opt(0, 0, 59, 1_000).unwrap())
);
assert_eq!(
from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(),
Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 7).unwrap())
);
assert_eq!(
from_str(r#""-262143-01-01T00:00:00""#).ok(),
Some(NaiveDate::MIN.and_hms_opt(0, 0, 0).unwrap())
);
assert_eq!(
from_str(r#""+262142-12-31T23:59:60.999999999""#).ok(),
Some(NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap())
);
assert_eq!(
from_str(r#""+262142-12-31T23:59:60.9999999999997""#).ok(), Some(NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap())
);
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());
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<F, E>(from_str: F)
where
F: Fn(&str) -> Result<rustc_serialize::TsSeconds, E>,
E: ::std::fmt::Debug,
{
assert_eq!(
*from_str("0").unwrap(),
NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
"should parse integers as timestamps"
);
assert_eq!(
*from_str("-1").unwrap(),
NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 59).unwrap(),
"should parse integers as timestamps"
);
}