whichtime_sys/parsers/sv/
weekday.rs1use crate::components::Component;
10use crate::context::ParsingContext;
11use crate::dictionaries::sv::get_weekday;
12use crate::error::Result;
13use crate::parsers::Parser;
14use crate::results::ParsedResult;
15use chrono::{Datelike, Duration, Weekday as ChronoWeekday};
16use fancy_regex::Regex;
17use std::sync::LazyLock;
18
19static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
20 Regex::new(
21 r"(?i)(?<![a-zA-ZåäöÅÄÖ])(?:(?P<modifier>nästa|nasta|förra|forra|kommande|denna|i)\s+)?(?P<prep>på\s+)?(?P<weekday>söndag|sondag|måndag|mandag|tisdag|onsdag|torsdag|fredag|lördag|lordag|sön\.?|son\.?|mån\.?|man\.?|tis\.?|ons\.?|tor\.?|tors\.?|fre\.?|lör\.?|lor\.?)(?![a-zA-ZåäöÅÄÖ])"
22 ).unwrap()
23});
24
25pub struct SVWeekdayParser;
27
28impl SVWeekdayParser {
29 pub fn new() -> Self {
30 Self
31 }
32}
33
34impl Default for SVWeekdayParser {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl Parser for SVWeekdayParser {
41 fn name(&self) -> &'static str {
42 "SVWeekdayParser"
43 }
44
45 fn should_apply(&self, _context: &ParsingContext) -> bool {
46 true
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 let captures = match PATTERN.captures(search_text) {
57 Ok(Some(caps)) => caps,
58 Ok(None) => break,
59 Err(_) => break,
60 };
61
62 let full_match = match captures.get(0) {
63 Some(m) => m,
64 None => break,
65 };
66
67 let match_start = start + full_match.start();
68 let match_end = start + full_match.end();
69
70 let modifier = captures.name("modifier").map(|m| m.as_str().to_lowercase());
71 let weekday_str = captures
72 .name("weekday")
73 .map(|m| m.as_str().to_lowercase())
74 .unwrap_or_default();
75
76 let clean_weekday = weekday_str.trim_end_matches('.');
78
79 let Some(weekday) = get_weekday(clean_weekday) else {
80 start = match_end;
81 continue;
82 };
83
84 let mut components = context.create_components();
85
86 let target_weekday = match weekday {
88 crate::types::Weekday::Sunday => ChronoWeekday::Sun,
89 crate::types::Weekday::Monday => ChronoWeekday::Mon,
90 crate::types::Weekday::Tuesday => ChronoWeekday::Tue,
91 crate::types::Weekday::Wednesday => ChronoWeekday::Wed,
92 crate::types::Weekday::Thursday => ChronoWeekday::Thu,
93 crate::types::Weekday::Friday => ChronoWeekday::Fri,
94 crate::types::Weekday::Saturday => ChronoWeekday::Sat,
95 };
96
97 let current_weekday = ref_date.weekday();
98 let current_day_num = current_weekday.num_days_from_sunday() as i64;
99 let target_day_num = target_weekday.num_days_from_sunday() as i64;
100
101 let mut days_diff = target_day_num - current_day_num;
103
104 let target_date = match modifier.as_deref() {
106 Some(m) if m.starts_with("nästa") || m.starts_with("nasta") || m == "kommande" => {
107 if days_diff <= 0 {
109 days_diff += 7;
110 }
111 ref_date + Duration::days(days_diff)
112 }
113 Some(m) if m.starts_with("förra") || m.starts_with("forra") => {
114 if days_diff >= 0 {
116 days_diff -= 7;
117 }
118 ref_date + Duration::days(days_diff)
119 }
120 _ => {
121 if days_diff > 0 {
123 days_diff -= 7;
124 }
125 ref_date + Duration::days(days_diff)
126 }
127 };
128
129 components.assign(Component::Year, target_date.year());
130 components.assign(Component::Month, target_date.month() as i32);
131 components.assign(Component::Day, target_date.day() as i32);
132 components.assign(Component::Weekday, weekday as i32);
133
134 results.push(context.create_result(match_start, match_end, components, None));
135 start = match_end;
136 }
137
138 Ok(results)
139 }
140}