whichtime_sys/parsers/en/
time_unit_later.rs1use crate::components::Component;
4use crate::context::ParsingContext;
5use crate::dictionaries::en::{get_time_unit, parse_number_pattern};
6use crate::error::Result;
7use crate::parsers::Parser;
8use crate::results::ParsedResult;
9use crate::scanner::TokenType;
10use crate::types::{Duration, TimeUnit, add_duration};
11use chrono::Datelike;
12use regex::Regex;
13use std::sync::LazyLock;
14
15static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
16 Regex::new(
17 r"(?i)(\d+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|a|an|the|few|several|couple)\s*(seconds?|minutes?|hours?|days?|weeks?|months?|years?|mins?|hrs?|secs?)\s*(?:later|after|from\s*now|forward|henceforth)\b"
18 ).unwrap()
19});
20
21pub struct TimeUnitLaterParser;
23
24impl Parser for TimeUnitLaterParser {
25 fn name(&self) -> &'static str {
26 "TimeUnitLaterParser"
27 }
28
29 fn should_apply(&self, context: &ParsingContext) -> bool {
30 context.has_token_type(TokenType::Later) && context.has_token_type(TokenType::TimeUnit)
31 }
32
33 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
34 let mut results = Vec::new();
35 let ref_date = context.reference.instant;
36
37 for mat in PATTERN.find_iter(context.text) {
38 let matched_text = mat.as_str();
39 let index = mat.start();
40
41 let Some(caps) = PATTERN.captures(matched_text) else {
42 continue;
43 };
44
45 let num_str = caps.get(1).map(|m| m.as_str()).unwrap_or("1");
46 let unit_str = caps
47 .get(2)
48 .map(|m| m.as_str().to_lowercase())
49 .unwrap_or_default();
50
51 let num = parse_number_pattern(num_str);
52 let Some(unit) = get_time_unit(&unit_str) else {
53 continue;
54 };
55
56 let mut duration = Duration::new();
58 match unit {
59 TimeUnit::Second => duration.second = Some(num),
60 TimeUnit::Minute => duration.minute = Some(num),
61 TimeUnit::Hour => duration.hour = Some(num),
62 TimeUnit::Day => duration.day = Some(num),
63 TimeUnit::Week => duration.week = Some(num),
64 TimeUnit::Month => duration.month = Some(num),
65 TimeUnit::Year => duration.year = Some(num),
66 TimeUnit::Quarter => duration.quarter = Some(num),
67 TimeUnit::Millisecond => duration.millisecond = Some(num),
68 }
69
70 let target_date = add_duration(ref_date, &duration);
71
72 let mut components = context.create_components();
73 components.assign(Component::Year, target_date.year());
74 components.assign(Component::Month, target_date.month() as i32);
75 components.assign(Component::Day, target_date.day() as i32);
76
77 if duration.has_time_component() {
78 use chrono::Timelike;
79 components.assign(Component::Hour, target_date.hour() as i32);
80 components.assign(Component::Minute, target_date.minute() as i32);
81 components.assign(Component::Second, target_date.second() as i32);
82 }
83
84 results.push(context.create_result(
85 index,
86 index + matched_text.len(),
87 components,
88 None,
89 ));
90 }
91
92 Ok(results)
93 }
94}