Skip to main content

chrono_node/calculation/
merging.rs

1//! upstream `calculation/mergingCalculation.ts`.
2
3use crate::core::components::{meridiem, Component, ParsingComponents};
4use crate::core::js_date::JsDate;
5use crate::core::result::ParsingResult;
6use crate::core::utils::dates::{assign_similar_date, imply_similar_date};
7
8/// `mergeDateTimeResult(dateResult, timeResult)`.
9pub fn merge_date_time_result(date_result: &ParsingResult, time_result: &ParsingResult) -> ParsingResult {
10    let mut result = date_result.js_clone();
11    result.start = merge_date_time_component(&date_result.start, &time_result.start);
12
13    if date_result.end.is_some() || time_result.end.is_some() {
14        let end_date = date_result.end.as_ref().unwrap_or(&date_result.start);
15        let end_time = time_result.end.as_ref().unwrap_or(&time_result.start);
16        let mut end_date_time = merge_date_time_component(end_date, end_time);
17
18        let earlier = matches!(
19            (end_date_time.date().get_time(), result.start.date().get_time()),
20            (Some(e), Some(s)) if e < s
21        );
22        if date_result.end.is_none() && earlier {
23            // For example, "Tuesday 9pm - 1am": the ending is 1am on the next day.
24            let mut next_day = JsDate::from_millis(end_date_time.date().get_time().unwrap_or(0));
25            next_day.set_date(next_day.get_date() + 1);
26            if end_date_time.is_certain(Component::Day) {
27                assign_similar_date(&mut end_date_time, next_day);
28            } else {
29                imply_similar_date(&mut end_date_time, next_day);
30            }
31        }
32        result.end = Some(end_date_time);
33    }
34    result
35}
36
37/// `mergeDateTimeComponent(dateComponent, timeComponent)`.
38pub fn merge_date_time_component(date: &ParsingComponents, time: &ParsingComponents) -> ParsingComponents {
39    let mut dt = date.js_clone();
40    // Upstream assigns/implies `time.get(...)` directly; every time component has these implied.
41    let t = |c| time.get_or_zero(c);
42    if time.is_certain(Component::Hour) {
43        dt.assign(Component::Hour, t(Component::Hour));
44        dt.assign(Component::Minute, t(Component::Minute));
45        if time.is_certain(Component::Second) {
46            dt.assign(Component::Second, t(Component::Second));
47            if time.is_certain(Component::Millisecond) {
48                dt.assign(Component::Millisecond, t(Component::Millisecond));
49            } else {
50                dt.imply(Component::Millisecond, t(Component::Millisecond));
51            }
52        } else {
53            dt.imply(Component::Second, t(Component::Second));
54            dt.imply(Component::Millisecond, t(Component::Millisecond));
55        }
56    } else {
57        dt.imply(Component::Hour, t(Component::Hour));
58        dt.imply(Component::Minute, t(Component::Minute));
59        dt.imply(Component::Second, t(Component::Second));
60        dt.imply(Component::Millisecond, t(Component::Millisecond));
61    }
62
63    if time.is_certain(Component::TimezoneOffset) {
64        dt.assign(Component::TimezoneOffset, t(Component::TimezoneOffset));
65    }
66
67    let date_has_meaningful_meridiem = date.get(Component::Meridiem).is_some()
68        && (date.is_certain(Component::Meridiem) || date.tags().iter().any(|t| t.starts_with("casualReference/")));
69
70    if time.is_certain(Component::Meridiem) {
71        dt.assign(Component::Meridiem, t(Component::Meridiem));
72    } else if let Some(m) = time.get(Component::Meridiem) {
73        if !date_has_meaningful_meridiem {
74            // The time guides meridiem only when the date part carries no meaningful hint.
75            dt.imply(Component::Meridiem, m);
76        }
77    }
78
79    // JS `null < 12` is true, hence `get_or_zero`.
80    if dt.get(Component::Meridiem) == Some(meridiem::PM) && dt.get_or_zero(Component::Hour) < 12 {
81        let hour = dt.get_or_zero(Component::Hour) + 12;
82        if time.is_certain(Component::Hour) {
83            dt.assign(Component::Hour, hour);
84        } else {
85            dt.imply(Component::Hour, hour);
86        }
87    }
88
89    let date_tags: Vec<String> = date.tags().to_vec();
90    let time_tags: Vec<String> = time.tags().to_vec();
91    dt.add_tags(date_tags.iter().map(String::as_str));
92    dt.add_tags(time_tags.iter().map(String::as_str));
93    dt
94}