whichtime_sys/parsers/zh/
weekday.rs1use crate::components::Component;
10use crate::context::ParsingContext;
11use crate::dictionaries::zh::get_weekday;
12use crate::error::Result;
13use crate::parsers::Parser;
14use crate::results::ParsedResult;
15use crate::types::Weekday;
16use chrono::{Datelike, Duration};
17use fancy_regex::Regex;
18use std::sync::LazyLock;
19
20static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
22 Regex::new(
23 r"(?P<modifier>这|這|这个|這個|本|今|下|下个|下個|上|上个|上個)?(?P<weekday>星期日|星期天|周日|週日|礼拜日|禮拜日|礼拜天|禮拜天|星期一|周一|週一|礼拜一|禮拜一|星期二|周二|週二|礼拜二|禮拜二|星期三|周三|週三|礼拜三|禮拜三|星期四|周四|週四|礼拜四|禮拜四|星期五|周五|週五|礼拜五|禮拜五|星期六|周六|週六|礼拜六|禮拜六)"
24 ).unwrap()
25});
26
27pub struct ZHWeekdayParser;
29
30impl ZHWeekdayParser {
31 pub fn new() -> Self {
32 Self
33 }
34}
35
36impl Parser for ZHWeekdayParser {
37 fn name(&self) -> &'static str {
38 "ZHWeekdayParser"
39 }
40
41 fn should_apply(&self, context: &ParsingContext) -> bool {
42 context.text.contains("星期")
43 || context.text.contains("周")
44 || context.text.contains("週")
45 || context.text.contains("礼拜")
46 || context.text.contains("禮拜")
47 }
48
49 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
50 let mut results = Vec::new();
51 let ref_date = context.reference.instant;
52
53 let mut start = 0;
54 while start < context.text.len() {
55 let search_text = &context.text[start..];
56
57 if let Ok(Some(caps)) = PATTERN.captures(search_text) {
58 let full_match = caps.get(0).unwrap();
59 let match_start = start + full_match.start();
60 let match_end = start + full_match.end();
61
62 let modifier = caps.name("modifier").map(|m| m.as_str());
63 let weekday_str = caps.name("weekday").map(|m| m.as_str()).unwrap_or("");
64
65 if let Some(weekday) = get_weekday(weekday_str) {
66 let target_weekday = match weekday {
67 Weekday::Sunday => chrono::Weekday::Sun,
68 Weekday::Monday => chrono::Weekday::Mon,
69 Weekday::Tuesday => chrono::Weekday::Tue,
70 Weekday::Wednesday => chrono::Weekday::Wed,
71 Weekday::Thursday => chrono::Weekday::Thu,
72 Weekday::Friday => chrono::Weekday::Fri,
73 Weekday::Saturday => chrono::Weekday::Sat,
74 };
75
76 let current_weekday = ref_date.weekday();
77 let current_num = current_weekday.num_days_from_sunday() as i64;
78 let target_num = target_weekday.num_days_from_sunday() as i64;
79
80 let days_offset = match modifier {
82 Some("下") | Some("下个") | Some("下個") => {
83 let diff = target_num - current_num;
85 (if diff <= 0 { diff + 7 } else { diff }) + 7
86 }
87 Some("上") | Some("上个") | Some("上個") => {
88 let diff = target_num - current_num;
90 (if diff >= 0 { diff - 7 } else { diff }) - 7
91 }
92 Some("这") | Some("這") | Some("这个") | Some("這個") | Some("本")
93 | Some("今") => {
94 target_num - current_num
97 }
98 None | Some(_) => {
99 let diff = target_num - current_num;
101 if diff > 0 {
102 diff - 7 } else if diff < 0 {
104 diff } else {
106 0 }
108 }
109 };
110
111 let target_date = ref_date + Duration::days(days_offset);
112
113 let mut components = context.create_components();
114 components.assign(Component::Year, target_date.year());
115 components.assign(Component::Month, target_date.month() as i32);
116 components.assign(Component::Day, target_date.day() as i32);
117 components.assign(Component::Weekday, weekday as i32);
118
119 results.push(context.create_result(match_start, match_end, components, None));
120 }
121
122 start = match_end;
123 continue;
124 }
125
126 if let Some(c) = search_text.chars().next() {
128 start += c.len_utf8();
129 } else {
130 break;
131 }
132 }
133
134 Ok(results)
135 }
136}
137
138impl Default for ZHWeekdayParser {
139 fn default() -> Self {
140 Self::new()
141 }
142}