diff options
-rw-r--r-- | CHANGES.md | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/lib.rs | 46 | ||||
-rw-r--r-- | tests/tests.rs | 65 |
4 files changed, 109 insertions, 5 deletions
@@ -17,3 +17,4 @@ * added March 5th, the fifth, Friday the 13th, etc. * added period before/after/around time * added noon and midnight +* added `<count>` `<periods>` from now/ago @@ -33,5 +33,7 @@ Some expressions it can handle: * noon on May 6, 1969 * midnight on May 6, 1969 * Friday the 13th +* 2 weeks ago +* ten seconds from now The complete API is available at https://docs.rs/two_timer/0.1.0/two_timer/. @@ -207,7 +207,10 @@ lazy_static! { moment_or_period => <moment> | <period> period => <named_period> | <specific_period> - specific_period => <modified_period> | <month_and_year> | <year> + specific_period => <modified_period> | <month_and_year> | <year> | <relative_period> + relative_period -> <count> <displacement> <from_now_or_ago> + displacement => [["week", "day", "hour", "minute", "second"]] ("s")? // not handling variable-width periods like months or years + from_now_or_ago => [["from now", "ago"]] month_and_year -> <a_month> <year> named_period => <a_day> | <a_month> modified_period -> <modifier> <modifiable_period> @@ -249,8 +252,8 @@ lazy_static! { o_n_day => <n_day> | <o_day> at_time -> ("at") <time> at_time_on -> ("at")? <time> ("on")? - time -> <hour_12> <am_pm>? | <hour_24> | <canonical_time> - canonical_time => [["noon", "midnight"]] + time -> <hour_12> <am_pm>? | <hour_24> | <named_time> + named_time => [["noon", "midnight"]] hour_24 => <h24> hour_24 => <h24> (":") <minute> hour_24 => <h24> (":") <minute> (":") <second> @@ -785,6 +788,41 @@ fn handle_specific_period( moment: &Match, config: &Config, ) -> Result<(NaiveDateTime, NaiveDateTime), TimeError> { + if let Some(moment) = moment.name("relative_period") { + let count = count(moment.name("count").unwrap()) as i64; + let (displacement, period) = match moment + .name("displacement") + .unwrap() + .as_str() + .chars() + .nth(0) + .unwrap() + { + 'w' | 'W' => (Duration::weeks(count), Period::Week), + 'd' | 'D' => (Duration::days(count), Period::Day), + 'h' | 'H' => (Duration::hours(count), Period::Hour), + 'm' | 'M' => (Duration::minutes(count), Period::Minute), + 's' | 'S' => (Duration::seconds(count), Period::Second), + _ => unreachable!(), + }; + let d = match moment + .name("from_now_or_ago") + .unwrap() + .as_str() + .chars() + .nth(0) + .unwrap() + { + 'a' | 'A' => config.now - displacement, + 'f' | 'F' => config.now + displacement, + _ => unreachable!(), + }; + let span = match period { + Period::Week => (d, d + Duration::weeks(1)), + _ => moment_to_period(d, &period, config) + }; + return Ok(span); + } if let Some(moment) = moment.name("month_and_year") { let y = year(moment, &config.now); let m = a_month(moment); @@ -1187,7 +1225,7 @@ fn a_month(m: &Match) -> u32 { // extract hour, minute, and second from time match // last parameter is basically whether the value returned is for "midnight", which requires special handling fn time(m: &Match) -> (u32, Option<u32>, Option<u32>, bool) { - if let Some(m) = m.name("canonical_time") { + if let Some(m) = m.name("named_time") { return match m.as_str().chars().nth(0).unwrap() { 'n' | 'N' => (12, None, None, false), _ => (0, None, None, true), diff --git a/tests/tests.rs b/tests/tests.rs index 51d466c..67d9fe5 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1331,4 +1331,67 @@ fn midnight() { assert!(false, "didn't match"); } } -}
\ No newline at end of file +} + +#[derive(Debug)] +enum Period { + Week, + Day, + Hour, + Minute, + Second, +} + +#[test] +fn displacement() { + let displacements = [ + ("week", Period::Week), + ("day", Period::Day), + ("hour", Period::Hour), + ("minute", Period::Minute), + ("second", Period::Second), + ]; + let now = NaiveDate::from_ymd(1969, 5, 10).and_hms(0, 0, 0); + for (phrase, period) in displacements.iter() { + for n in [1, 2, 3].iter() { + let phrase = if *n == 1 { + String::from(*phrase) + } else { + String::from(*phrase) + "s" + }; + let (displacement1, displacement2) = match period { + Period::Week => (Duration::weeks(*n), Duration::weeks(1)), + Period::Day => (Duration::days(*n), Duration::days(1)), + Period::Hour => (Duration::hours(*n), Duration::hours(1)), + Period::Minute => (Duration::minutes(*n), Duration::minutes(1)), + _ => (Duration::seconds(*n), Duration::seconds(1)), + }; + let d1 = now - displacement1; + let d2 = d1 + displacement2; + let expression = format!("{} {} ago", n, phrase); + match parse(&expression, Some(Config::new().now(now))) { + Ok((start, end, _)) => { + assert_eq!(d1, start); + assert_eq!(d2, end); + } + Err(e) => { + println!("{:?}", e); + assert!(false, "didn't match"); + } + } + let d1 = now + displacement1; + let d2 = d1 + displacement2; + let expression = format!("{} {} from now", n, phrase); + match parse(&expression, Some(Config::new().now(now))) { + Ok((start, end, _)) => { + assert_eq!(d1, start); + assert_eq!(d2, end); + } + Err(e) => { + println!("{:?}", e); + assert!(false, "didn't match"); + } + } + } + } +} |