Skip to main content

whichtime_sys/parsers/de/
time_unit_relative.rs

1//! German relative time unit parser
2//!
3//! Handles expressions like:
4//! - "kommende Woche" (next week)
5//! - "letzten Monat" (last month)
6//! - "letztes Quartal" (last quarter)
7//! - "kommendes Jahr" (next year)
8
9use crate::components::Component;
10use crate::context::ParsingContext;
11use crate::error::Result;
12use crate::parsers::Parser;
13use crate::results::ParsedResult;
14use crate::types::{Duration, add_duration};
15use chrono::{Datelike, Timelike};
16use fancy_regex::Regex;
17use std::sync::LazyLock;
18
19static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
20    Regex::new(
21        r"(?i)(?<![a-zA-ZäöüÄÖÜß])(kommend(?:e[rnms]?)?|nächst(?:e[rnms]?)?|naechst(?:e[rnms]?)?|letzt(?:e[rnms]?)?|vorig(?:e[rnms]?)?|vergangen(?:e[rnms]?)?)\s+(woche|monat(?:s)?|quartal(?:s)?|jahr(?:es)?)\b"
22    ).unwrap()
23});
24
25const MODIFIER_GROUP: usize = 1;
26const UNIT_GROUP: usize = 2;
27
28/// German relative time unit parser
29pub struct DETimeUnitRelativeParser;
30
31impl DETimeUnitRelativeParser {
32    pub fn new() -> Self {
33        Self
34    }
35}
36
37impl Parser for DETimeUnitRelativeParser {
38    fn name(&self) -> &'static str {
39        "DETimeUnitRelativeParser"
40    }
41
42    fn should_apply(&self, _context: &ParsingContext) -> bool {
43        true
44    }
45
46    fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
47        let mut results = Vec::new();
48        let ref_date = context.reference.instant;
49
50        let mut start = 0;
51        while start < context.text.len() {
52            let search_text = &context.text[start..];
53            let captures = match PATTERN.captures(search_text) {
54                Ok(Some(caps)) => caps,
55                Ok(None) => break,
56                Err(_) => break,
57            };
58
59            let full_match = match captures.get(0) {
60                Some(m) => m,
61                None => break,
62            };
63
64            let match_start = start + full_match.start();
65            let match_end = start + full_match.end();
66
67            let modifier = captures
68                .get(MODIFIER_GROUP)
69                .map(|m| m.as_str().to_lowercase())
70                .unwrap_or_default();
71
72            let unit = captures
73                .get(UNIT_GROUP)
74                .map(|m| m.as_str().to_lowercase())
75                .unwrap_or_default();
76
77            // Determine direction: next (+1) or last (-1)
78            let multiplier: f64 = if modifier.starts_with("kommend")
79                || modifier.starts_with("nächst")
80                || modifier.starts_with("naechst")
81            {
82                1.0
83            } else {
84                -1.0
85            };
86
87            let mut duration = Duration::new();
88
89            if unit.starts_with("woche") {
90                duration.week = Some(multiplier);
91            } else if unit.starts_with("monat") {
92                duration.month = Some(multiplier);
93            } else if unit.starts_with("quartal") {
94                duration.quarter = Some(multiplier);
95            } else if unit.starts_with("jahr") {
96                duration.year = Some(multiplier);
97            }
98
99            let target_date = add_duration(ref_date, &duration);
100
101            let mut components = context.create_components();
102            components.assign(Component::Year, target_date.year());
103            components.assign(Component::Month, target_date.month() as i32);
104            components.assign(Component::Day, target_date.day() as i32);
105            // Imply time from reference for relative date expressions
106            components.imply(Component::Hour, target_date.hour() as i32);
107            components.imply(Component::Minute, target_date.minute() as i32);
108            components.imply(Component::Second, target_date.second() as i32);
109
110            results.push(context.create_result(match_start, match_end, components, None));
111
112            start = match_end;
113        }
114
115        Ok(results)
116    }
117}
118
119impl Default for DETimeUnitRelativeParser {
120    fn default() -> Self {
121        Self::new()
122    }
123}