whichtime_sys/parsers/zh/
time_unit_within.rs1use crate::components::Component;
9use crate::context::ParsingContext;
10use crate::dictionaries::TimeUnit;
11use crate::dictionaries::zh::{get_time_unit, parse_number_pattern};
12use crate::error::Result;
13use crate::parsers::Parser;
14use crate::results::ParsedResult;
15use chrono::{Datelike, Duration, Timelike};
16use fancy_regex::Regex;
17use std::sync::LazyLock;
18
19static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
21 Regex::new(
22 r"(?P<number>[0-9一二三四五六七八九十两兩]+)(?P<unit>秒|秒钟|秒鐘|分|分钟|分鐘|小时|小時|钟头|鐘頭|天|日|周|週|星期|礼拜|禮拜|个月|個月|月|年)(?:之?内|之?內)"
23 ).unwrap()
24});
25
26pub struct ZHTimeUnitWithinParser;
28
29impl ZHTimeUnitWithinParser {
30 pub fn new() -> Self {
31 Self
32 }
33
34 fn parse_number(s: &str) -> i32 {
35 parse_number_pattern(s) as i32
36 }
37}
38
39impl Parser for ZHTimeUnitWithinParser {
40 fn name(&self) -> &'static str {
41 "ZHTimeUnitWithinParser"
42 }
43
44 fn should_apply(&self, context: &ParsingContext) -> bool {
45 context.text.contains('内') || context.text.contains('內')
46 }
47
48 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
49 let mut results = Vec::new();
50 let ref_date = context.reference.instant;
51
52 let mut start = 0;
53 while start < context.text.len() {
54 let search_text = &context.text[start..];
55
56 if let Ok(Some(caps)) = PATTERN.captures(search_text) {
57 let full_match = caps.get(0).unwrap();
58 let match_start = start + full_match.start();
59 let match_end = start + full_match.end();
60
61 let number = caps
62 .name("number")
63 .map(|m| Self::parse_number(m.as_str()))
64 .unwrap_or(1);
65 let unit_str = caps.name("unit").map(|m| m.as_str()).unwrap_or("");
66
67 let unit = get_time_unit(unit_str);
68
69 if let Some(time_unit) = unit {
70 let target_date = match time_unit {
71 TimeUnit::Second => ref_date + Duration::seconds(number as i64),
72 TimeUnit::Minute => ref_date + Duration::minutes(number as i64),
73 TimeUnit::Hour => ref_date + Duration::hours(number as i64),
74 TimeUnit::Day => ref_date + Duration::days(number as i64),
75 TimeUnit::Week => ref_date + Duration::weeks(number as i64),
76 TimeUnit::Month => {
77 let new_month = ref_date.month() as i32 + number;
78 let years_to_add = (new_month - 1) / 12;
79 let final_month = ((new_month - 1) % 12) + 1;
80 ref_date
81 .with_year(ref_date.year() + years_to_add)
82 .and_then(|d| d.with_month(final_month as u32))
83 .unwrap_or(ref_date)
84 }
85 TimeUnit::Year => ref_date
86 .with_year(ref_date.year() + number)
87 .unwrap_or(ref_date),
88 _ => ref_date + Duration::days(number as i64),
89 };
90
91 let mut components = context.create_components();
92 components.assign(Component::Year, target_date.year());
93 components.assign(Component::Month, target_date.month() as i32);
94 components.assign(Component::Day, target_date.day() as i32);
95
96 match time_unit {
98 TimeUnit::Second | TimeUnit::Minute | TimeUnit::Hour => {
99 components.assign(Component::Hour, target_date.hour() as i32);
100 components.assign(Component::Minute, target_date.minute() as i32);
101 if time_unit == TimeUnit::Second {
102 components.assign(Component::Second, target_date.second() as i32);
103 }
104 }
105 _ => {}
106 }
107
108 results.push(context.create_result(match_start, match_end, components, None));
109 }
110
111 start = match_end;
112 continue;
113 }
114
115 if let Some(c) = search_text.chars().next() {
117 start += c.len_utf8();
118 } else {
119 break;
120 }
121 }
122
123 Ok(results)
124 }
125}
126
127impl Default for ZHTimeUnitWithinParser {
128 fn default() -> Self {
129 Self::new()
130 }
131}