tiberius/tds/
time.rs

1//! Date and time handling.
2//!
3//! When using the `tds73` feature flag together with SQL Server 2008 or later,
4//! the following [`time`] mappings to and from the database are available:
5//!
6//! - `Time` -> [`Time`](time/struct.Time.html)
7//! - `Date` -> [`Date`]
8//! - `DateTime` -> [`PrimitiveDateTime`]
9//! - `DateTime2` -> [`PrimitiveDateTime`]
10//! - `SmallDateTime` -> [`PrimitiveDateTime`]
11//! - `DateTimeOffset` -> [`OffsetDateTime`]
12//!
13//! With SQL Server 2005 and the `tds73` feature flag disabled, the mapping is
14//! different:
15//!
16//! - `DateTime` -> [`PrimitiveDateTime`]
17//! - `SmallDateTime` -> [`PrimitiveDateTime`]
18//!
19//! [`time`]: time/index.html
20//! [`Date`]: time/struct.Date.html
21//! [`PrimitiveDateTime`]: time/struct.PrimitiveDateTime.html
22//! [`OffsetDateTime`]: time/struct.OffsetDateTime.html
23
24#[cfg(feature = "chrono")]
25#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))]
26pub mod chrono;
27
28#[cfg(feature = "time")]
29#[cfg_attr(feature = "docs", doc(cfg(feature = "time")))]
30pub mod time;
31
32use crate::{tds::codec::Encode, SqlReadBytes};
33#[cfg(feature = "tds73")]
34use byteorder::{ByteOrder, LittleEndian};
35use bytes::{BufMut, BytesMut};
36#[cfg(feature = "tds73")]
37use futures_util::io::AsyncReadExt;
38
39/// A presentation of `datetime` type in the server.
40///
41/// # Warning
42///
43/// It isn't recommended to use this type directly. For dealing with `datetime`,
44/// use the `time` feature of this crate and its `PrimitiveDateTime` type.
45#[derive(Copy, Clone, Debug, Eq, PartialEq)]
46pub struct DateTime {
47    days: i32,
48    seconds_fragments: u32,
49}
50
51impl DateTime {
52    /// Construct a new `DateTime` instance.
53    pub fn new(days: i32, seconds_fragments: u32) -> Self {
54        Self {
55            days,
56            seconds_fragments,
57        }
58    }
59
60    /// Days since 1st of January, 1900 (including the negative range until 1st
61    /// of January, 1753).
62    pub fn days(self) -> i32 {
63        self.days
64    }
65
66    /// 1/300 of a second, so a value of 300 equals 1 second (since midnight).
67    pub fn seconds_fragments(self) -> u32 {
68        self.seconds_fragments
69    }
70
71    pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
72    where
73        R: SqlReadBytes + Unpin,
74    {
75        let days = src.read_i32_le().await?;
76        let seconds_fragments = src.read_u32_le().await?;
77
78        Ok(Self {
79            days,
80            seconds_fragments,
81        })
82    }
83}
84
85impl Encode<BytesMut> for DateTime {
86    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
87        dst.put_i32_le(self.days);
88        dst.put_u32_le(self.seconds_fragments);
89
90        Ok(())
91    }
92}
93
94/// A presentation of `smalldatetime` type in the server.
95///
96/// # Warning
97///
98/// It isn't recommended to use this type directly. For dealing with
99/// `smalldatetime`, use the `time` feature of this crate and its
100/// `PrimitiveDateTime` type.
101#[derive(Copy, Clone, Debug, Eq, PartialEq)]
102pub struct SmallDateTime {
103    days: u16,
104    seconds_fragments: u16,
105}
106
107impl SmallDateTime {
108    /// Construct a new `SmallDateTime` instance.
109    pub fn new(days: u16, seconds_fragments: u16) -> Self {
110        Self {
111            days,
112            seconds_fragments,
113        }
114    }
115    /// Days since 1st of January, 1900.
116    pub fn days(self) -> u16 {
117        self.days
118    }
119
120    /// 1/300 of a second, so a value of 300 equals 1 second (since midnight)
121    pub fn seconds_fragments(self) -> u16 {
122        self.seconds_fragments
123    }
124
125    pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
126    where
127        R: SqlReadBytes + Unpin,
128    {
129        let days = src.read_u16_le().await?;
130        let seconds_fragments = src.read_u16_le().await?;
131
132        Ok(Self {
133            days,
134            seconds_fragments,
135        })
136    }
137}
138
139impl Encode<BytesMut> for SmallDateTime {
140    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
141        dst.put_u16_le(self.days);
142        dst.put_u16_le(self.seconds_fragments);
143
144        Ok(())
145    }
146}
147
148/// A presentation of `date` type in the server.
149///
150/// # Warning
151///
152/// It isn't recommended to use this type directly. If you want to deal with
153/// `date`, use the `time` feature of this crate and its `Date` type.
154#[derive(Copy, Clone, Debug, Eq, PartialEq)]
155#[cfg(feature = "tds73")]
156#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
157pub struct Date(u32);
158
159#[cfg(feature = "tds73")]
160#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
161impl Date {
162    #[inline]
163    /// Construct a new `Date`
164    ///
165    /// # Panics
166    /// max value of 3 bytes (`u32::max_value() > 8`)
167    pub fn new(days: u32) -> Date {
168        assert_eq!(days >> 24, 0);
169        Date(days)
170    }
171
172    #[inline]
173    /// The number of days from 1st of January, year 1.
174    pub fn days(self) -> u32 {
175        self.0
176    }
177
178    pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
179    where
180        R: SqlReadBytes + Unpin,
181    {
182        let mut bytes = [0u8; 4];
183        src.read_exact(&mut bytes[..3]).await?;
184        Ok(Self::new(LittleEndian::read_u32(&bytes)))
185    }
186}
187
188#[cfg(feature = "tds73")]
189#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
190impl Encode<BytesMut> for Date {
191    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
192        let mut tmp = [0u8; 4];
193        LittleEndian::write_u32(&mut tmp, self.days());
194        assert_eq!(tmp[3], 0);
195        dst.extend_from_slice(&tmp[0..3]);
196
197        Ok(())
198    }
199}
200
201/// A presentation of `time` type in the server.
202///
203/// # Warning
204///
205/// It isn't recommended to use this type directly. If you want to deal with
206/// `time`, use the `time` feature of this crate and its `Time` type.
207#[derive(Copy, Clone, Debug)]
208#[cfg(feature = "tds73")]
209#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
210pub struct Time {
211    increments: u64,
212    scale: u8,
213}
214
215#[cfg(feature = "tds73")]
216#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
217impl PartialEq for Time {
218    fn eq(&self, t: &Time) -> bool {
219        self.increments as f64 / 10f64.powi(self.scale as i32)
220            == t.increments as f64 / 10f64.powi(t.scale as i32)
221    }
222}
223
224#[cfg(feature = "tds73")]
225#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
226impl Time {
227    /// Construct a new `Time`
228    pub fn new(increments: u64, scale: u8) -> Self {
229        Self { increments, scale }
230    }
231
232    #[inline]
233    /// Number of 10^-n second increments since midnight, where `n` is defined
234    /// in [`scale`].
235    ///
236    /// [`scale`]: #method.scale
237    pub fn increments(self) -> u64 {
238        self.increments
239    }
240
241    #[inline]
242    /// The accuracy of the increments.
243    pub fn scale(self) -> u8 {
244        self.scale
245    }
246
247    #[inline]
248    /// Length of the field in number of bytes.
249    pub(crate) fn len(self) -> crate::Result<u8> {
250        Ok(match self.scale {
251            0..=2 => 3,
252            3..=4 => 4,
253            5..=7 => 5,
254            _ => {
255                return Err(crate::Error::Protocol(
256                    format!("timen: invalid scale {}", self.scale).into(),
257                ))
258            }
259        })
260    }
261
262    pub(crate) async fn decode<R>(src: &mut R, n: usize, rlen: usize) -> crate::Result<Time>
263    where
264        R: SqlReadBytes + Unpin,
265    {
266        let val = match (n, rlen) {
267            (0..=2, 3) => {
268                let hi = src.read_u16_le().await? as u64;
269                let lo = src.read_u8().await? as u64;
270
271                hi | lo << 16
272            }
273            (3..=4, 4) => src.read_u32_le().await? as u64,
274            (5..=7, 5) => {
275                let hi = src.read_u32_le().await? as u64;
276                let lo = src.read_u8().await? as u64;
277
278                hi | lo << 32
279            }
280            _ => {
281                return Err(crate::Error::Protocol(
282                    format!("timen: invalid length {}", n).into(),
283                ))
284            }
285        };
286
287        Ok(Time {
288            increments: val,
289            scale: n as u8,
290        })
291    }
292}
293
294#[cfg(feature = "tds73")]
295#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
296impl Encode<BytesMut> for Time {
297    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
298        match self.len()? {
299            3 => {
300                assert_eq!(self.increments >> 24, 0);
301                dst.put_u16_le(self.increments as u16);
302                dst.put_u8((self.increments >> 16) as u8);
303            }
304            4 => {
305                assert_eq!(self.increments >> 32, 0);
306                dst.put_u32_le(self.increments as u32);
307            }
308            5 => {
309                assert_eq!(self.increments >> 40, 0);
310                dst.put_u32_le(self.increments as u32);
311                dst.put_u8((self.increments >> 32) as u8);
312            }
313            _ => unreachable!(),
314        }
315
316        Ok(())
317    }
318}
319
320#[derive(Copy, Clone, Debug, PartialEq)]
321#[cfg(feature = "tds73")]
322#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
323/// A presentation of `datetime2` type in the server.
324///
325/// # Warning
326///
327/// It isn't recommended to use this type directly. For dealing with
328/// `datetime2`, use the `time` feature of this crate and its `PrimitiveDateTime`
329/// type.
330pub struct DateTime2 {
331    date: Date,
332    time: Time,
333}
334
335#[cfg(feature = "tds73")]
336#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
337impl DateTime2 {
338    /// Construct a new `DateTime2` from the date and time components.
339    pub fn new(date: Date, time: Time) -> Self {
340        Self { date, time }
341    }
342
343    /// The date component.
344    pub fn date(self) -> Date {
345        self.date
346    }
347
348    /// The time component.
349    pub fn time(self) -> Time {
350        self.time
351    }
352
353    pub(crate) async fn decode<R>(src: &mut R, n: usize, rlen: usize) -> crate::Result<Self>
354    where
355        R: SqlReadBytes + Unpin,
356    {
357        let time = Time::decode(src, n, rlen).await?;
358
359        let mut bytes = [0u8; 4];
360        src.read_exact(&mut bytes[..3]).await?;
361        let date = Date::new(LittleEndian::read_u32(&bytes));
362
363        Ok(Self::new(date, time))
364    }
365}
366
367#[cfg(feature = "tds73")]
368#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
369impl Encode<BytesMut> for DateTime2 {
370    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
371        self.time.encode(dst)?;
372
373        let mut tmp = [0u8; 4];
374        LittleEndian::write_u32(&mut tmp, self.date.days());
375        assert_eq!(tmp[3], 0);
376        dst.extend_from_slice(&tmp[0..3]);
377
378        Ok(())
379    }
380}
381
382#[derive(Copy, Clone, Debug, PartialEq)]
383#[cfg(feature = "tds73")]
384#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
385/// A presentation of `datetimeoffset` type in the server.
386///
387/// # Warning
388///
389/// It isn't recommended to use this type directly. For dealing with
390/// `datetimeoffset`, use the `time` feature of this crate and its `OffsetDateTime`
391/// type with the correct timezone.
392pub struct DateTimeOffset {
393    datetime2: DateTime2,
394    offset: i16,
395}
396
397#[cfg(feature = "tds73")]
398#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
399impl DateTimeOffset {
400    /// Construct a new `DateTimeOffset` from a `datetime2`, offset marking
401    /// number of minutes from UTC.
402    pub fn new(datetime2: DateTime2, offset: i16) -> Self {
403        Self { datetime2, offset }
404    }
405
406    /// The date and time part.
407    pub fn datetime2(self) -> DateTime2 {
408        self.datetime2
409    }
410
411    /// Number of minutes from UTC.
412    pub fn offset(self) -> i16 {
413        self.offset
414    }
415
416    pub(crate) async fn decode<R>(src: &mut R, n: usize, rlen: u8) -> crate::Result<Self>
417    where
418        R: SqlReadBytes + Unpin,
419    {
420        let datetime2 = DateTime2::decode(src, n, rlen as usize).await?;
421        let offset = src.read_i16_le().await?;
422
423        Ok(Self { datetime2, offset })
424    }
425}
426
427#[cfg(feature = "tds73")]
428#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
429impl Encode<BytesMut> for DateTimeOffset {
430    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
431        self.datetime2.encode(dst)?;
432        dst.put_i16_le(self.offset);
433
434        Ok(())
435    }
436}