From f24b5edb3b40ad5df9ec36c81c3c0af96c5729b2 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 10 May 2023 23:08:05 +0200 Subject: [PATCH 1/4] Add a new from_str_at_date(date, string) function --- README.md | 14 ++++++++-- src/lib.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++-- tests/simple.rs | 32 ++++++++++++++++++++-- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2ad79de..667d24c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A Rust crate for parsing human-readable relative time strings and converting the - Parses a variety of human-readable time formats. - Supports positive and negative durations. - Allows for chaining time units (e.g., "1 hour 2 minutes" or "2 days and 2 hours"). +- Calculate durations relative to a specified date. ## Usage @@ -21,18 +22,25 @@ Add this to your `Cargo.toml`: humantime_to_duration = "0.1.1" ``` -Then, import the crate and use the from_str function: +Then, import the crate and use the `from_str` and `from_str_at_date` functions: ``` use humantime_to_duration::from_str; use time::Duration; let duration = from_str("+3 days"); assert_eq!(duration.unwrap(), Duration::days(3)); + +let today = OffsetDateTime::now_utc().date(); +let yesterday = today - Duration::days(1); +assert_eq!( + from_str_at_date(yesterday, "2 days").unwrap(), + Duration::days(1) +); ``` ### Supported Formats -The `from_str` function supports the following formats for relative time: +The `from_str` and `from_str_at_date` functions support the following formats for relative time: - `num` `unit` (e.g., "-1 hour", "+3 days") - `unit` (e.g., "hour", "day") @@ -47,7 +55,7 @@ The `from_str` function supports the following formats for relative time: ## Return Values -The `from_str` function returns: +The `from_str` and `from_str_at_date` functions return: - `Ok(Duration)` - If the input string can be parsed as a relative time - `Err(ParseDurationError)` - If the input string cannot be parsed as a relative time diff --git a/src/lib.rs b/src/lib.rs index 4ff1e52..85b3167 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ use regex::{Error as RegexError, Regex}; use std::error::Error; use std::fmt::{self, Display}; -use time::Duration; +use time::{Date, Duration, OffsetDateTime}; #[derive(Debug)] pub enum ParseDurationError { @@ -77,6 +77,17 @@ impl From for ParseDurationError { /// /// This function will return `Err(ParseDurationError::InvalidInput)` if the input string /// cannot be parsed as a relative time. +/// +/// # Examples +/// +/// ``` +/// use time::Duration; +/// use humantime_to_duration::{from_str, ParseDurationError}; +/// +/// assert_eq!(from_str("1 hour, 30 minutes").unwrap(), Duration::minutes(90)); +/// assert_eq!(from_str("tomorrow").unwrap(), Duration::days(1)); +/// assert!(matches!(from_str("invalid"), Err(ParseDurationError::InvalidInput))); +/// ``` pub fn from_str(s: &str) -> Result { let time_pattern: Regex = Regex::new( r"(?x) @@ -153,12 +164,42 @@ pub fn from_str(s: &str) -> Result { } } +/// Parses a duration string and returns a `Duration` instance, with the duration +/// calculated from the specified date. +/// +/// # Arguments +/// +/// * `date` - A `Date` instance representing the base date for the calculation +/// * `s` - A string slice representing the relative time. +/// +/// # Examples +/// +/// ``` +/// use time::{Date, Duration, OffsetDateTime}; +/// use humantime_to_duration::{from_str_at_date, ParseDurationError}; +/// let today = OffsetDateTime::now_utc().date(); +/// let yesterday = today - Duration::days(1); +/// assert_eq!( +/// from_str_at_date(yesterday, "2 days").unwrap(), +/// Duration::days(1) // 1 day from the specified date + 1 day from the input string +/// ); +/// ``` +pub fn from_str_at_date(date: Date, s: &str) -> Result { + let time_now = OffsetDateTime::now_utc().date(); + let date_duration = date - time_now; + + match from_str(s) { + Ok(duration) => Ok(duration + date_duration), + Err(error) => Err(error), + } +} + #[cfg(test)] mod tests { - use super::from_str; use super::ParseDurationError; - use time::Duration; + use super::{from_str, from_str_at_date}; + use time::{Date, Duration, Month, OffsetDateTime}; #[test] fn test_years() { @@ -300,4 +341,30 @@ mod tests { _ => assert!(false), }*/ } + + #[test] + fn test_from_str_at_date() { + let date = Date::from_calendar_date(2014, Month::September, 5).unwrap(); + let now = OffsetDateTime::now_utc().date(); + let days_diff = (date - now).whole_days(); + + assert_eq!( + from_str_at_date(date, "1 day").unwrap(), + Duration::days(days_diff + 1) + ); + + assert_eq!( + from_str_at_date(date, "2 hours").unwrap(), + Duration::days(days_diff) + Duration::hours(2) + ); + } + + #[test] + fn test_invalid_input_at_date() { + let date = Date::from_calendar_date(2014, Month::September, 5).unwrap(); + assert!(matches!( + from_str_at_date(date, "invalid"), + Err(ParseDurationError::InvalidInput) + )); + } } diff --git a/tests/simple.rs b/tests/simple.rs index 3cf9b24..e81dd58 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -1,5 +1,5 @@ -use humantime_to_duration::{from_str, ParseDurationError}; -use time::Duration; +use humantime_to_duration::{from_str, from_str_at_date, ParseDurationError}; +use time::{Duration, OffsetDateTime}; #[test] fn test_invalid_input() { @@ -135,3 +135,31 @@ fn test_display_should_fail() { "Invalid input string: cannot be parsed as a relative time" ); } + +#[test] +fn test_from_str_at_date_day() { + let today = OffsetDateTime::now_utc().date(); + let yesterday = today - Duration::days(1); + assert_eq!( + from_str_at_date(yesterday, "2 days").unwrap(), + Duration::days(1) + ); +} + +#[test] +fn test_invalid_input_at_date() { + let today = OffsetDateTime::now_utc().date(); + let yesterday = today - Duration::days(1); + let result = from_str_at_date(yesterday, "foobar"); + println!("{result:?}"); + match result { + Err(ParseDurationError::InvalidInput) => assert!(true), + _ => assert!(false), + } + + let result = from_str_at_date(yesterday, "invalid 1r"); + match result { + Err(ParseDurationError::InvalidInput) => assert!(true), + _ => assert!(false), + } +} From 4bacbbb448777c2bf9018286d95a34f252ec6bb0 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 11 May 2023 15:11:31 +0200 Subject: [PATCH 2/4] Also include from_str_at_date in the readme.md Co-authored-by: Daniel Hofstetter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 667d24c..be4e64d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ humantime_to_duration = "0.1.1" Then, import the crate and use the `from_str` and `from_str_at_date` functions: ``` -use humantime_to_duration::from_str; +use humantime_to_duration::{from_str, from_str_at_date}; use time::Duration; let duration = from_str("+3 days"); From d7f97529fb7abda364b2f0c1c84a599eaf0bd88f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 11 May 2023 16:47:37 +0200 Subject: [PATCH 3/4] Simplify the code Co-authored-by: Daniel Hofstetter --- src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 85b3167..90661c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,10 +188,7 @@ pub fn from_str_at_date(date: Date, s: &str) -> Result Ok(duration + date_duration), - Err(error) => Err(error), - } + Ok(from_str(s)? + date_duration) } #[cfg(test)] From 5c9e48550ceddfc4d23e6978fa4a93a2b6d3a6a4 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 11 May 2023 16:48:02 +0200 Subject: [PATCH 4/4] Simplify the test Co-authored-by: Daniel Hofstetter --- tests/simple.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/simple.rs b/tests/simple.rs index e81dd58..43015ee 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -149,15 +149,14 @@ fn test_from_str_at_date_day() { #[test] fn test_invalid_input_at_date() { let today = OffsetDateTime::now_utc().date(); - let yesterday = today - Duration::days(1); - let result = from_str_at_date(yesterday, "foobar"); + let result = from_str_at_date(today, "foobar"); println!("{result:?}"); match result { Err(ParseDurationError::InvalidInput) => assert!(true), _ => assert!(false), } - let result = from_str_at_date(yesterday, "invalid 1r"); + let result = from_str_at_date(today, "invalid 1r"); match result { Err(ParseDurationError::InvalidInput) => assert!(true), _ => assert!(false),