chrono_node/core/
reference.rs1use super::js_date::JsDate;
4use super::timezone::{to_timezone_offset, TimezoneAbbrMap, TimezoneInput};
5
6#[derive(Clone, Debug, Default, PartialEq, Eq)]
8pub struct ParsingReference {
9 pub instant: Option<JsDate>,
10 pub timezone: Option<TimezoneInput>,
11}
12
13#[derive(Clone, Debug, Default, PartialEq, Eq)]
15pub enum ReferenceInput {
16 #[default]
17 Now,
18 Date(JsDate),
19 Reference(ParsingReference),
20}
21
22impl From<JsDate> for ReferenceInput {
23 fn from(date: JsDate) -> Self {
24 ReferenceInput::Date(date)
25 }
26}
27
28impl From<ParsingReference> for ReferenceInput {
29 fn from(reference: ParsingReference) -> Self {
30 ReferenceInput::Reference(reference)
31 }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct ReferenceWithTimezone {
37 pub instant: JsDate,
38 pub timezone_offset: Option<i64>,
40}
41
42impl ReferenceWithTimezone {
43 pub fn new(instant: JsDate, timezone_offset: Option<i64>) -> ReferenceWithTimezone {
44 ReferenceWithTimezone {
45 instant,
46 timezone_offset,
47 }
48 }
49
50 pub fn from_input(input: &ReferenceInput, overrides: &TimezoneAbbrMap, now: JsDate) -> ReferenceWithTimezone {
52 match input {
53 ReferenceInput::Date(date) => ReferenceWithTimezone::new(*date, None),
54 ReferenceInput::Now => ReferenceWithTimezone::new(now, to_timezone_offset(None, Some(now), overrides)),
55 ReferenceInput::Reference(r) => {
56 let instant = r.instant.unwrap_or(now);
57 let offset = to_timezone_offset(r.timezone.as_ref(), Some(instant), overrides);
58 ReferenceWithTimezone::new(instant, offset)
59 }
60 }
61 }
62
63 pub fn get_date_with_adjusted_timezone(&self) -> JsDate {
66 let mut date = self.instant;
67 if self.timezone_offset.is_some() {
68 date.set_minutes(date.get_minutes() - self.get_system_timezone_adjustment_minute(Some(self.instant), None));
69 }
70 date
71 }
72
73 pub fn get_system_timezone_adjustment_minute(&self, date: Option<JsDate>, override_offset: Option<i64>) -> i64 {
75 let date = date.unwrap_or_else(JsDate::now);
76 let current = -date.get_timezone_offset();
77 let target = override_offset.or(self.timezone_offset).unwrap_or(current);
78 current - target
79 }
80
81 pub fn get_timezone_offset(&self) -> i64 {
83 self.timezone_offset.unwrap_or(-self.instant.get_timezone_offset())
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn adjusted_date_shifts_into_reference_zone() {
93 let instant = JsDate::parse_iso("2021-06-09T12:00:00.000Z").unwrap();
94 let r = ReferenceWithTimezone::from_input(
95 &ReferenceInput::Reference(ParsingReference {
96 instant: Some(instant),
97 timezone: Some(TimezoneInput::Abbr("CDT".into())),
98 }),
99 &TimezoneAbbrMap::new(),
100 JsDate::INVALID,
101 );
102 assert_eq!(r.timezone_offset, Some(-300));
103 assert_eq!(
104 r.get_date_with_adjusted_timezone().to_iso_string().unwrap(),
105 "2021-06-09T07:00:00.000Z"
106 );
107 assert_eq!(r.get_timezone_offset(), -300);
108 }
109
110 #[test]
111 fn plain_date_has_no_offset() {
112 let instant = JsDate::parse_iso("2021-06-09T12:00:00.000Z").unwrap();
113 let r = ReferenceWithTimezone::from_input(&instant.into(), &TimezoneAbbrMap::new(), JsDate::INVALID);
114 assert_eq!(r.timezone_offset, None);
115 assert_eq!(r.get_date_with_adjusted_timezone(), instant);
116 assert_eq!(r.get_timezone_offset(), 0);
117 }
118}