Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/items/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
}
Expand Down
16 changes: 8 additions & 8 deletions src/items/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ use super::{
pub(crate) struct Date {
pub(crate) day: u8,
pub(crate) month: u8,
pub(crate) year: Option<u16>,
pub(crate) year: Option<u32>,
}

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,
Expand Down Expand Up @@ -118,12 +118,12 @@ impl TryFrom<Date> for jiff::civil::Date {
type Error = &'static str;

fn try_from(date: Date) -> Result<Self, Self::Error> {
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")
}
}

Expand Down
8 changes: 2 additions & 6 deletions src/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
39 changes: 19 additions & 20 deletions src/items/year.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u16, &'static str> {
pub(super) fn year_from_str(year_str: &str) -> Result<u32, &'static str> {
let mut year = year_str
.parse::<u16>()
.map_err(|_| "year must be a valid u16 number")?;
.parse::<u32>()
.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.
Expand All @@ -34,13 +36,8 @@ pub(super) fn year_from_str(year_str: &str) -> Result<u16, &'static str> {
}
}

// 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)
Expand All @@ -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());
}
}