summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md1
-rw-r--r--README.md2
-rw-r--r--src/lib.rs46
-rw-r--r--tests/tests.rs65
4 files changed, 109 insertions, 5 deletions
diff --git a/CHANGES.md b/CHANGES.md
index c7114a0..e7c3b2b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -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
diff --git a/README.md b/README.md
index 3a90a26..11d8314 100644
--- a/README.md
+++ b/README.md
@@ -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/.
diff --git a/src/lib.rs b/src/lib.rs
index 479c000..96d3fa4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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");
+ }
+ }
+ }
+ }
+}