whichtime_sys/parsers/sv/
month_name.rs1use crate::components::Component;
11use crate::context::ParsingContext;
12use crate::dictionaries::sv::get_month;
13use crate::error::Result;
14use crate::parsers::Parser;
15use crate::results::ParsedResult;
16use chrono::Datelike;
17use fancy_regex::Regex;
18use std::sync::LazyLock;
19
20static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
21 Regex::new(
22 r"(?ix)
23 (?:den\s+)?
24 (?P<day>\d{1,2})
25 (?:
26 \s*(?:-|–|till)\s*
27 (?P<end_day>\d{1,2})
28 )?
29 \s+
30 (?P<month>januari|februari|mars|april|maj|juni|juli|augusti|september|oktober|november|december|jan\.?|feb\.?|mar\.?|apr\.?|jun\.?|jul\.?|aug\.?|sep\.?|sept\.?|okt\.?|nov\.?|dec\.?)
31 (?:
32 \s+
33 (?P<year>\d{4}|\d{2})
34 )?
35 (?![a-zA-ZåäöÅÄÖ])"
36 ).unwrap()
37});
38
39pub struct SVMonthNameParser;
41
42impl SVMonthNameParser {
43 pub fn new() -> Self {
44 Self
45 }
46}
47
48impl Default for SVMonthNameParser {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl Parser for SVMonthNameParser {
55 fn name(&self) -> &'static str {
56 "SVMonthNameParser"
57 }
58
59 fn should_apply(&self, _context: &ParsingContext) -> bool {
60 true
61 }
62
63 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
64 let mut results = Vec::new();
65 let ref_date = context.reference.instant;
66
67 let mut start = 0;
68 while start < context.text.len() {
69 let search_text = &context.text[start..];
70 let captures = match PATTERN.captures(search_text) {
71 Ok(Some(caps)) => caps,
72 Ok(None) => break,
73 Err(_) => break,
74 };
75
76 let full_match = match captures.get(0) {
77 Some(m) => m,
78 None => break,
79 };
80
81 let match_start = start + full_match.start();
82 let match_end = start + full_match.end();
83
84 let day_str = captures.name("day").map(|m| m.as_str());
85 let month_str = captures
86 .name("month")
87 .map(|m| m.as_str().to_lowercase())
88 .unwrap_or_default();
89 let year_str = captures.name("year").map(|m| m.as_str());
90 let end_day_str = captures.name("end_day").map(|m| m.as_str());
91
92 let day: i32 = day_str.and_then(|d| d.parse().ok()).unwrap_or(1);
94
95 let clean_month = month_str.trim_end_matches('.');
97 let month = get_month(clean_month).unwrap_or(0);
98
99 if month == 0 || !(1..=31).contains(&day) {
100 start = match_end;
101 continue;
102 }
103
104 let mut components = context.create_components();
105
106 if let Some(y) = year_str {
108 let mut year: i32 = y.parse().unwrap_or(ref_date.year());
109 if year < 100 {
110 year = if year > 50 { 1900 + year } else { 2000 + year };
111 }
112 components.assign(Component::Year, year);
113 } else {
114 components.imply(Component::Year, ref_date.year());
115 }
116
117 components.assign(Component::Month, month as i32);
118 components.assign(Component::Day, day);
119
120 if !components.is_valid_date() {
121 start = match_end;
122 continue;
123 }
124
125 let end_components = if let Some(end_day_text) = end_day_str {
127 let end_day: i32 = end_day_text.parse().unwrap_or(0);
128 if end_day > 0 && end_day <= 31 {
129 let mut end_comp = context.create_components();
130 if let Some(start_year) = components.get(Component::Year) {
131 if year_str.is_some() {
132 end_comp.assign(Component::Year, start_year);
133 } else {
134 end_comp.imply(Component::Year, start_year);
135 }
136 }
137 end_comp.assign(Component::Month, month as i32);
138 end_comp.assign(Component::Day, end_day);
139
140 if end_comp.is_valid_date() {
141 Some(end_comp)
142 } else {
143 None
144 }
145 } else {
146 None
147 }
148 } else {
149 None
150 };
151
152 results.push(context.create_result(match_start, match_end, components, end_components));
153 start = match_end;
154 }
155
156 Ok(results)
157 }
158}