Skip to main content

whichtime_sys/refiners/
merge_datetime.rs

1//! Merge date and time refiner - combines adjacent date and time results
2
3use crate::components::Component;
4use crate::context::ParsingContext;
5use crate::refiners::Refiner;
6use crate::results::ParsedResult;
7
8/// Refiner that combines adjacent date-only and time-only matches.
9pub struct MergeDateTimeRefiner;
10
11impl Refiner for MergeDateTimeRefiner {
12    fn refine(&self, context: &ParsingContext, results: Vec<ParsedResult>) -> Vec<ParsedResult> {
13        if results.len() < 2 {
14            return results;
15        }
16
17        let mut merged = Vec::with_capacity(results.len());
18        let mut i = 0;
19
20        while i < results.len() {
21            let current = &results[i];
22
23            // Check if next result exists and could be merged
24            if i + 1 < results.len() {
25                let next = &results[i + 1];
26
27                // Check if they're adjacent (with optional whitespace/connectors)
28                let gap_start = current.end_index;
29                let gap_end = next.index;
30
31                if gap_end >= gap_start {
32                    let gap = if gap_end > gap_start {
33                        &context.text[gap_start..gap_end]
34                    } else {
35                        ""
36                    };
37                    let gap_lower = gap.trim().to_lowercase();
38                    let is_connectable = gap_lower.is_empty()
39                        || gap_lower == "at"
40                        || gap_lower == ","
41                        || gap_lower == "on"
42                        || gap_lower == "um"  // German
43                        || gap_lower == "a"   // Spanish/Italian
44                        || gap_lower == "à"   // French
45                        || gap_lower == "om"  // Dutch
46                        || gap_lower == "в"   // Russian
47                        || gap_lower == "に"  // Japanese
48                        || gap_lower == "の"  // Japanese possessive
49                        || gap_lower == "о"; // Ukrainian
50
51                    if is_connectable {
52                        // Check if one is date-only and other is time-only
53                        let current_is_date = current.start.is_only_date();
54                        let current_is_time = current.start.is_only_time();
55                        let next_is_date = next.start.is_only_date();
56                        let next_is_time = next.start.is_only_time();
57
58                        if (current_is_date && next_is_time) || (current_is_time && next_is_date) {
59                            // Merge them
60                            let (date_result, time_result) = if current_is_date {
61                                (current, next)
62                            } else {
63                                (next, current)
64                            };
65
66                            let mut merged_components = date_result.start;
67
68                            // When merging date and time, make the year certain
69                            // This prevents ForwardDateRefiner from moving it forward
70                            if let Some(year) = merged_components.get(Component::Year) {
71                                merged_components.assign(Component::Year, year);
72                            }
73
74                            // Copy time components
75                            if let Some(hour) = time_result.start.get(Component::Hour) {
76                                merged_components.assign(Component::Hour, hour);
77                            }
78                            if let Some(minute) = time_result.start.get(Component::Minute) {
79                                merged_components.assign(Component::Minute, minute);
80                            }
81                            if let Some(second) = time_result.start.get(Component::Second) {
82                                merged_components.assign(Component::Second, second);
83                            }
84                            if let Some(meridiem) = time_result.start.get(Component::Meridiem) {
85                                merged_components.assign(Component::Meridiem, meridiem);
86                            }
87
88                            let merged_end = if let Some(mut end_comp) = time_result.end {
89                                if let Some(year) = date_result.start.get(Component::Year) {
90                                    end_comp.assign(Component::Year, year);
91                                }
92                                if let Some(month) = date_result.start.get(Component::Month) {
93                                    end_comp.assign(Component::Month, month);
94                                }
95                                if let Some(day) = date_result.start.get(Component::Day) {
96                                    end_comp.assign(Component::Day, day);
97                                }
98                                Some(end_comp)
99                            } else {
100                                date_result.end
101                            };
102
103                            let merged_result = ParsedResult::new(
104                                context.reference,
105                                current.index,
106                                &context.text[current.index..next.end_index],
107                                merged_components,
108                                merged_end,
109                            );
110
111                            merged.push(merged_result);
112                            i += 2;
113                            continue;
114                        }
115                    }
116                }
117            }
118
119            merged.push(results[i].clone());
120            i += 1;
121        }
122
123        merged
124    }
125}