From 4c6f0a77068ac476a12eca3d5a3f582e506cdd02 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 28 May 2023 15:32:02 +0200 Subject: [PATCH] Error if there are too many fields for `NaiveDate` --- src/format/parsed.rs | 22 ++++++++++++++-------- src/naive/date.rs | 25 ++++++++++++++++--------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 0d88888919..df3181baf9 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -6,7 +6,7 @@ use core::convert::TryFrom; -use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; +use super::{ParseResult, BAD_FORMAT, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone}; use crate::oldtime::Duration as OldDuration; @@ -286,6 +286,15 @@ impl Parsed { set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) } + fn has_time_fields(&self) -> bool { + self.hour_div_12.is_some() + || self.hour_mod_12.is_some() + || self.minute.is_some() + || self.second.is_some() + || self.nanosecond.is_some() + || self.timestamp.is_some() + } + /// Returns a parsed naive date out of given fields. /// /// This method is able to determine the date from given subset of fields: @@ -301,6 +310,9 @@ impl Parsed { /// 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 { + if self.has_time_fields() || self.offset.is_some() { + return Err(BAD_FORMAT); + } self.to_naive_date_inner(true) } @@ -484,13 +496,7 @@ impl Parsed { use_default: bool, ) -> ParseResult { let date = self.to_naive_date_inner(use_default); - let time = if use_default - && self.hour_div_12.is_none() - && self.hour_mod_12.is_none() - && self.minute.is_none() - && self.second.is_none() - && self.nanosecond.is_none() - { + let time = if use_default && !self.has_time_fields() { Ok(NaiveTime::MIN) } else { self.to_naive_time_inner(use_default) diff --git a/src/naive/date.rs b/src/naive/date.rs index 7beca486ff..214745b2eb 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -543,13 +543,18 @@ impl NaiveDate { /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap())); /// ``` /// - /// Time and offset is ignored for the purpose of parsing. + /// Time and offset will return an error, parse to a [`NaiveDateTime`] or + /// `DateTime` first. /// /// ``` - /// # 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_opt(2014, 5, 17).unwrap())); + /// use chrono::{DateTime, FixedOffset, NaiveDate}; + /// use chrono::format::ParseErrorKind; + /// + /// let result = NaiveDate::parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"); + /// assert_eq!(result.unwrap_err().kind(), ParseErrorKind::BadFormat); + /// let dt = + /// DateTime::::parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"); + /// assert_eq!(dt.unwrap().date_naive(), NaiveDate::from_ymd_opt(2014, 5, 17).unwrap()); /// ``` /// /// If the format string doesn't contain a day field, it defaults to the first day. @@ -2963,10 +2968,6 @@ mod tests { #[test] fn test_date_parse_from_str() { let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - 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)) @@ -2988,6 +2989,12 @@ mod tests { NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(), NaiveDate::from_ymd_opt(2019, 1, 13), ); + + assert!(NaiveDate::parse_from_str("2014-5-7T12:34:56", "%Y-%m-%dT%H:%M:%S").is_err()); + assert!(NaiveDate::parse_from_str("2014-5-7+09:30", "%Y-%m-%dT%z").is_err()); + assert!( + NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z").is_err() + ); } #[test]