summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordfhoughton <dfhoughton@gmail.com>2020-03-07 14:05:02 -0500
committerdfhoughton <dfhoughton@gmail.com>2020-03-07 14:05:02 -0500
commit38518e3450f7887d3997fb809a3f437e2e1ecd98 (patch)
tree64a5ae83a8489ae1a411df59b6c963a7e28306df
parentb41efb6ccc6174b6c0d2cd05f2b42c1ce8b653e8 (diff)
downloadtwo-timer-38518e3450f7887d3997fb809a3f437e2e1ecd98.zip
various fixes and changes -- see CHANGES.md
-rw-r--r--CHANGES.md6
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--src/lib.rs61
-rw-r--r--tests/tests.rs89
5 files changed, 99 insertions, 61 deletions
diff --git a/CHANGES.md b/CHANGES.md
index dc0f297..9a60268 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,11 @@
# Change Log
+## 2.0.0 *2020-3-7*
+* fixing specific time to specific time pattern: "noon yesterday through midnight today"
+* allow parsing of hours with leading 0; e.g., "08:57:29"
+* added "month the nth" pattern -- "July the 4th", "November the Fifth"
+* ***IMPORTANT*** changing the nature of daytimes -- "3 PM", "13:14" -- so their period is always 1 second; this seems
+more intuitively correct to me, but it changes the semantics sufficiently that I thought it necessary to bump the major version number
## 1.3.4
* fixed panic when parsing "24"
## 1.3.3
diff --git a/Cargo.lock b/Cargo.lock
index e45eee4..e76be6a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -171,7 +171,7 @@ dependencies = [
[[package]]
name = "two_timer"
-version = "1.3.4"
+version = "2.0.0"
dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index 5691088..e167a3f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "two_timer"
-version = "1.3.4"
+version = "2.0.0"
authors = ["dfhoughton <dfhoughton@gmail.com>"]
description="parser for English time expressions"
homepage="https://github.com/dfhoughton/two-timer"
diff --git a/src/lib.rs b/src/lib.rs
index bc54e38..e85c886 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -327,7 +327,7 @@ lazy_static! {
o_day => <n_ordinal> | <a_ordinal> | <roman>
day_and_month -> <n_month> r("[./-]") <n_day> // 5-6
- day_and_month -> <a_month> <o_n_day> // June 5, June 5th, June fifth
+ day_and_month -> <a_month> ("the")? <o_n_day> // June 5, June 5th, June fifth, June the fifth
day_and_month -> ("the") <o_day> ("of") <a_month> // the 5th of June, the fifth of June
o_n_day => <n_day> | <o_day>
@@ -345,7 +345,7 @@ lazy_static! {
displacement => [["week", "day", "hour", "minute", "second"]] ("s")? // not handling variable-width periods like months or years
from_now_or_ago => [["from now", "ago"]]
h12 => (?-B) [(1..=12).into_iter().collect::<Vec<_>>()]
- h24 => [(1..=24).into_iter().collect::<Vec<_>>()]
+ h24 => [(1..=24).into_iter().flat_map(|i| vec![format!("{}", i), format!("{:02}", i)]).collect::<Vec<_>>()]
minute => (?-B) [ (0..60).into_iter().map(|i| format!("{:02}", i)).collect::<Vec<_>>() ]
modifier => [["this", "last", "next"]]
named_time => [["noon", "midnight"]]
@@ -825,9 +825,7 @@ impl TimeError {
// for the end time, if the span is less than a day, use the first, otherwise use the second
// e.g., Monday through Friday at 3 PM should end at 3 PM, but Monday through Friday should end at the end of Friday
fn pick_terminus(d1: NaiveDateTime, d2: NaiveDateTime, through: bool) -> NaiveDateTime {
- if d1.day() == d2.day() && d1.month() == d2.month() && d1.year() == d2.year() {
- d1
- } else if through {
+ if through {
d2
} else {
d1
@@ -1188,22 +1186,15 @@ fn handle_specific_time(
Err(s) => Err(s),
Ok(d) => {
let (hour, minute, second, _) = time(moment);
- let period = if second.is_some() {
- Period::Second
- } else if minute.is_some() {
- Period::Minute
- } else {
- Period::Hour
- };
let m = d
.and_hms(0, 0, 0)
.with_hour(hour)
.unwrap()
- .with_minute(minute.unwrap_or(0))
+ .with_minute(minute)
.unwrap()
- .with_second(second.unwrap_or(0))
+ .with_second(second)
.unwrap();
- Ok(moment_to_period(m, &period, config))
+ Ok(moment_to_period(m, &Period::Second, config))
}
};
}
@@ -1237,25 +1228,18 @@ fn handle_one_time(
fn moment_and_time(config: &Config, daytime: Option<&Match>) -> (NaiveDateTime, NaiveDateTime) {
if let Some(daytime) = daytime {
let (hour, minute, second, is_midnight) = time(daytime);
- let period = if second.is_some() {
- Period::Second
- } else if minute.is_some() {
- Period::Minute
- } else {
- Period::Hour
- };
let mut m = config
.now
.with_hour(hour)
.unwrap()
- .with_minute(minute.unwrap_or(0))
+ .with_minute(minute)
.unwrap()
- .with_second(second.unwrap_or(0))
+ .with_second(second)
.unwrap();
if is_midnight {
m = m + Duration::days(1); // midnight is second 0 *of the next day*
}
- moment_to_period(m, &period, config)
+ moment_to_period(m, &Period::Second, config)
} else {
moment_to_period(config.now, &config.period, config)
}
@@ -1294,19 +1278,12 @@ fn relative_moment(
}
if let Some(t) = m.name("time") {
let (hour, minute, second, is_midnight) = time(t);
- let period = if second.is_some() {
- Period::Second
- } else if minute.is_some() {
- Period::Minute
- } else {
- Period::Hour
- };
let mut t = other_time
.with_hour(hour)
.unwrap()
- .with_minute(minute.unwrap_or(0))
+ .with_minute(minute)
.unwrap()
- .with_second(second.unwrap_or(0))
+ .with_second(second)
.unwrap();
if is_midnight {
t = t + Duration::days(1); // midnight is second 0 *of the next day*
@@ -1316,7 +1293,7 @@ fn relative_moment(
} else if !before && t < *other_time {
t = t + Duration::days(1);
}
- return Ok(moment_to_period(t, &period, config));
+ return Ok(moment_to_period(t, &Period::Second, config));
}
if let Some(month) = m.name("a_month") {
let month = a_month(month);
@@ -1427,7 +1404,7 @@ fn specific_moment(
m: &Match,
config: &Config,
) -> Result<(NaiveDateTime, NaiveDateTime), TimeError> {
- if let Some(m) = m.name("specific_day") {
+ if m.has("specific_day") {
return handle_specific_day(m, config);
}
if let Some(m) = m.name("specific_period") {
@@ -1462,11 +1439,11 @@ 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) {
+fn time(m: &Match) -> (u32, u32, u32, bool) {
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),
+ 'n' | 'N' => (12, 0, 0, false),
+ _ => (0, 0, 0, true),
};
}
let hour = if let Some(hour_24) = m.name("hour_24") {
@@ -1498,12 +1475,12 @@ fn time(m: &Match) -> (u32, Option<u32>, Option<u32>, bool) {
let minute = s_to_n(minute.as_str());
if let Some(second) = m.name("second") {
let second = s_to_n(second.as_str());
- (hour, Some(minute), Some(second), false)
+ (hour, minute, second, false)
} else {
- (hour, Some(minute), None, false)
+ (hour, minute, 0, false)
}
} else {
- (hour, None, None, false)
+ (hour, 0, 0, false)
}
}
diff --git a/tests/tests.rs b/tests/tests.rs
index a814899..ae1126e 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -88,7 +88,7 @@ fn day_5_6_69_at_3_30_pm() {
{
let (start, end, _) = parse(phrase, None).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::minutes(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
}
@@ -105,7 +105,7 @@ fn day_5_6_69_at_3_pm() {
{
let (start, end, _) = parse(phrase, None).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::hours(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
}
@@ -167,7 +167,7 @@ fn at_3_pm() {
for phrase in ["3 PM", "3 pm", "15"].iter() {
let (start, end, _) = parse(phrase, Some(Config::new().now(now))).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::hours(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
}
@@ -178,7 +178,7 @@ fn at_3_00_pm() {
for phrase in ["3:00 PM", "3:00 pm", "15:00"].iter() {
let (start, end, _) = parse(phrase, Some(Config::new().now(now))).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::minutes(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
}
@@ -200,7 +200,7 @@ fn at_3_pm_yesterday() {
for phrase in ["3 PM yesterday", "3 pm yesterday", "15 yesterday"].iter() {
let (start, end, _) = parse(phrase, Some(Config::new().now(now))).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::hours(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
}
@@ -776,7 +776,7 @@ fn friday_at_3_pm() {
let then = NaiveDate::from_ymd(1969, 5, 2).and_hms(15, 0, 0);
let (start, end, _) = parse("Friday at 3 pm", Some(Config::new().now(now))).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::hours(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
#[test]
@@ -785,7 +785,7 @@ fn tuesday_at_3_pm() {
let then = NaiveDate::from_ymd(1969, 4, 29).and_hms(15, 0, 0);
let (start, end, _) = parse("Tuesday at 3 pm", Some(Config::new().now(now))).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::hours(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
#[test]
@@ -794,7 +794,7 @@ fn monday_at_3_pm() {
let then = NaiveDate::from_ymd(1969, 5, 5).and_hms(15, 0, 0);
let (start, end, _) = parse("Monday at 3 pm", Some(Config::new().now(now))).unwrap();
assert_eq!(then, start);
- assert_eq!(then + Duration::hours(1), end);
+ assert_eq!(then + Duration::seconds(1), end);
}
#[test]
@@ -851,7 +851,7 @@ fn tuesday_through_friday() {
fn tuesday_through_3_pm_on_friday() {
let now = NaiveDate::from_ymd(1969, 5, 6).and_hms(0, 0, 0);
let d1 = NaiveDate::from_ymd(1969, 4, 29).and_hms(0, 0, 0);
- let d2 = NaiveDate::from_ymd(1969, 5, 2).and_hms(15, 0, 0);
+ let d2 = NaiveDate::from_ymd(1969, 5, 2).and_hms(15, 0, 1);
let (start, end, _) = parse(
"Tuesday through 3 PM on Friday",
Some(Config::new().now(now)),
@@ -872,6 +872,56 @@ fn this_year_through_today() {
}
#[test]
+fn noon_yesterday_through_midnight_today() {
+ let now = NaiveDate::from_ymd(1969, 5, 6).and_hms(0, 0, 0);
+ let d1 = NaiveDate::from_ymd(1969, 5, 5).and_hms(12, 0, 0);
+ let d2 = NaiveDate::from_ymd(1969, 5, 7).and_hms(0, 0, 1);
+ let (start, end, _) = parse(
+ "noon yesterday through midnight today",
+ Some(Config::new().now(now)),
+ )
+ .unwrap();
+ assert_eq!(d1, start);
+ assert_eq!(d2, end);
+}
+
+#[test]
+fn very_specific_through_very_specific() {
+ let d1 = NaiveDate::from_ymd(2014, 10, 6).and_hms(8, 57, 29);
+ let d2 = NaiveDate::from_ymd(2020, 3, 6).and_hms(17, 28, 34);
+ let (start, end, _) = parse("2014-10-06 08:57:29 - 2020-03-06 17:28:33", None).unwrap();
+ assert_eq!(d1, start);
+ assert_eq!(d2, end);
+}
+
+#[test]
+fn very_specific_up_to_very_specific() {
+ let d1 = NaiveDate::from_ymd(2014, 10, 6).and_hms(8, 57, 29);
+ let d2 = NaiveDate::from_ymd(2020, 3, 6).and_hms(17, 28, 33);
+ let (start, end, _) = parse("2014-10-06 08:57:29 up to 2020-03-06 17:28:33", None).unwrap();
+ assert_eq!(d1, start);
+ assert_eq!(d2, end);
+}
+
+#[test]
+fn somewhat_specific_through_somewhat_specific() {
+ let d1 = NaiveDate::from_ymd(2014, 10, 6).and_hms(8, 57, 00);
+ let d2 = NaiveDate::from_ymd(2020, 3, 6).and_hms(17, 28, 01);
+ let (start, end, _) = parse("2014-10-06 08:57 - 2020-03-06 17:28", None).unwrap();
+ assert_eq!(d1, start);
+ assert_eq!(d2, end);
+}
+
+#[test]
+fn somewhat_specific_up_to_somewhat_specific() {
+ let d1 = NaiveDate::from_ymd(2014, 10, 6).and_hms(8, 57, 00);
+ let d2 = NaiveDate::from_ymd(2020, 3, 6).and_hms(17, 28, 00);
+ let (start, end, _) = parse("2014-10-06 08:57 up to 2020-03-06 17:28", None).unwrap();
+ assert_eq!(d1, start);
+ assert_eq!(d2, end);
+}
+
+#[test]
fn april_3_25_bc() {
let d1 = NaiveDate::from_ymd(-24, 4, 3).and_hms(0, 0, 0);
let d2 = d1 + Duration::days(1);
@@ -1084,7 +1134,7 @@ fn next_weekend_on_saturday_when_sunday_starts_week() {
#[test]
fn regression_12pm() {
let d1 = NaiveDate::from_ymd(2018, 5, 21).and_hms(0, 0, 0);
- let d2 = NaiveDate::from_ymd(2018, 5, 21).and_hms(1, 0, 0);
+ let d2 = d1 + Duration::seconds(1);
if let Ok((start, end, _)) = parse("12 pm on May 21, 2018", None) {
assert_eq!(d1, start);
assert_eq!(d2, end);
@@ -1241,7 +1291,14 @@ fn day_and_month() {
let now = NaiveDate::from_ymd(1969, 5, 10).and_hms(0, 0, 0);
let d1 = NaiveDate::from_ymd(1969, 5, 15).and_hms(0, 0, 0);
let d2 = d1 + Duration::days(1);
- let patterns = ["the ides of May", "5-15", "the fifteenth", "May fifteenth"];
+ let patterns = [
+ "the ides of May",
+ "5-15",
+ "the fifteenth",
+ "May fifteenth",
+ "May the 15th",
+ "May the fifteenth",
+ ];
for p in patterns.iter() {
match parse(p, Some(Config::new().now(now))) {
Ok((start, end, _)) => {
@@ -1360,7 +1417,7 @@ fn number_before_test() {
#[test]
fn noon() {
let d1 = NaiveDate::from_ymd(1969, 5, 6).and_hms(12, 0, 0);
- let d2 = d1 + Duration::hours(1);
+ let d2 = d1 + Duration::seconds(1);
match parse("noon on May 6, 1969", None) {
Ok((start, end, _)) => {
assert_eq!(d1, start);
@@ -1376,7 +1433,7 @@ fn noon() {
#[test]
fn midnight() {
let d1 = NaiveDate::from_ymd(1969, 5, 7).and_hms(0, 0, 0);
- let d2 = d1 + Duration::hours(1);
+ let d2 = d1 + Duration::seconds(1);
match parse("midnight on May 6, 1969", None) {
Ok((start, end, _)) => {
assert_eq!(d1, start);
@@ -1501,7 +1558,7 @@ fn specific_time() {
#[test]
fn no_space_before_pm() {
let d1 = NaiveDate::from_ymd(1969, 5, 6).and_hms(13, 0, 0);
- let d2 = d1 + Duration::hours(1);
+ let d2 = d1 + Duration::seconds(1);
match parse("1969-05-06 at 1PM", None) {
Ok((start, end, _)) => {
assert_eq!(d1, start);
@@ -1512,7 +1569,6 @@ fn no_space_before_pm() {
assert!(false, "didn't match");
}
}
- let d2 = d1 + Duration::minutes(1);
match parse("1969-05-06 at 1:00PM", None) {
Ok((start, end, _)) => {
assert_eq!(d1, start);
@@ -1523,7 +1579,6 @@ fn no_space_before_pm() {
assert!(false, "didn't match");
}
}
- let d2 = d1 + Duration::seconds(1);
match parse("1969-05-06 at 1:00:00PM", None) {
Ok((start, end, _)) => {
assert_eq!(d1, start);
@@ -1540,4 +1595,4 @@ fn no_space_before_pm() {
fn relative_time_regression() {
parse("24", None).unwrap();
assert!(true, "'24' didn't cause a panic");
-} \ No newline at end of file
+}