Skip to content

Commit

Permalink
Add test_month to test relative dates
Browse files Browse the repository at this point in the history
  • Loading branch information
dhilst committed Sep 2, 2024
1 parent cc7e40b commit 7592100
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 43 deletions.
77 changes: 41 additions & 36 deletions src/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ where

// Parse an item
pub fn parse_one(input: &mut &str) -> PResult<Item> {
// eprintln!("--- > parse_one {input}");
eprintln!("--- > parse_one {input}");
let result = alt((
combined::parse.map(Item::DateTime),
date::parse.map(Item::Date),
Expand All @@ -151,7 +151,7 @@ pub fn parse_one(input: &mut &str) -> PResult<Item> {
))
.parse_next(input)?;

//eprintln!("--- < parse_one {input} {result:?}");
eprintln!("--- < parse_one {input} {result:?}");
Ok(result)
}

Expand All @@ -169,7 +169,6 @@ fn with_timezone_restore(
) -> Option<DateTime<FixedOffset>> {
let offset: FixedOffset = chrono::FixedOffset::from(offset);
let copy = at;
//eprintln!("with_timezone {at} {offset}");
let x = at
.with_timezone(&offset)
.with_day(copy.day())?
Expand All @@ -181,14 +180,11 @@ fn with_timezone_restore(
Some(x)
}

pub(crate) fn at_date(
date: Vec<Item>,
mut d: DateTime<FixedOffset>,
) -> Result<DateTime<FixedOffset>, ParseDateTimeError> {
d = d.with_hour(0).unwrap_or(d);
d = d.with_minute(0).unwrap_or(d);
d = d.with_second(0).unwrap_or(d);
d = d.with_nanosecond(0).unwrap_or(d);
fn at_date_inner(date: Vec<Item>, mut d: DateTime<FixedOffset>) -> Option<DateTime<FixedOffset>> {
d = d.with_hour(0).unwrap();
d = d.with_minute(0).unwrap();
d = d.with_second(0).unwrap();
d = d.with_nanosecond(0).unwrap();

for item in date {
match item {
Expand All @@ -199,13 +195,12 @@ pub(crate) fn at_date(
.with_timezone(&d.timezone())
}
Item::Date(date::Date { day, month, year }) => {
d = d.with_day(day).unwrap_or(d);
d = d.with_month(month).unwrap_or(d);
d = year
// converts i32 to u32 safely
.and_then(|year: u32| <u32 as TryInto<i32>>::try_into(year).ok())
.and_then(|year| d.with_year(year))
.unwrap_or(d);
d = d.with_day(day)?;
d = d.with_month(month)?;
if let Some(year) = year {
let y = year as i32;
d = d.with_year(y)?
}
}
Item::DateTime(combined::DateTime {
date: date::Date { day, month, year },
Expand All @@ -222,16 +217,14 @@ pub(crate) fn at_date(
let offset: FixedOffset = chrono::FixedOffset::from(offset);
d = d.with_timezone(&offset);
}
d = d.with_day(day).unwrap_or(d);
d = d.with_month(month).unwrap_or(d);
d = year
// converts i32 to u32 safely
.and_then(|year: u32| <u32 as TryInto<i32>>::try_into(year).ok())
.and_then(|year| d.with_year(year))
.unwrap_or(d);
d = d.with_hour(hour).unwrap_or(d);
d = d.with_minute(minute).unwrap_or(d);
d = d.with_second(second as u32).unwrap_or(d);
d = d.with_day(day)?;
d = d.with_month(month)?;
d = d.with_hour(hour)?;
d = d.with_minute(minute)?;
d = d.with_second(second as u32)?;
if let Some(year) = year {
d = d.with_year(year as i32)?
}
}
Item::Year(year) => d = d.with_year(year as i32).unwrap_or(d),
Item::Time(time::Time {
Expand All @@ -242,11 +235,11 @@ pub(crate) fn at_date(
}) => {
if let Some(offset) = offset {
// timezone overflowed
d = with_timezone_restore(offset, d).ok_or(ParseDateTimeError::InvalidInput)?;
d = with_timezone_restore(offset, d)?;
}
d = d.with_hour(hour).unwrap_or(d);
d = d.with_minute(minute).unwrap_or(d);
d = d.with_second(second as u32).unwrap_or(d);
d = d.with_hour(hour)?;
d = d.with_minute(minute)?;
d = d.with_second(second as u32)?;
}
Item::Weekday(weekday::Weekday {
offset: _, // TODO: use the offset
Expand All @@ -269,9 +262,14 @@ pub(crate) fn at_date(

d = beginning_of_day
}
Item::Relative(relative::Relative::Years(x)) => d = d.with_year(x).unwrap_or(d),
Item::Relative(relative::Relative::Years(x)) => {
d = d.with_year(d.year() + x)?;
}
Item::Relative(relative::Relative::Months(x)) => {
d = d.with_month(x as u32).unwrap_or(d)
d += d
.date_naive()
.checked_add_months(chrono::Months::new(x as u32))?
.signed_duration_since(d.date_naive());
}
Item::Relative(relative::Relative::Days(x)) => d += chrono::Duration::days(x.into()),
Item::Relative(relative::Relative::Hours(x)) => d += chrono::Duration::hours(x.into()),
Expand All @@ -283,12 +281,19 @@ pub(crate) fn at_date(
d += chrono::Duration::seconds(x as i64);
}
Item::TimeZone(offset) => {
d = with_timezone_restore(offset, d).ok_or(ParseDateTimeError::InvalidInput)?;
d = with_timezone_restore(offset, d)?;
}
}
}

Ok(d)
Some(d)
}

pub(crate) fn at_date(
date: Vec<Item>,
d: DateTime<FixedOffset>,
) -> Result<DateTime<FixedOffset>, ParseDateTimeError> {
at_date_inner(date, d).ok_or(ParseDateTimeError::InvalidInput)
}

pub(crate) fn at_local(date: Vec<Item>) -> Result<DateTime<FixedOffset>, ParseDateTimeError> {
Expand Down
1 change: 0 additions & 1 deletion src/items/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ impl From<Offset> for chrono::FixedOffset {
) -> Self {
let secs = hours * 3600 + minutes * 60;

eprintln!("------------------> {hours} {minutes} {secs}");
if negative {
FixedOffset::west_opt(secs.try_into().expect("secs overflow"))
.expect("timezone overflow")
Expand Down
49 changes: 43 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,7 @@ mod tests {

#[test]
fn invalid_offset_format() {
let invalid_offsets = vec![
// "+0700", // this is not invalid
// "UTC+2", // this is not invalid
// "Z-1", // this is not invalid, means UTC-1
"UTC+01005", // this is
];
let invalid_offsets = vec!["UTC+01005"];
for offset in invalid_offsets {
assert_eq!(
parse_datetime(offset),
Expand Down Expand Up @@ -405,4 +400,46 @@ mod tests {
assert_eq!(result, Err(ParseDateTimeError::InvalidInput));
}
}

mod test_relative {
use crate::parse_datetime;
use std::env;

#[test]
fn test_month() {
env::set_var("TZ", "UTC");

assert_eq!(
parse_datetime("28 feb + 1 month")
.expect("parse_datetime")
.format("%+")
.to_string(),
"2024-03-28T00:00:00+00:00"
);

// 29 feb 2025 is invalid
assert!(parse_datetime("29 feb + 1 year").is_err());

// 29 feb 2025 is an invalid date
assert!(parse_datetime("29 feb 2025").is_err());

// because 29 feb 2025 is invalid, 29 feb 2025 + 1 day is invalid
// arithmetic does not operate on invalid dates
assert_eq!(
parse_datetime("29 feb 2025 + 1 day")
.unwrap()
.format("%+")
.to_string(),
"2023-03-01T00:00:00+00:00"
);
// 28 feb 2023 + 1 day = 1 mar
assert_eq!(
parse_datetime("28 feb 2023 + 1 day")
.unwrap()
.format("%+")
.to_string(),
"2023-03-01T00:00:00+00:00"
);
}
}
}

0 comments on commit 7592100

Please sign in to comment.