From fa15d84ab51bd4a587850568e29ef2e4398bc326 Mon Sep 17 00:00:00 2001 From: jtmoon79 <815261+jtmoon79@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:44:48 -0700 Subject: [PATCH] Add more tests for `parse_from_str` and specifiers Add exhaustive set of tests for `DateTime::parse_from_str` function and all strftime specifiers. Issue #1112 --- src/datetime/tests.rs | 211 +++++++++++++++++++++++++++++++++++++++++- src/naive/date.rs | 4 +- src/naive/mod.rs | 2 +- 3 files changed, 213 insertions(+), 4 deletions(-) diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 55fa8f3787..210fe85542 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -6,6 +6,8 @@ use crate::offset::{FixedOffset, TimeZone, Utc}; #[cfg(feature = "clock")] use crate::offset::{Local, Offset}; use crate::{Datelike, Days, LocalResult, Months, NaiveDateTime, TimeDelta}; +use crate::naive::date::{MAX_YEAR, MIN_YEAR}; +use crate::format::ParseResult; #[derive(Clone)] struct DstTester; @@ -989,8 +991,215 @@ fn test_utc_datetime_from_str_with_spaces() { assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err()); } +/// Test `parse_from_str` focused on strftime `%Y`, `%y`, and `%C` specifiers. #[test] -fn test_datetime_parse_from_str() { +fn test_datetime_parse_from_str_year() { + fn parse(data: &str, format: &str) -> ParseResult> { + eprintln!("parse: data: {:?}, format: {:?}", data, format); + DateTime::::parse_from_str(data, format) + } + fn parse_year(data: &str, format: &str) -> i32 { + eprintln!("parse_year: data: {:?}, format: {:?}", data, format); + DateTime::::parse_from_str(data, format).unwrap().year() + } + let dt = ymdhms( + &FixedOffset::east_opt(-9 * 60 * 60).unwrap(), + 2013, 8, 9, 23, 54, 35 + ); + + // + // %Y + // + // ok + assert_eq!(parse("2013-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("08-2013-09T23:54:35 -0900", "%m-%Y-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse_year("9999-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), 9999); + assert_eq!(parse_year("999-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), 999); + assert_eq!(parse_year("99-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), 99); + assert_eq!(parse_year("9-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), 9); + assert_eq!(parse_year("0-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), 0); + let d1: String = MIN_YEAR.to_string() + "-08-09T23:54:35 -0900"; + assert_eq!(parse_year(d1.as_str(), "%Y-%m-%dT%H:%M:%S %z"), MIN_YEAR); + let d1: String = (MIN_YEAR + 1).to_string() + "-08-09T23:54:35 -0900"; + assert_eq!(parse_year(d1.as_str(), "%Y-%m-%dT%H:%M:%S %z"), MIN_YEAR + 1); + // errors + // XXX: MAX_YEAR cannont be parsed, can only parse up to four decimal digits + let d1: String = MAX_YEAR.to_string() + "-08-09T23:54:35 -0900"; + assert!(parse(d1.as_str(), "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("99999-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("A-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("0x11-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("108-09T23:54:35 -0900", "%Y%m-%dT%H:%M:%S %z").is_err()); + + // + // %y + // + // ok + assert_eq!(parse("13-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("08-13-09T23:54:35 -0900", "%m-%y-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse_year("0-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z"), 2000); + assert_eq!(parse_year("99-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z"), 1999); + assert_eq!(parse_year("108-09T23:54:35 -0900", "%y%m-%dT%H:%M:%S %z"), 2010); + assert_eq!(parse_year("081-09T23:54:35 -0900", "%m%y-%dT%H:%M:%S %z"), 2001); + // errors + assert!(parse("999-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("100-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("013-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("0x11-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("A-08-09T23:54:35 -0900", "%y-%m-%dT%H:%M:%S %z").is_err()); + + // + // %C + // + // ok + assert_eq!(parse("2013-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("20_13-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse_year("0_13-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z"), 13); + assert_eq!(parse_year("9_13-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z"), 913); + assert_eq!(parse_year("99_13-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z"), 9913); + assert_eq!(parse_year("013-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), 103); + assert_eq!(parse_year("113-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), 1103); + assert_eq!(parse_year("223-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), 2203); + assert_eq!(parse_year("553-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), 5503); + assert_eq!(parse_year("993-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), 9903); + assert_eq!(parse_year("9923-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z"), 9923); + // errors + assert!(parse("913_2-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("9913-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("99923-08-09T23:54:35 -0900", "%C%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("20-13-09T23:54:35 -0900", "%m-%C-%dT%H:%M:%S %z").is_err()); + assert!(parse("999_01-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("100_01-08-09T23:54:35 -0900", "%C_%y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("0x11-08-09T23:54:35 -0900", "%C-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("A-08-09T23:54:35 -0900", "%C-%m-%dT%H:%M:%S %z").is_err()); +} + +/// Test `parse_from_str` focused on strftime `%m`, `%b`, `%h`, and `%B` specifiers. +#[test] +fn test_datetime_parse_from_str_month() { + fn parse(data: &str, format: &str) -> ParseResult> { + eprintln!("parse: data: {:?}, format: {:?}", data, format); + DateTime::::parse_from_str(data, format) + } + fn parse_month(data: &str, format: &str) -> u32 { + eprintln!("parse_month: data: {:?}, format: {:?}", data, format); + DateTime::::parse_from_str(data, format).unwrap().month() + } + let dt = ymdhms( + &FixedOffset::east_opt(-9 * 60 * 60).unwrap(), + 2013, 8, 9, 23, 54, 35 + ); + + // + // %m + // + // ok + assert_eq!(parse("2013-08-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("201308-09T23:54:35 -0900", "%Y%m-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013-0809T23:54:35 -0900", "%Y-%m%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("20130809T23:54:35 -0900", "%Y%m%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("20130809235435-0900", "%Y%m%d%H%M%S%z"), Ok(dt)); + assert_eq!(parse_month("20130109235435-0900", "%Y%m%d%H%M%S%z"), 1); + assert_eq!(parse_month("20131209235435-0900", "%Y%m%d%H%M%S%z"), 12); + // errors + assert!(parse("2013-00-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-13-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-55-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-99-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-123-09T23:54:35 -0900", "%Y-%m-%dT%H:%M:%S %z").is_err()); + + // + // %b + // + // ok + assert_eq!(parse("2013-Aug-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013aug-09T23:54:35 -0900", "%Y%b-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013-AUG09T23:54:35 -0900", "%Y-%b%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013aug09T23:54:35 -0900", "%Y%b%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013aug09235435-0900", "%Y%b%d%H%M%S%z"), Ok(dt)); + assert_eq!(parse_month("2013jan09235435-0900", "%Y%b%d%H%M%S%z"), 1); + assert_eq!(parse_month("2013feb09235435-0900", "%Y%b%d%H%M%S%z"), 2); + assert_eq!(parse_month("2013mar09235435-0900", "%Y%b%d%H%M%S%z"), 3); + assert_eq!(parse_month("2013apr09235435-0900", "%Y%b%d%H%M%S%z"), 4); + assert_eq!(parse_month("2013may09235435-0900", "%Y%b%d%H%M%S%z"), 5); + assert_eq!(parse_month("2013jun09235435-0900", "%Y%b%d%H%M%S%z"), 6); + assert_eq!(parse_month("2013jul09235435-0900", "%Y%b%d%H%M%S%z"), 7); + assert_eq!(parse_month("2013aug09235435-0900", "%Y%b%d%H%M%S%z"), 8); + assert_eq!(parse_month("2013sep09235435-0900", "%Y%b%d%H%M%S%z"), 9); + assert_eq!(parse_month("2013oct09235435-0900", "%Y%b%d%H%M%S%z"), 10); + assert_eq!(parse_month("2013nov09235435-0900", "%Y%b%d%H%M%S%z"), 11); + assert_eq!(parse_month("2013dec09235435-0900", "%Y%b%d%H%M%S%z"), 12); + // errors + assert!(parse("2013-AWG-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AU-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AG-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUG.-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUGU-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUGUST-09T23:54:35 -0900", "%Y-%b-%dT%H:%M:%S %z").is_err()); + + // + // %h + // + // ok + assert_eq!(parse("2013-Aug-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013aug-09T23:54:35 -0900", "%Y%h-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013-AUG09T23:54:35 -0900", "%Y-%h%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013aug09T23:54:35 -0900", "%Y%h%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013aug09235435-0900", "%Y%h%d%H%M%S%z"), Ok(dt)); + assert_eq!(parse_month("2013jan09235435-0900", "%Y%h%d%H%M%S%z"), 1); + assert_eq!(parse_month("2013feb09235435-0900", "%Y%h%d%H%M%S%z"), 2); + assert_eq!(parse_month("2013mar09235435-0900", "%Y%h%d%H%M%S%z"), 3); + assert_eq!(parse_month("2013apr09235435-0900", "%Y%h%d%H%M%S%z"), 4); + assert_eq!(parse_month("2013may09235435-0900", "%Y%h%d%H%M%S%z"), 5); + assert_eq!(parse_month("2013jun09235435-0900", "%Y%h%d%H%M%S%z"), 6); + assert_eq!(parse_month("2013jul09235435-0900", "%Y%h%d%H%M%S%z"), 7); + assert_eq!(parse_month("2013aug09235435-0900", "%Y%h%d%H%M%S%z"), 8); + assert_eq!(parse_month("2013sep09235435-0900", "%Y%h%d%H%M%S%z"), 9); + assert_eq!(parse_month("2013oct09235435-0900", "%Y%h%d%H%M%S%z"), 10); + assert_eq!(parse_month("2013nov09235435-0900", "%Y%h%d%H%M%S%z"), 11); + assert_eq!(parse_month("2013dec09235435-0900", "%Y%h%d%H%M%S%z"), 12); + // errors + assert!(parse("2013-AWG-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AU-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AG-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUG.-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUGU-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUGUST-09T23:54:35 -0900", "%Y-%h-%dT%H:%M:%S %z").is_err()); + + // + // %B + // + // ok + assert_eq!(parse("2013-August-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013-aUgUsT-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013-august-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013august-09T23:54:35 -0900", "%Y%B-%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013-AUGust09T23:54:35 -0900", "%Y-%B%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013august09T23:54:35 -0900", "%Y%B%dT%H:%M:%S %z"), Ok(dt)); + assert_eq!(parse("2013august09235435-0900", "%Y%B%d%H%M%S%z"), Ok(dt)); + assert_eq!(parse_month("2013january09235435-0900", "%Y%B%d%H%M%S%z"), 1); + assert_eq!(parse_month("2013february09235435-0900", "%Y%B%d%H%M%S%z"), 2); + assert_eq!(parse_month("2013march09235435-0900", "%Y%B%d%H%M%S%z"), 3); + assert_eq!(parse_month("2013april09235435-0900", "%Y%B%d%H%M%S%z"), 4); + assert_eq!(parse_month("2013may09235435-0900", "%Y%B%d%H%M%S%z"), 5); + assert_eq!(parse_month("2013june09235435-0900", "%Y%B%d%H%M%S%z"), 6); + assert_eq!(parse_month("2013july09235435-0900", "%Y%B%d%H%M%S%z"), 7); + assert_eq!(parse_month("2013august09235435-0900", "%Y%B%d%H%M%S%z"), 8); + assert_eq!(parse_month("2013september09235435-0900", "%Y%B%d%H%M%S%z"), 9); + assert_eq!(parse_month("2013october09235435-0900", "%Y%B%d%H%M%S%z"), 10); + assert_eq!(parse_month("2013november09235435-0900", "%Y%B%d%H%M%S%z"), 11); + assert_eq!(parse_month("2013december09235435-0900", "%Y%B%d%H%M%S%z"), 12); + // errors + assert!(parse("2013-AUGUS-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUGU-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AUG.-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-AG-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z").is_err()); + assert!(parse("2013-A-09T23:54:35 -0900", "%Y-%B-%dT%H:%M:%S %z").is_err()); +} + +/// Test `parse_from_str` focused on strftime `%Z`, `%z`, `%:z`, and `%::z` specifiers. +#[test] +fn test_datetime_parse_from_str_timezone() { let dt = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35); let parse = DateTime::::parse_from_str; diff --git a/src/naive/date.rs b/src/naive/date.rs index b367936f3b..a44b1c3ccb 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -28,8 +28,8 @@ use crate::{Datelike, TimeDelta, 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; +pub(crate) const MAX_YEAR: i32 = internals::MAX_YEAR; +pub(crate) 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 diff --git a/src/naive/mod.rs b/src/naive/mod.rs index f1bd6d6cbc..fefd1f6473 100644 --- a/src/naive/mod.rs +++ b/src/naive/mod.rs @@ -4,7 +4,7 @@ //! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)), //! but can be also used for the simpler date and time handling. -mod date; +pub(crate) mod date; pub(crate) mod datetime; mod internals; mod isoweek;