1#[cfg(feature = "chrono")]
25#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
26pub mod chrono;
27
28#[cfg(feature = "time")]
29#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
30#[allow(clippy::module_inception)]
32pub mod time;
33
34use crate::{tds::codec::Encode, SqlReadBytes};
35#[cfg(feature = "tds73")]
36use byteorder::{ByteOrder, LittleEndian};
37use bytes::{BufMut, BytesMut};
38#[cfg(feature = "tds73")]
39use futures_util::io::AsyncReadExt;
40
41#[derive(Copy, Clone, Debug, Eq, PartialEq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct DateTime {
50 days: i32,
51 seconds_fragments: u32,
52}
53
54impl DateTime {
55 pub fn new(days: i32, seconds_fragments: u32) -> Self {
57 Self {
58 days,
59 seconds_fragments,
60 }
61 }
62
63 pub fn days(self) -> i32 {
66 self.days
67 }
68
69 pub fn seconds_fragments(self) -> u32 {
71 self.seconds_fragments
72 }
73
74 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
75 where
76 R: SqlReadBytes + Unpin,
77 {
78 let days = src.read_i32_le().await?;
79 let seconds_fragments = src.read_u32_le().await?;
80
81 Ok(Self {
82 days,
83 seconds_fragments,
84 })
85 }
86}
87
88impl Encode<BytesMut> for DateTime {
89 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
90 dst.put_i32_le(self.days);
91 dst.put_u32_le(self.seconds_fragments);
92
93 Ok(())
94 }
95}
96
97#[derive(Copy, Clone, Debug, Eq, PartialEq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub struct SmallDateTime {
107 days: u16,
108 seconds_fragments: u16,
109}
110
111impl SmallDateTime {
112 pub fn new(days: u16, seconds_fragments: u16) -> Self {
114 Self {
115 days,
116 seconds_fragments,
117 }
118 }
119 pub fn days(self) -> u16 {
121 self.days
122 }
123
124 pub fn seconds_fragments(self) -> u16 {
126 self.seconds_fragments
127 }
128
129 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
130 where
131 R: SqlReadBytes + Unpin,
132 {
133 let days = src.read_u16_le().await?;
134 let seconds_fragments = src.read_u16_le().await?;
135
136 Ok(Self {
137 days,
138 seconds_fragments,
139 })
140 }
141}
142
143impl Encode<BytesMut> for SmallDateTime {
144 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
145 dst.put_u16_le(self.days);
146 dst.put_u16_le(self.seconds_fragments);
147
148 Ok(())
149 }
150}
151
152#[derive(Copy, Clone, Debug, Eq, PartialEq)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160#[cfg(feature = "tds73")]
161#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
162pub struct Date(u32);
163
164#[cfg(feature = "tds73")]
165#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
166impl Date {
167 #[inline]
168 pub fn new(days: u32) -> Date {
173 assert_eq!(days >> 24, 0);
174 Date(days)
175 }
176
177 #[inline]
178 pub fn days(self) -> u32 {
180 self.0
181 }
182
183 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
184 where
185 R: SqlReadBytes + Unpin,
186 {
187 let mut bytes = [0u8; 4];
188 src.read_exact(&mut bytes[..3]).await?;
189 Ok(Self::new(LittleEndian::read_u32(&bytes)))
190 }
191}
192
193#[cfg(feature = "tds73")]
194#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
195impl Encode<BytesMut> for Date {
196 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
197 let mut tmp = [0u8; 4];
198 LittleEndian::write_u32(&mut tmp, self.days());
199 assert_eq!(tmp[3], 0);
200 dst.extend_from_slice(&tmp[0..3]);
201
202 Ok(())
203 }
204}
205
206#[derive(Copy, Clone, Debug)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
214#[cfg(feature = "tds73")]
215#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
216pub struct Time {
217 increments: u64,
218 scale: u8,
219}
220
221#[cfg(feature = "tds73")]
222#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
223impl PartialEq for Time {
224 fn eq(&self, t: &Time) -> bool {
225 self.increments as f64 / 10f64.powi(self.scale as i32)
226 == t.increments as f64 / 10f64.powi(t.scale as i32)
227 }
228}
229
230#[cfg(feature = "tds73")]
231#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
232impl Time {
233 pub fn new(increments: u64, scale: u8) -> Self {
235 Self { increments, scale }
236 }
237
238 #[inline]
239 pub fn increments(self) -> u64 {
244 self.increments
245 }
246
247 #[inline]
248 pub fn scale(self) -> u8 {
250 self.scale
251 }
252
253 #[inline]
254 pub(crate) fn len(self) -> crate::Result<u8> {
256 Ok(match self.scale {
257 0..=2 => 3,
258 3..=4 => 4,
259 5..=7 => 5,
260 _ => {
261 return Err(crate::Error::Protocol(
262 format!("timen: invalid scale {}", self.scale).into(),
263 ))
264 }
265 })
266 }
267
268 pub(crate) async fn decode<R>(src: &mut R, n: usize, rlen: usize) -> crate::Result<Time>
269 where
270 R: SqlReadBytes + Unpin,
271 {
272 let val = match (n, rlen) {
273 (0..=2, 3) => {
274 let hi = src.read_u16_le().await? as u64;
275 let lo = src.read_u8().await? as u64;
276
277 hi | lo << 16
278 }
279 (3..=4, 4) => src.read_u32_le().await? as u64,
280 (5..=7, 5) => {
281 let hi = src.read_u32_le().await? as u64;
282 let lo = src.read_u8().await? as u64;
283
284 hi | lo << 32
285 }
286 _ => {
287 return Err(crate::Error::Protocol(
288 format!("timen: invalid length {}", n).into(),
289 ))
290 }
291 };
292
293 Ok(Time {
294 increments: val,
295 scale: n as u8,
296 })
297 }
298}
299
300#[cfg(feature = "tds73")]
301#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
302impl Encode<BytesMut> for Time {
303 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
304 match self.len()? {
305 3 => {
306 assert_eq!(self.increments >> 24, 0);
307 dst.put_u16_le(self.increments as u16);
308 dst.put_u8((self.increments >> 16) as u8);
309 }
310 4 => {
311 assert_eq!(self.increments >> 32, 0);
312 dst.put_u32_le(self.increments as u32);
313 }
314 5 => {
315 assert_eq!(self.increments >> 40, 0);
316 dst.put_u32_le(self.increments as u32);
317 dst.put_u8((self.increments >> 32) as u8);
318 }
319 _ => unreachable!(),
320 }
321
322 Ok(())
323 }
324}
325
326#[derive(Copy, Clone, Debug, PartialEq)]
327#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
328#[cfg(feature = "tds73")]
329#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
330pub struct DateTime2 {
338 date: Date,
339 time: Time,
340}
341
342#[cfg(feature = "tds73")]
343#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
344impl DateTime2 {
345 pub fn new(date: Date, time: Time) -> Self {
347 Self { date, time }
348 }
349
350 pub fn date(self) -> Date {
352 self.date
353 }
354
355 pub fn time(self) -> Time {
357 self.time
358 }
359
360 pub(crate) async fn decode<R>(src: &mut R, n: usize, rlen: usize) -> crate::Result<Self>
361 where
362 R: SqlReadBytes + Unpin,
363 {
364 let time = Time::decode(src, n, rlen).await?;
365
366 let mut bytes = [0u8; 4];
367 src.read_exact(&mut bytes[..3]).await?;
368 let date = Date::new(LittleEndian::read_u32(&bytes));
369
370 Ok(Self::new(date, time))
371 }
372}
373
374#[cfg(feature = "tds73")]
375#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
376impl Encode<BytesMut> for DateTime2 {
377 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
378 self.time.encode(dst)?;
379
380 let mut tmp = [0u8; 4];
381 LittleEndian::write_u32(&mut tmp, self.date.days());
382 assert_eq!(tmp[3], 0);
383 dst.extend_from_slice(&tmp[0..3]);
384
385 Ok(())
386 }
387}
388
389#[derive(Copy, Clone, Debug, PartialEq)]
390#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
391#[cfg(feature = "tds73")]
392#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
393pub struct DateTimeOffset {
401 datetime2: DateTime2,
402 offset: i16,
403}
404
405#[cfg(feature = "tds73")]
406#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
407impl DateTimeOffset {
408 pub fn new(datetime2: DateTime2, offset: i16) -> Self {
411 Self { datetime2, offset }
412 }
413
414 pub fn datetime2(self) -> DateTime2 {
416 self.datetime2
417 }
418
419 pub fn offset(self) -> i16 {
421 self.offset
422 }
423
424 pub(crate) async fn decode<R>(src: &mut R, n: usize, rlen: u8) -> crate::Result<Self>
425 where
426 R: SqlReadBytes + Unpin,
427 {
428 let datetime2 = DateTime2::decode(src, n, rlen as usize).await?;
429 let offset = src.read_i16_le().await?;
430
431 Ok(Self { datetime2, offset })
432 }
433}
434
435#[cfg(feature = "tds73")]
436#[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
437impl Encode<BytesMut> for DateTimeOffset {
438 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
439 self.datetime2.encode(dst)?;
440 dst.put_i16_le(self.offset);
441
442 Ok(())
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
450
451 #[test]
452 fn datetime_accessors() {
453 let dt = DateTime::new(-100, 12345);
454 assert_eq!(dt.days(), -100);
455 assert_eq!(dt.seconds_fragments(), 12345);
456 }
457
458 #[tokio::test]
459 async fn datetime_round_trip_including_pre_1900() {
460 for dt in [
461 DateTime::new(0, 0),
462 DateTime::new(200, 3000),
463 DateTime::new(-53690, 25920000),
464 ] {
465 let mut buf = BytesMut::new();
466 dt.encode(&mut buf).unwrap();
467 let decoded = DateTime::decode(&mut buf.into_sql_read_bytes())
468 .await
469 .unwrap();
470 assert_eq!(decoded, dt);
471 }
472 }
473
474 #[test]
475 fn smalldatetime_accessors() {
476 let dt = SmallDateTime::new(100, 200);
477 assert_eq!(dt.days(), 100);
478 assert_eq!(dt.seconds_fragments(), 200);
479 }
480
481 #[tokio::test]
482 async fn smalldatetime_round_trip() {
483 let dt = SmallDateTime::new(65535, 1439);
484 let mut buf = BytesMut::new();
485 dt.encode(&mut buf).unwrap();
486 let decoded = SmallDateTime::decode(&mut buf.into_sql_read_bytes())
487 .await
488 .unwrap();
489 assert_eq!(decoded, dt);
490 }
491
492 #[cfg(feature = "tds73")]
493 #[test]
494 fn date_accessor_and_new() {
495 let date = Date::new(730119);
496 assert_eq!(date.days(), 730119);
497 }
498
499 #[cfg(feature = "tds73")]
500 #[test]
501 #[should_panic]
502 fn date_new_panics_on_overflow() {
503 Date::new(0x0100_0000);
505 }
506
507 #[cfg(feature = "tds73")]
508 #[tokio::test]
509 async fn date_round_trip() {
510 for days in [0u32, 1, 730119, 0x00ff_ffff] {
511 let date = Date::new(days);
512 let mut buf = BytesMut::new();
513 date.encode(&mut buf).unwrap();
514 assert_eq!(buf.len(), 3);
515 let decoded = Date::decode(&mut buf.into_sql_read_bytes()).await.unwrap();
516 assert_eq!(decoded, date);
517 }
518 }
519
520 #[cfg(feature = "tds73")]
521 #[test]
522 fn time_accessors_and_len() {
523 let time = Time::new(1234, 5);
524 assert_eq!(time.increments(), 1234);
525 assert_eq!(time.scale(), 5);
526 assert_eq!(time.len().unwrap(), 5);
527
528 assert_eq!(Time::new(0, 0).len().unwrap(), 3);
529 assert_eq!(Time::new(0, 3).len().unwrap(), 4);
530 assert!(Time::new(0, 8).len().is_err());
531 }
532
533 #[cfg(feature = "tds73")]
534 #[test]
535 fn time_partial_eq_across_scales() {
536 assert_eq!(Time::new(100, 2), Time::new(10_000_000, 7));
538 assert_ne!(Time::new(100, 2), Time::new(200, 2));
539 }
540
541 #[cfg(feature = "tds73")]
542 #[tokio::test]
543 async fn time_round_trip_all_len_buckets() {
544 for (increments, scale) in [(255u64, 2u8), (65535, 4), (16_777_215, 7)] {
545 let time = Time::new(increments, scale);
546 let rlen = time.len().unwrap();
547 let mut buf = BytesMut::new();
548 time.encode(&mut buf).unwrap();
549 let decoded = Time::decode(
550 &mut buf.into_sql_read_bytes(),
551 scale as usize,
552 rlen as usize,
553 )
554 .await
555 .unwrap();
556 assert_eq!(decoded, time);
557 }
558 }
559
560 #[cfg(feature = "tds73")]
561 #[tokio::test]
562 async fn time_round_trip_high_bytes_set() {
563 for (increments, scale) in [(0x00FF_1234u64, 2u8), (0x00AB_1234_5678u64, 7)] {
571 let time = Time::new(increments, scale);
572 let rlen = time.len().unwrap();
573
574 let mut buf = BytesMut::new();
575 time.encode(&mut buf).unwrap();
576
577 let decoded = Time::decode(
578 &mut buf.into_sql_read_bytes(),
579 scale as usize,
580 rlen as usize,
581 )
582 .await
583 .unwrap();
584
585 assert_eq!(decoded, time);
586 assert_eq!(decoded.increments(), increments);
587 }
588 }
589
590 #[cfg(feature = "tds73")]
591 #[tokio::test]
592 async fn time_decode_invalid_length_errors() {
593 let mut buf = BytesMut::new();
594 buf.put_u8(0);
595 let err = Time::decode(&mut buf.into_sql_read_bytes(), 0, 4).await;
597 assert!(err.is_err());
598 }
599
600 #[cfg(feature = "tds73")]
601 #[tokio::test]
602 async fn datetime2_round_trip_and_accessors() {
603 let dt2 = DateTime2::new(Date::new(730119), Time::new(222, 7));
604 assert_eq!(dt2.date(), Date::new(730119));
605 assert_eq!(dt2.time(), Time::new(222, 7));
606
607 let rlen = dt2.time().len().unwrap();
608 let mut buf = BytesMut::new();
609 dt2.encode(&mut buf).unwrap();
610 let decoded = DateTime2::decode(&mut buf.into_sql_read_bytes(), 7, rlen as usize)
611 .await
612 .unwrap();
613 assert_eq!(decoded, dt2);
614 }
615
616 #[cfg(feature = "tds73")]
617 #[tokio::test]
618 async fn datetimeoffset_round_trip_and_accessors() {
619 let dt2 = DateTime2::new(Date::new(730119), Time::new(222, 7));
620 let dto = DateTimeOffset::new(dt2, -120);
621 assert_eq!(dto.datetime2(), dt2);
622 assert_eq!(dto.offset(), -120);
623
624 let rlen = dto.datetime2().time().len().unwrap();
625 let mut buf = BytesMut::new();
626 dto.encode(&mut buf).unwrap();
627 let decoded = DateTimeOffset::decode(&mut buf.into_sql_read_bytes(), 7, rlen)
628 .await
629 .unwrap();
630 assert_eq!(decoded, dto);
631 }
632}