whichtime_sys/parsers/uk/
casual_date.rs1use crate::components::Component;
9use crate::context::ParsingContext;
10use crate::error::Result;
11use crate::parsers::Parser;
12use crate::results::ParsedResult;
13use crate::types::Meridiem;
14use chrono::{Datelike, Duration, Timelike};
15use fancy_regex::Regex;
16use std::sync::LazyLock;
17
18static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
19 Regex::new(
20 r"(?i)(?<![а-яА-ЯіїєґІЇЄҐ'])(?:(?P<date>зараз|сьогодні|сьогодни|завтра|післязавтра|вчора|позавчора)(?:\s+(?P<time>вранці|зранку|опівдні|вдень|ввечері|увечері|вночі|опівночі))?|(?P<time_only>вранці|зранку|опівдні|вдень|ввечері|увечері|вночі|опівночі))(?![а-яА-ЯіїєґІЇЄҐ'])"
21 ).unwrap()
22});
23
24pub struct UKCasualDateParser;
26
27impl UKCasualDateParser {
28 pub fn new() -> Self {
29 Self
30 }
31
32 fn assign_time_part(components: &mut crate::components::FastComponents, time_part: &str) {
33 match time_part.to_lowercase().as_str() {
34 "вранці" | "зранку" => {
35 components.assign(Component::Hour, 6);
36 components.assign(Component::Minute, 0);
37 components.assign(Component::Meridiem, Meridiem::AM as i32);
38 }
39 "опівдні" => {
40 components.assign(Component::Hour, 12);
41 components.assign(Component::Minute, 0);
42 components.assign(Component::Meridiem, Meridiem::PM as i32);
43 }
44 "вдень" => {
45 components.assign(Component::Hour, 14);
46 components.assign(Component::Minute, 0);
47 components.assign(Component::Meridiem, Meridiem::PM as i32);
48 }
49 "ввечері" | "увечері" => {
50 components.assign(Component::Hour, 20);
51 components.assign(Component::Minute, 0);
52 components.assign(Component::Meridiem, Meridiem::PM as i32);
53 }
54 "вночі" => {
55 components.assign(Component::Hour, 2);
56 components.assign(Component::Minute, 0);
57 components.assign(Component::Meridiem, Meridiem::AM as i32);
58 }
59 "опівночі" => {
60 components.assign(Component::Hour, 0);
61 components.assign(Component::Minute, 0);
62 components.assign(Component::Second, 0);
63 }
64 _ => {}
65 }
66 }
67}
68
69impl Default for UKCasualDateParser {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl Parser for UKCasualDateParser {
76 fn name(&self) -> &'static str {
77 "UKCasualDateParser"
78 }
79
80 fn should_apply(&self, _context: &ParsingContext) -> bool {
81 true
82 }
83
84 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
85 let mut results = Vec::new();
86 let ref_date = context.reference.instant;
87
88 let mut start = 0;
89 while start < context.text.len() {
90 let search_text = &context.text[start..];
91 let captures = match PATTERN.captures(search_text) {
92 Ok(Some(caps)) => caps,
93 Ok(None) => break,
94 Err(_) => break,
95 };
96
97 let full_match = match captures.get(0) {
98 Some(m) => m,
99 None => break,
100 };
101
102 let match_start = start + full_match.start();
103 let match_end = start + full_match.end();
104
105 let date_keyword = captures.name("date").map(|m| m.as_str().to_lowercase());
106 let time_part = captures.name("time").map(|m| m.as_str());
107 let time_only = captures.name("time_only").map(|m| m.as_str());
108
109 let mut components = context.create_components();
110 let mut target_date = ref_date;
111
112 if let Some(ref kw) = date_keyword {
113 match kw.as_str() {
114 "зараз" => {
115 components.assign(Component::Hour, ref_date.hour() as i32);
116 components.assign(Component::Minute, ref_date.minute() as i32);
117 components.assign(Component::Second, ref_date.second() as i32);
118 }
119 "сьогодні" | "сьогодни" => {}
120 "завтра" => {
121 target_date = ref_date + Duration::days(1);
122 }
123 "післязавтра" => {
124 target_date = ref_date + Duration::days(2);
125 }
126 "вчора" => {
127 target_date = ref_date - Duration::days(1);
128 }
129 "позавчора" => {
130 target_date = ref_date - Duration::days(2);
131 }
132 _ => {}
133 }
134 }
135
136 components.assign(Component::Year, target_date.year());
137 components.assign(Component::Month, target_date.month() as i32);
138 components.assign(Component::Day, target_date.day() as i32);
139
140 if let Some(tp) = time_part.or(time_only) {
142 Self::assign_time_part(&mut components, tp);
143 }
144
145 results.push(context.create_result(match_start, match_end, components, None));
146 start = match_end;
147 }
148
149 Ok(results)
150 }
151}