1use bitflags::bitflags;
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[repr(u8)]
17pub enum Component {
18 Year = 0,
19 Month = 1,
20 Day = 2,
21 Weekday = 3,
22 Hour = 4,
23 Minute = 5,
24 Second = 6,
25 Millisecond = 7,
26 Meridiem = 8,
27 TimezoneOffset = 9,
28}
29
30impl Component {
31 pub const COUNT: usize = 10;
33
34 #[inline]
36 pub const fn flag(self) -> ComponentFlags {
37 ComponentFlags::from_bits_truncate(1 << (self as u8))
38 }
39
40 pub const fn as_str(self) -> &'static str {
42 match self {
43 Component::Year => "year",
44 Component::Month => "month",
45 Component::Day => "day",
46 Component::Weekday => "weekday",
47 Component::Hour => "hour",
48 Component::Minute => "minute",
49 Component::Second => "second",
50 Component::Millisecond => "millisecond",
51 Component::Meridiem => "meridiem",
52 Component::TimezoneOffset => "timezoneOffset",
53 }
54 }
55}
56
57bitflags! {
58 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
60 pub struct ComponentFlags: u16 {
61 const YEAR = 1 << 0;
62 const MONTH = 1 << 1;
63 const DAY = 1 << 2;
64 const WEEKDAY = 1 << 3;
65 const HOUR = 1 << 4;
66 const MINUTE = 1 << 5;
67 const SECOND = 1 << 6;
68 const MILLISECOND = 1 << 7;
69 const MERIDIEM = 1 << 8;
70 const TIMEZONE_OFFSET = 1 << 9;
71 }
72}
73
74#[derive(Debug, Clone, Copy, Default)]
79pub struct FastComponents {
80 values: [i32; Component::COUNT],
82 known: ComponentFlags,
84 implied: ComponentFlags,
86}
87
88impl FastComponents {
89 #[inline]
91 pub const fn new() -> Self {
92 Self {
93 values: [0; Component::COUNT],
94 known: ComponentFlags::empty(),
95 implied: ComponentFlags::empty(),
96 }
97 }
98
99 pub fn with_defaults(reference: &crate::ReferenceWithTimezone) -> Self {
101 use chrono::Datelike;
102
103 let date = reference.instant;
104 let mut components = Self::new();
105
106 components.imply(Component::Year, date.year());
108 components.imply(Component::Month, date.month() as i32);
109 components.imply(Component::Day, date.day() as i32);
110 components.imply(Component::Hour, 12);
111 components.imply(Component::Minute, 0);
112 components.imply(Component::Second, 0);
113 components.imply(Component::Millisecond, 0);
114
115 components
116 }
117
118 #[inline]
120 pub fn is_certain(&self, comp: Component) -> bool {
121 self.known.contains(comp.flag())
122 }
123
124 #[inline]
126 pub fn has(&self, comp: Component) -> bool {
127 let flag = comp.flag();
128 self.known.contains(flag) || self.implied.contains(flag)
129 }
130
131 #[inline]
133 pub fn get(&self, comp: Component) -> Option<i32> {
134 if self.has(comp) {
135 Some(self.values[comp as usize])
136 } else {
137 None
138 }
139 }
140
141 #[inline]
143 pub fn assign(&mut self, comp: Component, value: i32) -> &mut Self {
144 let idx = comp as usize;
145 let flag = comp.flag();
146 self.values[idx] = value;
147 self.known.insert(flag);
148 self.implied.remove(flag);
149 self
150 }
151
152 #[inline]
154 pub fn imply(&mut self, comp: Component, value: i32) -> &mut Self {
155 if !self.known.contains(comp.flag()) {
156 let idx = comp as usize;
157 self.values[idx] = value;
158 self.implied.insert(comp.flag());
159 }
160 self
161 }
162
163 #[inline]
165 pub fn delete(&mut self, comp: Component) -> &mut Self {
166 let flag = comp.flag();
167 self.known.remove(flag);
168 self.implied.remove(flag);
169 self.values[comp as usize] = 0;
170 self
171 }
172
173 #[inline]
175 pub fn is_only_date(&self) -> bool {
176 !self.is_certain(Component::Hour)
177 && !self.is_certain(Component::Minute)
178 && !self.is_certain(Component::Second)
179 }
180
181 #[inline]
183 pub fn is_only_time(&self) -> bool {
184 !self.is_certain(Component::Weekday)
185 && !self.is_certain(Component::Day)
186 && !self.is_certain(Component::Month)
187 && !self.is_certain(Component::Year)
188 }
189
190 #[inline]
192 pub fn is_only_weekday_component(&self) -> bool {
193 self.is_certain(Component::Weekday)
194 && !self.is_certain(Component::Day)
195 && !self.is_certain(Component::Month)
196 }
197
198 #[inline]
200 pub fn is_date_with_unknown_year(&self) -> bool {
201 self.is_certain(Component::Month) && !self.is_certain(Component::Year)
202 }
203
204 pub fn get_certain_components(&self) -> Vec<Component> {
206 let mut result = Vec::with_capacity(Component::COUNT);
207 for i in 0..Component::COUNT {
208 let comp = unsafe { std::mem::transmute::<u8, Component>(i as u8) };
209 if self.is_certain(comp) {
210 result.push(comp);
211 }
212 }
213 result
214 }
215
216 pub fn is_valid_date(&self) -> bool {
218 use chrono::NaiveDate;
219
220 let year = match self.get(Component::Year) {
221 Some(y) => y,
222 None => return false,
223 };
224 let month = match self.get(Component::Month) {
225 Some(m) => m as u32,
226 None => return false,
227 };
228 let day = match self.get(Component::Day) {
229 Some(d) => d as u32,
230 None => return false,
231 };
232
233 if NaiveDate::from_ymd_opt(year, month, day).is_none() {
235 return false;
236 }
237
238 if let Some(hour) = self.get(Component::Hour)
240 && !(0..24).contains(&hour)
241 {
242 return false;
243 }
244 if let Some(minute) = self.get(Component::Minute)
245 && !(0..60).contains(&minute)
246 {
247 return false;
248 }
249 if let Some(second) = self.get(Component::Second)
250 && !(0..60).contains(&second)
251 {
252 return false;
253 }
254
255 true
256 }
257
258 pub fn to_datetime(
260 &self,
261 reference: &crate::ReferenceWithTimezone,
262 ) -> Option<chrono::DateTime<chrono::Local>> {
263 use chrono::{Local, NaiveDate, TimeZone};
264
265 let year = self.get(Component::Year)?;
266 let month = self.get(Component::Month)? as u32;
267 let day = self.get(Component::Day)? as u32;
268 let hour = self.get(Component::Hour).unwrap_or(12) as u32;
269 let minute = self.get(Component::Minute).unwrap_or(0) as u32;
270 let second = self.get(Component::Second).unwrap_or(0) as u32;
271
272 let naive_dt =
273 NaiveDate::from_ymd_opt(year, month, day)?.and_hms_opt(hour, minute, second)?;
274
275 let local_dt = Local.from_local_datetime(&naive_dt).single()?;
276
277 if let Some(offset) = self.get(Component::TimezoneOffset) {
279 let adjustment = reference.get_system_timezone_adjustment(Some(local_dt), Some(offset));
280 Some(local_dt + chrono::Duration::minutes(adjustment as i64))
281 } else {
282 Some(local_dt)
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_component_flag() {
293 assert_eq!(Component::Year.flag(), ComponentFlags::YEAR);
294 assert_eq!(Component::Month.flag(), ComponentFlags::MONTH);
295 assert_eq!(Component::Hour.flag(), ComponentFlags::HOUR);
296 }
297
298 #[test]
299 fn test_fast_components_assign_and_get() {
300 let mut comp = FastComponents::new();
301
302 comp.assign(Component::Year, 2024);
303 comp.assign(Component::Month, 11);
304 comp.assign(Component::Day, 25);
305
306 assert_eq!(comp.get(Component::Year), Some(2024));
307 assert_eq!(comp.get(Component::Month), Some(11));
308 assert_eq!(comp.get(Component::Day), Some(25));
309 assert_eq!(comp.get(Component::Hour), None);
310
311 assert!(comp.is_certain(Component::Year));
312 assert!(!comp.is_certain(Component::Hour));
313 }
314
315 #[test]
316 fn test_fast_components_imply() {
317 let mut comp = FastComponents::new();
318
319 comp.imply(Component::Hour, 12);
320 assert_eq!(comp.get(Component::Hour), Some(12));
321 assert!(!comp.is_certain(Component::Hour));
322
323 comp.assign(Component::Hour, 15);
325 assert_eq!(comp.get(Component::Hour), Some(15));
326 assert!(comp.is_certain(Component::Hour));
327
328 comp.imply(Component::Hour, 9);
330 assert_eq!(comp.get(Component::Hour), Some(15));
331 }
332
333 #[test]
334 fn test_fast_components_copy() {
335 let mut a = FastComponents::new();
336 a.assign(Component::Year, 2024);
337
338 let b = a;
340
341 assert_eq!(b.get(Component::Year), Some(2024));
342 }
343
344 #[test]
345 fn test_size() {
346 assert!(std::mem::size_of::<FastComponents>() <= 64);
348 }
349}