1use std::{
8 cmp::Ordering,
9 fmt,
10 io::{Read, Write},
11 ops::{Add, Sub},
12 str::FromStr,
13};
14
15use chrono::{Datelike, Duration, TimeZone, Timelike, Utc};
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
17
18use crate::encoding::*;
19
20const NANOS_PER_SECOND: i64 = 1_000_000_000;
21const NANOS_PER_TICK: i64 = 100;
22const TICKS_PER_SECOND: i64 = NANOS_PER_SECOND / NANOS_PER_TICK;
23
24const MIN_YEAR: u16 = 1601;
25const MAX_YEAR: u16 = 9999;
26
27pub type DateTimeUtc = chrono::DateTime<Utc>;
28
29#[derive(PartialEq, Debug, Clone, Copy)]
32pub struct DateTime {
33 date_time: DateTimeUtc,
34}
35
36impl Serialize for DateTime {
37 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
38 where
39 S: Serializer,
40 {
41 let ticks = self.checked_ticks();
42 ticks.serialize(serializer)
43 }
44}
45
46impl<'de> Deserialize<'de> for DateTime {
47 fn deserialize<D>(deserializer: D) -> Result<DateTime, D::Error>
48 where
49 D: Deserializer<'de>,
50 {
51 let ticks = i64::deserialize(deserializer)?;
52 Ok(DateTime::from(ticks))
53 }
54}
55
56impl BinaryEncoder<DateTime> for DateTime {
58 fn byte_len(&self) -> usize {
59 8
60 }
61
62 fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
63 let ticks = self.checked_ticks();
64 write_i64(stream, ticks)
65 }
66
67 fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
68 let ticks = read_i64(stream)?;
69 let date_time = DateTime::from(ticks);
70 Ok(date_time - decoding_options.client_offset)
73 }
74}
75
76impl Default for DateTime {
77 fn default() -> Self {
78 DateTime::epoch()
79 }
80}
81
82impl Add<Duration> for DateTime {
83 type Output = Self;
84
85 fn add(self, duration: Duration) -> Self {
86 DateTime::from(self.date_time + duration)
87 }
88}
89
90impl Sub<DateTime> for DateTime {
91 type Output = Duration;
92
93 fn sub(self, other: Self) -> Duration {
94 self.date_time - other.date_time
95 }
96}
97
98impl Sub<Duration> for DateTime {
99 type Output = Self;
100
101 fn sub(self, duration: Duration) -> Self {
102 DateTime::from(self.date_time - duration)
103 }
104}
105
106impl PartialOrd for DateTime {
107 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
108 Some(self.date_time.cmp(&other.date_time))
109 }
110}
111
112impl From<(u16, u16, u16, u16, u16, u16)> for DateTime {
114 fn from(dt: (u16, u16, u16, u16, u16, u16)) -> Self {
115 let (year, month, day, hour, minute, second) = dt;
116 DateTime::from((year, month, day, hour, minute, second, 0))
117 }
118}
119
120impl From<(u16, u16, u16, u16, u16, u16, u32)> for DateTime {
122 fn from(dt: (u16, u16, u16, u16, u16, u16, u32)) -> Self {
123 let (year, month, day, hour, minute, second, nanos) = dt;
124 if month < 1 || month > 12 {
125 panic!("Invalid month");
126 }
127 if day < 1 || day > 31 {
128 panic!("Invalid day");
129 }
130 if hour > 23 {
131 panic!("Invalid hour");
132 }
133 if minute > 59 {
134 panic!("Invalid minute");
135 }
136 if second > 59 {
137 panic!("Invalid second");
138 }
139 if nanos as i64 >= NANOS_PER_SECOND {
140 panic!("Invalid nanosecond");
141 }
142 let dt = Utc.ymd(year as i32, month as u32, day as u32).and_hms_nano(
143 hour as u32,
144 minute as u32,
145 second as u32,
146 nanos,
147 );
148 DateTime::from(dt)
149 }
150}
151
152impl From<DateTimeUtc> for DateTime {
153 fn from(date_time: DateTimeUtc) -> Self {
154 let year = date_time.year();
156 let month = date_time.month();
157 let day = date_time.day();
158 let hour = date_time.hour();
159 let minute = date_time.minute();
160 let second = date_time.second();
161 let nanos = (date_time.nanosecond() / NANOS_PER_TICK as u32) * NANOS_PER_TICK as u32;
162 let date_time = Utc
163 .ymd(year, month, day)
164 .and_hms_nano(hour, minute, second, nanos);
165 DateTime { date_time }
166 }
167}
168
169impl From<i64> for DateTime {
170 fn from(value: i64) -> Self {
171 if value == i64::max_value() {
172 Self::endtimes()
174 } else {
175 let secs = value / TICKS_PER_SECOND;
176 let nanos = (value - secs * TICKS_PER_SECOND) * NANOS_PER_TICK;
177 let duration = Duration::seconds(secs) + Duration::nanoseconds(nanos);
178 Self::from(Self::epoch_chrono() + duration)
179 }
180 }
181}
182
183impl Into<i64> for DateTime {
184 fn into(self) -> i64 {
185 self.checked_ticks()
186 }
187}
188
189impl Into<DateTimeUtc> for DateTime {
190 fn into(self) -> DateTimeUtc {
191 self.as_chrono()
192 }
193}
194
195impl fmt::Display for DateTime {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 write!(f, "{}", self.date_time.to_rfc3339())
198 }
199}
200
201impl FromStr for DateTime {
202 type Err = ();
203
204 fn from_str(s: &str) -> Result<Self, Self::Err> {
205 DateTimeUtc::from_str(s).map(DateTime::from).map_err(|e| {
206 error!("Cannot parse date {}, error = {}", s, e);
207 })
208 }
209}
210
211impl DateTime {
212 pub fn now() -> DateTime {
214 DateTime::from(Utc::now())
215 }
216
217 pub fn now_with_offset(offset: Duration) -> DateTime {
219 DateTime::from(Utc::now() + offset)
220 }
221
222 pub fn null() -> DateTime {
224 DateTime::epoch()
226 }
227
228 pub fn is_null(&self) -> bool {
230 self.ticks() == 0i64
231 }
232
233 pub fn epoch() -> DateTime {
235 DateTime::from(Self::epoch_chrono())
236 }
237
238 pub fn endtimes() -> DateTime {
240 DateTime::from(Self::endtimes_chrono())
241 }
242
243 pub fn endtimes_ticks() -> i64 {
245 Self::duration_to_ticks(Self::endtimes_chrono().signed_duration_since(Self::epoch_chrono()))
246 }
247
248 pub fn ymd(year: u16, month: u16, day: u16) -> DateTime {
250 DateTime::ymd_hms(year, month, day, 0, 0, 0)
251 }
252
253 pub fn ymd_hms(
255 year: u16,
256 month: u16,
257 day: u16,
258 hour: u16,
259 minute: u16,
260 second: u16,
261 ) -> DateTime {
262 DateTime::from((year, month, day, hour, minute, second))
263 }
264
265 pub fn ymd_hms_nano(
267 year: u16,
268 month: u16,
269 day: u16,
270 hour: u16,
271 minute: u16,
272 second: u16,
273 nanos: u32,
274 ) -> DateTime {
275 DateTime::from((year, month, day, hour, minute, second, nanos))
276 }
277
278 pub fn ticks(&self) -> i64 {
280 Self::duration_to_ticks(self.date_time.signed_duration_since(Self::epoch_chrono()))
281 }
282
283 pub fn checked_ticks(&self) -> i64 {
286 let nanos = self.ticks();
287 if nanos < 0 {
288 return 0;
289 }
290 if nanos > Self::endtimes_ticks() {
291 return i64::max_value();
292 }
293 nanos
294 }
295
296 pub fn as_chrono(&self) -> DateTimeUtc {
298 self.date_time
299 }
300
301 fn epoch_chrono() -> DateTimeUtc {
303 Utc.ymd(MIN_YEAR as i32, 1, 1).and_hms(0, 0, 0)
304 }
305
306 fn endtimes_chrono() -> DateTimeUtc {
309 Utc.ymd(MAX_YEAR as i32, 12, 31).and_hms(23, 59, 59)
310 }
311
312 fn duration_to_ticks(duration: Duration) -> i64 {
314 let seconds_part = Duration::seconds(duration.num_seconds());
317 let seconds = seconds_part.num_seconds();
318 let nanos = (duration - seconds_part).num_nanoseconds().unwrap();
319 seconds * TICKS_PER_SECOND + nanos / NANOS_PER_TICK
321 }
322}