1use constants::{MAX_TICKS, MIN_TICKS, TICKS_CEILING, TICKS_PER_DAY, TICK_MASK};
2use constants::{MIN_DATE_TIME, NANOSECONDS_PER_TICK, TICKS_PER_SECOND};
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5use std::time::Duration;
6use thiserror::Error;
7use time::{error::IndeterminateOffset, OffsetDateTime, PrimitiveDateTime, UtcOffset};
8mod constants;
9
10#[derive(Error, Debug)]
11pub enum OutOfRangeError {
12 #[error("date time is too large")]
13 TooLarge,
14 #[error("date time is too small")]
15 TooSmall,
16}
17
18#[derive(Error, Debug)]
19pub enum ParseError {
20 #[error("can't determine offset for date time")]
21 IndeterminateOffset(IndeterminateOffset),
22 #[error("date time is out of range")]
23 OutOfRange(OutOfRangeError),
24}
25
26fn ticks_to_date_time(ticks: u64) -> PrimitiveDateTime {
27 MIN_DATE_TIME
28 + Duration::new(
29 ticks / TICKS_PER_SECOND as u64,
30 (ticks % TICKS_PER_SECOND as u64 * NANOSECONDS_PER_TICK as u64) as u32,
31 )
32}
33
34fn round_division(dividend: i128, divisor: i128) -> i128 {
35 let double_remainder = (dividend % divisor) * 2;
36 dividend / divisor
37 + if double_remainder.abs() >= divisor.abs() {
38 if dividend.is_negative() == divisor.is_negative() {
39 1
40 } else {
41 -1
42 }
43 } else {
44 0
45 }
46}
47
48fn date_time_to_tick(date_time: PrimitiveDateTime) -> i128 {
49 let nanoseconds = (date_time - MIN_DATE_TIME).whole_nanoseconds();
50 round_division(nanoseconds, 100)
51}
52
53#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
55pub enum DateTimeCs {
56 Local(OffsetDateTime),
57 Unspecified(PrimitiveDateTime),
58 Utc(OffsetDateTime),
59}
60
61impl DateTimeCs {
62 pub fn from_binary(date_time_data: i64) -> Result<Self, ParseError> {
63 let kind = ((date_time_data >> 62) & 3) as u8;
64 let ticks = date_time_data as u64 & TICK_MASK;
65 if kind == 0 || kind == 1 {
66 if ticks > MAX_TICKS {
67 return Err(ParseError::OutOfRange(OutOfRangeError::TooLarge));
68 }
69
70 let date_time = ticks_to_date_time(ticks);
71 return Ok(if kind == 0 {
72 DateTimeCs::Unspecified(date_time)
73 } else {
74 DateTimeCs::Utc(date_time.assume_utc())
75 });
76 }
77
78 let mut ticks = ticks as i64;
79 if ticks > TICKS_CEILING as i64 - TICKS_PER_DAY as i64 {
80 ticks -= TICKS_CEILING as i64;
81 }
82
83 let offset = if ticks > MAX_TICKS as i64 {
84 UtcOffset::local_offset_at(constants::MAX_DATE_TIME.assume_utc())
85 } else if ticks < MIN_TICKS as i64 {
86 UtcOffset::local_offset_at(constants::MIN_DATE_TIME.assume_utc())
87 } else {
88 let date_time = ticks_to_date_time(ticks as u64).assume_utc();
89 UtcOffset::local_offset_at(date_time)
90 }
91 .map_err(ParseError::IndeterminateOffset)?;
92
93 ticks += offset.whole_seconds() as i64 * 10000000;
94 if ticks > MAX_TICKS as i64 {
95 return Err(ParseError::OutOfRange(OutOfRangeError::TooLarge));
96 }
97
98 Ok(DateTimeCs::Local(
99 ticks_to_date_time(ticks as u64).assume_offset(offset),
100 ))
101 }
102
103 pub fn to_binary(&self) -> Result<i64, OutOfRangeError> {
104 let ticks = match self {
105 DateTimeCs::Utc(d) => date_time_to_tick(d.date().with_time(d.time())),
106 DateTimeCs::Unspecified(d) => date_time_to_tick(d.to_owned()),
107 DateTimeCs::Local(d) => {
108 let ticks = date_time_to_tick(d.date().with_time(d.time()));
109 if ticks < 0 {
110 ticks + TICKS_CEILING as i128
111 } else {
112 ticks
113 }
114 }
115 };
116
117 if ticks >= TICKS_CEILING as i128 {
118 return Err(OutOfRangeError::TooLarge);
119 }
120
121 if ticks < MIN_TICKS as i128 {
122 return Err(OutOfRangeError::TooSmall);
123 }
124
125 let kind: i64 = match self {
126 DateTimeCs::Unspecified(_) => 0,
127 DateTimeCs::Utc(_) => 1,
128 DateTimeCs::Local(_) => 2,
129 };
130
131 Ok(ticks as i64 | (kind << 62))
132 }
133}
134
135impl From<PrimitiveDateTime> for DateTimeCs {
136 fn from(value: PrimitiveDateTime) -> Self {
137 Self::Unspecified(value)
138 }
139}
140
141impl From<OffsetDateTime> for DateTimeCs {
142 fn from(value: OffsetDateTime) -> Self {
143 if value.offset().is_utc() {
144 Self::Utc(value)
145 } else {
146 Self::Local(value)
147 }
148 }
149}