whichtime_sys/parsers/sv/
casual_date.rs1use crate::components::Component;
8use crate::context::ParsingContext;
9use crate::error::Result;
10use crate::parsers::Parser;
11use crate::results::ParsedResult;
12use crate::types::Meridiem;
13use chrono::{Datelike, Duration, Timelike};
14use fancy_regex::Regex;
15use std::sync::LazyLock;
16
17static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
18 Regex::new(
19 r"(?i)(?<![a-zA-ZåäöÅÄÖ])(nu|idag|i\s*dag|ikväll|i\s*kväll|ikvall|i\s*kvall|imorgon|i\s*morgon|igår|i\s*går|igar|i\s*gar|övermorgon|i\s*övermorgon|overmorgon|i\s*overmorgon|förrgår|i\s*förrgår|forrgar|i\s*forrgar)(?:\s+(på|vid)\s+(morgonen|förmiddagen|formiddagen|middagen|eftermiddagen|kvällen|kvallen|natten|midnatt|middag))?(?![a-zA-ZåäöÅÄÖ])"
20 ).unwrap()
21});
22
23const DATE_GROUP: usize = 1;
24const TIME_GROUP: usize = 3;
25
26pub struct SVCasualDateParser;
28
29impl SVCasualDateParser {
30 pub fn new() -> Self {
31 Self
32 }
33
34 fn assign_time_part(components: &mut crate::components::FastComponents, time_part: &str) {
35 match time_part.to_lowercase().as_str() {
36 "morgonen" => {
37 components.assign(Component::Hour, 6);
38 components.assign(Component::Minute, 0);
39 components.assign(Component::Meridiem, Meridiem::AM as i32);
40 }
41 "förmiddagen" | "formiddagen" => {
42 components.assign(Component::Hour, 9);
43 components.assign(Component::Minute, 0);
44 components.assign(Component::Meridiem, Meridiem::AM as i32);
45 }
46 "middagen" | "middag" => {
47 components.assign(Component::Hour, 12);
48 components.assign(Component::Minute, 0);
49 components.assign(Component::Meridiem, Meridiem::PM as i32);
50 }
51 "eftermiddagen" => {
52 components.assign(Component::Hour, 15);
53 components.assign(Component::Minute, 0);
54 components.assign(Component::Meridiem, Meridiem::PM as i32);
55 }
56 "kvällen" | "kvallen" => {
57 components.assign(Component::Hour, 20);
58 components.assign(Component::Minute, 0);
59 components.assign(Component::Meridiem, Meridiem::PM as i32);
60 }
61 "natten" => {
62 components.assign(Component::Hour, 2);
63 components.assign(Component::Minute, 0);
64 components.assign(Component::Meridiem, Meridiem::AM as i32);
65 }
66 "midnatt" => {
67 components.assign(Component::Hour, 0);
68 components.assign(Component::Minute, 0);
69 components.assign(Component::Second, 0);
70 }
71 _ => {}
72 }
73 }
74}
75
76impl Default for SVCasualDateParser {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl Parser for SVCasualDateParser {
83 fn name(&self) -> &'static str {
84 "SVCasualDateParser"
85 }
86
87 fn should_apply(&self, _context: &ParsingContext) -> bool {
88 true
89 }
90
91 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
92 let mut results = Vec::new();
93 let ref_date = context.reference.instant;
94
95 let mut start = 0;
96 while start < context.text.len() {
97 let search_text = &context.text[start..];
98 let captures = match PATTERN.captures(search_text) {
99 Ok(Some(caps)) => caps,
100 Ok(None) => break,
101 Err(_) => break,
102 };
103
104 let full_match = match captures.get(0) {
105 Some(m) => m,
106 None => break,
107 };
108
109 let match_start = start + full_match.start();
110 let match_end = start + full_match.end();
111
112 let date_keyword = captures
113 .get(DATE_GROUP)
114 .map(|m| m.as_str().to_lowercase().replace(" ", ""))
115 .unwrap_or_default();
116
117 let time_part = captures.get(TIME_GROUP).map(|m| m.as_str());
118
119 let mut components = context.create_components();
120 let mut target_date = ref_date;
121
122 match date_keyword.as_str() {
123 "nu" => {
124 components.assign(Component::Hour, ref_date.hour() as i32);
125 components.assign(Component::Minute, ref_date.minute() as i32);
126 components.assign(Component::Second, ref_date.second() as i32);
127 }
128 "idag" => {}
129 "ikväll" | "ikvall" => {
130 components.imply(Component::Hour, 20);
131 components.imply(Component::Minute, 0);
132 }
133 "imorgon" => {
134 target_date = ref_date + Duration::days(1);
135 }
136 "igår" | "igar" => {
137 target_date = ref_date - Duration::days(1);
138 }
139 "övermorgon" | "overmorgon" | "iövermorgon" | "iovermorgon" => {
140 target_date = ref_date + Duration::days(2);
141 }
142 "förrgår" | "forrgar" | "iförrgår" | "iforrgar" => {
143 target_date = ref_date - Duration::days(2);
144 }
145 _ => {}
146 }
147
148 components.assign(Component::Year, target_date.year());
149 components.assign(Component::Month, target_date.month() as i32);
150 components.assign(Component::Day, target_date.day() as i32);
151
152 if let Some(tp) = time_part {
154 Self::assign_time_part(&mut components, tp);
155 }
156
157 results.push(context.create_result(match_start, match_end, components, None));
158 start = match_end;
159 }
160
161 Ok(results)
162 }
163}