diff --git a/src/items/builder.rs b/src/items/builder.rs index 5fe841c..de44f3d 100644 --- a/src/items/builder.rs +++ b/src/items/builder.rs @@ -221,7 +221,7 @@ impl DateTimeBuilder { let d: civil::Date = if date.year.is_some() { date.try_into()? } else { - date.with_year(dt.date().year() as u16).try_into()? + date.with_year(dt.date().year() as u32).try_into()? }; dt = dt.with().date(d).build()?; } diff --git a/src/items/date.rs b/src/items/date.rs index 5de3351..97cfdef 100644 --- a/src/items/date.rs +++ b/src/items/date.rs @@ -44,11 +44,11 @@ use super::{ pub(crate) struct Date { pub(crate) day: u8, pub(crate) month: u8, - pub(crate) year: Option, + pub(crate) year: Option, } impl Date { - pub(super) fn with_year(self, year: u16) -> Self { + pub(super) fn with_year(self, year: u32) -> Self { Date { day: self.day, month: self.month, @@ -118,12 +118,12 @@ impl TryFrom for jiff::civil::Date { type Error = &'static str; fn try_from(date: Date) -> Result { - jiff::civil::Date::new( - date.year.unwrap_or(0) as i16, - date.month as i8, - date.day as i8, - ) - .map_err(|_| "date is not valid") + let year = date.year.unwrap_or(0); + let year: i16 = year + .try_into() + .map_err(|_| "date year is outside the supported range")?; + jiff::civil::Date::new(year, date.month as i8, date.day as i8) + .map_err(|_| "date is not valid") } } diff --git a/src/items/mod.rs b/src/items/mod.rs index deb790c..9646dc6 100644 --- a/src/items/mod.rs +++ b/src/items/mod.rs @@ -408,13 +408,9 @@ mod tests { let result = parse(&mut "2025-05-19 @1690466034"); assert!(result.is_err()); - // Pure number as year (too large). + // Pure number as year (large years are parsed successfully). let result = parse(&mut "jul 18 12:30 10000"); - assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("year must be no greater than 9999")); + assert!(result.is_ok()); // Pure number as time (too long). let result = parse(&mut "01:02 12345"); diff --git a/src/items/year.rs b/src/items/year.rs index 564d12d..489b0a7 100644 --- a/src/items/year.rs +++ b/src/items/year.rs @@ -14,11 +14,13 @@ use winnow::{stream::AsChar, token::take_while, ModalResult, Parser}; use super::primitive::s; +const GNU_MAX_YEAR: u32 = 2_147_485_547; + // TODO: Leverage `TryFrom` trait. -pub(super) fn year_from_str(year_str: &str) -> Result { +pub(super) fn year_from_str(year_str: &str) -> Result { let mut year = year_str - .parse::() - .map_err(|_| "year must be a valid u16 number")?; + .parse::() + .map_err(|_| "year must be a valid u32 number")?; // If year is 68 or smaller, then 2000 is added to it; otherwise, if year // is less than 100, then 1900 is added to it. @@ -34,13 +36,8 @@ pub(super) fn year_from_str(year_str: &str) -> Result { } } - // 2147485547 is the maximum value accepted by GNU, but chrono only - // behaves like GNU for years in the range: [0, 9999], so we keep in the - // range [0, 9999]. - // - // See discussion in https://github.com/uutils/parse_datetime/issues/160. - if year > 9999 { - return Err("year must be no greater than 9999"); + if year > GNU_MAX_YEAR { + return Err("year must be no greater than 2147485547"); } Ok(year) @@ -57,18 +54,20 @@ mod tests { #[test] fn test_year() { // 2-characters are converted to 19XX/20XX - assert_eq!(year_from_str("10").unwrap(), 2010u16); - assert_eq!(year_from_str("68").unwrap(), 2068u16); - assert_eq!(year_from_str("69").unwrap(), 1969u16); - assert_eq!(year_from_str("99").unwrap(), 1999u16); + assert_eq!(year_from_str("10").unwrap(), 2010u32); + assert_eq!(year_from_str("68").unwrap(), 2068u32); + assert_eq!(year_from_str("69").unwrap(), 1969u32); + assert_eq!(year_from_str("99").unwrap(), 1999u32); // 3,4-characters are converted verbatim - assert_eq!(year_from_str("468").unwrap(), 468u16); - assert_eq!(year_from_str("469").unwrap(), 469u16); - assert_eq!(year_from_str("1568").unwrap(), 1568u16); - assert_eq!(year_from_str("1569").unwrap(), 1569u16); + assert_eq!(year_from_str("468").unwrap(), 468u32); + assert_eq!(year_from_str("469").unwrap(), 469u32); + assert_eq!(year_from_str("1568").unwrap(), 1568u32); + assert_eq!(year_from_str("1569").unwrap(), 1569u32); - // years greater than 9999 are not accepted - assert!(year_from_str("10000").is_err()); + // very large years are accepted up to GNU's upper bound + assert_eq!(year_from_str("10000").unwrap(), 10000u32); + assert_eq!(year_from_str("2147485547").unwrap(), 2_147_485_547u32); + assert!(year_from_str("2147485548").is_err()); } }