whichtime_sys/parsers/sv/
time_unit_relative.rs1use crate::components::Component;
11use crate::context::ParsingContext;
12use crate::dictionaries::sv::{get_time_unit, parse_number_pattern};
13use crate::error::Result;
14use crate::parsers::Parser;
15use crate::results::ParsedResult;
16use crate::types::{Duration, TimeUnit, add_duration};
17use chrono::{Datelike, Timelike};
18use fancy_regex::Regex;
19use std::sync::LazyLock;
20
21static RELATIVE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
23 Regex::new(
24 r"(?i)(?<![a-zA-ZåäöÅÄÖ])(?P<modifier>nästa|nasta|förra|forra|kommande)\s+(?P<num>\d+|en|ett|två|tva|tre|fyra|fem|sex|sju|åtta|atta|nio|tio|elva|tolv)\s+(?P<unit>sekunder?|minuter?|timm(?:ar|e)?|dagar?|veckor?|månader?|manader?|år|ar)(?![a-zA-ZåäöÅÄÖ])"
25 ).unwrap()
26});
27
28static EFTER_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
30 Regex::new(
31 r"(?i)(?<![a-zA-ZåäöÅÄÖ])efter\s+(?P<num>\d+|en|ett|två|tva|tre|fyra|fem|sex|sju|åtta|atta|nio|tio|elva|tolv)\s+(?P<unit>sekunder?|minuter?|timm(?:ar|e)?|dagar?|veckor?|månader?|manader?|år|ar)(?![a-zA-ZåäöÅÄÖ])"
32 ).unwrap()
33});
34
35static SIGN_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
37 Regex::new(
38 r"(?<![a-zA-ZåäöÅÄÖ0-9])(?P<sign>[+\-])(?P<num>\d+)\s*(?P<unit>sekunder?|minuter?|timm(?:ar|e)?|dagar?|veckor?|månader?|manader?|år|ar)(?![a-zA-ZåäöÅÄÖ])"
39 ).unwrap()
40});
41
42pub struct SVTimeUnitRelativeParser;
44
45impl SVTimeUnitRelativeParser {
46 pub fn new() -> Self {
47 Self
48 }
49
50 fn parse_unit(unit_str: &str) -> Option<TimeUnit> {
51 let lower = unit_str.to_lowercase();
52 get_time_unit(&lower)
53 }
54}
55
56impl Default for SVTimeUnitRelativeParser {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl Parser for SVTimeUnitRelativeParser {
63 fn name(&self) -> &'static str {
64 "SVTimeUnitRelativeParser"
65 }
66
67 fn should_apply(&self, _context: &ParsingContext) -> bool {
68 true
69 }
70
71 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
72 let mut results = Vec::new();
73 let ref_date = context.reference.instant;
74
75 let mut start = 0;
77 while start < context.text.len() {
78 let search_text = &context.text[start..];
79 let captures = match RELATIVE_PATTERN.captures(search_text) {
80 Ok(Some(caps)) => caps,
81 Ok(None) => break,
82 Err(_) => break,
83 };
84
85 let full_match = match captures.get(0) {
86 Some(m) => m,
87 None => break,
88 };
89
90 let match_start = start + full_match.start();
91 let match_end = start + full_match.end();
92
93 let modifier = captures
94 .name("modifier")
95 .map(|m| m.as_str().to_lowercase())
96 .unwrap_or_default();
97 let num_str = captures.name("num").map(|m| m.as_str()).unwrap_or("1");
98 let unit_str = captures
99 .name("unit")
100 .map(|m| m.as_str())
101 .unwrap_or_default();
102
103 let num = parse_number_pattern(num_str);
104 if let Some(unit) = Self::parse_unit(unit_str) {
105 let is_past = modifier.starts_with("förra") || modifier.starts_with("forra");
106 let multiplier = if is_past { -1.0 } else { 1.0 };
107 let adjusted_num = num * multiplier;
108
109 let mut duration = Duration::new();
110 match unit {
111 TimeUnit::Second => duration.second = Some(adjusted_num),
112 TimeUnit::Minute => duration.minute = Some(adjusted_num),
113 TimeUnit::Hour => duration.hour = Some(adjusted_num),
114 TimeUnit::Day => duration.day = Some(adjusted_num),
115 TimeUnit::Week => duration.week = Some(adjusted_num),
116 TimeUnit::Month => duration.month = Some(adjusted_num),
117 TimeUnit::Year => duration.year = Some(adjusted_num),
118 _ => {}
119 }
120
121 let target_date = add_duration(ref_date, &duration);
122
123 let mut components = context.create_components();
124 components.assign(Component::Year, target_date.year());
125 components.assign(Component::Month, target_date.month() as i32);
126 components.assign(Component::Day, target_date.day() as i32);
127
128 if duration.has_time_component() {
129 components.assign(Component::Hour, target_date.hour() as i32);
130 components.assign(Component::Minute, target_date.minute() as i32);
131 components.assign(Component::Second, target_date.second() as i32);
132 } else {
133 components.imply(Component::Hour, ref_date.hour() as i32);
134 components.imply(Component::Minute, ref_date.minute() as i32);
135 }
136
137 results.push(context.create_result(match_start, match_end, components, None));
138 }
139
140 start = match_end;
141 }
142
143 start = 0;
145 while start < context.text.len() {
146 let search_text = &context.text[start..];
147 let captures = match EFTER_PATTERN.captures(search_text) {
148 Ok(Some(caps)) => caps,
149 Ok(None) => break,
150 Err(_) => break,
151 };
152
153 let full_match = match captures.get(0) {
154 Some(m) => m,
155 None => break,
156 };
157
158 let match_start = start + full_match.start();
159 let match_end = start + full_match.end();
160
161 let overlaps = results.iter().any(|r| {
163 (match_start >= r.index && match_start < r.index + r.text.len())
164 || (r.index >= match_start && r.index < match_end)
165 });
166 if overlaps {
167 start = match_end;
168 continue;
169 }
170
171 let num_str = captures.name("num").map(|m| m.as_str()).unwrap_or("1");
172 let unit_str = captures
173 .name("unit")
174 .map(|m| m.as_str())
175 .unwrap_or_default();
176
177 let num = parse_number_pattern(num_str);
178 if let Some(unit) = Self::parse_unit(unit_str) {
179 let mut duration = Duration::new();
180 match unit {
181 TimeUnit::Second => duration.second = Some(num),
182 TimeUnit::Minute => duration.minute = Some(num),
183 TimeUnit::Hour => duration.hour = Some(num),
184 TimeUnit::Day => duration.day = Some(num),
185 TimeUnit::Week => duration.week = Some(num),
186 TimeUnit::Month => duration.month = Some(num),
187 TimeUnit::Year => duration.year = Some(num),
188 _ => {}
189 }
190
191 let target_date = add_duration(ref_date, &duration);
192
193 let mut components = context.create_components();
194 components.assign(Component::Year, target_date.year());
195 components.assign(Component::Month, target_date.month() as i32);
196 components.assign(Component::Day, target_date.day() as i32);
197
198 if duration.has_time_component() {
199 components.assign(Component::Hour, target_date.hour() as i32);
200 components.assign(Component::Minute, target_date.minute() as i32);
201 components.assign(Component::Second, target_date.second() as i32);
202 } else {
203 components.imply(Component::Hour, ref_date.hour() as i32);
204 components.imply(Component::Minute, ref_date.minute() as i32);
205 }
206
207 results.push(context.create_result(match_start, match_end, components, None));
208 }
209
210 start = match_end;
211 }
212
213 start = 0;
215 while start < context.text.len() {
216 let search_text = &context.text[start..];
217 let captures = match SIGN_PATTERN.captures(search_text) {
218 Ok(Some(caps)) => caps,
219 Ok(None) => break,
220 Err(_) => break,
221 };
222
223 let full_match = match captures.get(0) {
224 Some(m) => m,
225 None => break,
226 };
227
228 let match_start = start + full_match.start();
229 let match_end = start + full_match.end();
230
231 let overlaps = results.iter().any(|r| {
233 (match_start >= r.index && match_start < r.index + r.text.len())
234 || (r.index >= match_start && r.index < match_end)
235 });
236 if overlaps {
237 start = match_end;
238 continue;
239 }
240
241 let sign = captures.name("sign").map(|m| m.as_str()).unwrap_or("+");
242 let num_str = captures.name("num").map(|m| m.as_str()).unwrap_or("1");
243 let unit_str = captures
244 .name("unit")
245 .map(|m| m.as_str())
246 .unwrap_or_default();
247
248 let num: f64 = num_str.parse().unwrap_or(0.0);
249 let adjusted_num = if sign == "-" { -num } else { num };
250
251 if let Some(unit) = Self::parse_unit(unit_str) {
252 let mut duration = Duration::new();
253 match unit {
254 TimeUnit::Second => duration.second = Some(adjusted_num),
255 TimeUnit::Minute => duration.minute = Some(adjusted_num),
256 TimeUnit::Hour => duration.hour = Some(adjusted_num),
257 TimeUnit::Day => duration.day = Some(adjusted_num),
258 TimeUnit::Week => duration.week = Some(adjusted_num),
259 TimeUnit::Month => duration.month = Some(adjusted_num),
260 TimeUnit::Year => duration.year = Some(adjusted_num),
261 _ => {}
262 }
263
264 let target_date = add_duration(ref_date, &duration);
265
266 let mut components = context.create_components();
267 components.assign(Component::Year, target_date.year());
268 components.assign(Component::Month, target_date.month() as i32);
269 components.assign(Component::Day, target_date.day() as i32);
270
271 components.assign(Component::Hour, target_date.hour() as i32);
273 components.assign(Component::Minute, target_date.minute() as i32);
274
275 results.push(context.create_result(match_start, match_end, components, None));
276 }
277
278 start = match_end;
279 }
280
281 Ok(results)
282 }
283}