tiberius_rustls/tds/time/
time.rs

1//! Mappings between TDS and and time crate types (with `time` feature flag
2//! enabled).
3//!
4//! The time library offers better ergonomy and are highly recommended if
5//! needing to modify and deal with date and time in SQL Server.
6
7use std::time::Duration;
8pub use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
9
10use crate::tds::codec::ColumnData;
11
12#[inline]
13fn from_days(days: u64, start_year: i32) -> Date {
14    Date::from_calendar_date(start_year, Month::January, 1).unwrap()
15        + Duration::from_secs(60 * 60 * 24 * days)
16}
17
18#[inline]
19#[cfg(feature = "tds73")]
20fn from_secs(secs: u64) -> Time {
21    Time::from_hms(0, 0, 0).unwrap() + Duration::from_secs(secs)
22}
23
24#[inline]
25fn from_sec_fragments(sec_fragments: u64) -> Time {
26    Time::from_hms(0, 0, 0).unwrap() + Duration::from_nanos(sec_fragments * (1e9 as u64) / 300)
27}
28
29#[inline]
30fn to_days(date: Date, start_year: i32) -> i64 {
31    (date - Date::from_calendar_date(start_year, Month::January, 1).unwrap()).whole_days()
32}
33
34#[inline]
35#[cfg(not(feature = "tds73"))]
36fn to_sec_fragments(from: Time) -> i64 {
37    let nanos: i64 = (from - Time::from_hms(0, 0, 0).unwrap())
38        .whole_nanoseconds()
39        .try_into()
40        .unwrap();
41
42    nanos * 300 / (1e9 as i64)
43}
44
45#[cfg(feature = "tds73")]
46from_sql!(
47    PrimitiveDateTime:
48        ColumnData::SmallDateTime(ref dt) => dt.map(|dt| PrimitiveDateTime::new(
49            from_days(dt.days as u64, 1900),
50            from_secs(dt.seconds_fragments as u64 * 60),
51        )),
52        ColumnData::DateTime2(ref dt) => dt.map(|dt| PrimitiveDateTime::new(
53            from_days(dt.date.days() as u64, 1),
54            Time::from_hms(0,0,0).unwrap() + Duration::from_nanos(dt.time.increments * 10u64.pow(9 - dt.time.scale as u32))
55        )),
56        ColumnData::DateTime(ref dt) => dt.map(|dt| PrimitiveDateTime::new(
57            from_days(dt.days as u64, 1900),
58            from_sec_fragments(dt.seconds_fragments as u64)
59        ));
60    Time:
61        ColumnData::Time(ref time) => time.map(|time| {
62            let ns = time.increments * 10u64.pow(9 - time.scale as u32);
63            Time::from_hms(0,0,0).unwrap() + Duration::from_nanos(ns)
64        });
65    Date:
66        ColumnData::Date(ref date) => date.map(|date| from_days(date.days() as u64, 1));
67    OffsetDateTime:
68        ColumnData::DateTimeOffset(ref dto) => dto.map(|dto| {
69            let date = from_days(dto.datetime2.date.days() as u64, 1);
70            let dt = dto.datetime2;
71
72            let time = Time::from_hms(0,0,0).unwrap()
73                + Duration::from_nanos(dt.time.increments * 10u64.pow(9 - dt.time.scale as u32));
74
75            let offset = UtcOffset::from_whole_seconds(dto.offset as i32 * 60).unwrap();
76
77            date.with_time(time).assume_utc().to_offset(offset)
78        })
79);
80
81#[cfg(feature = "tds73")]
82to_sql!(self_,
83        Date: (ColumnData::Date, super::Date::new(to_days(*self_, 1) as u32));
84        Time: (ColumnData::Time, {
85            let nanos: u64 = (*self_ - Time::from_hms(0, 0, 0).unwrap()).whole_nanoseconds().try_into().unwrap();
86            let increments = nanos / 100;
87
88            super::Time {increments, scale: 7}
89        });
90        PrimitiveDateTime: (ColumnData::DateTime2, {
91            let time = self_.time();
92            let nanos: u64 = (time - Time::from_hms(0, 0, 0).unwrap()).whole_nanoseconds().try_into().unwrap();
93            let increments = nanos / 100;
94
95            let date = super::Date::new(to_days(self_.date(), 1) as u32);
96            let time = super::Time {increments, scale: 7};
97
98            super::DateTime2::new(date, time)
99        });
100        OffsetDateTime: (ColumnData::DateTimeOffset, {
101            let tz = self_.offset();
102            let offset = (tz.whole_seconds() / 60) as i16;
103
104            let utc_date = self_.to_offset(UtcOffset::UTC);
105
106            let nanos: u64 = (utc_date.time() - Time::from_hms(0, 0, 0).unwrap()).whole_nanoseconds().try_into().unwrap();
107
108            let date = super::Date::new(to_days(utc_date.date(), 1) as u32);
109            let time = super::Time { increments: nanos / 100, scale: 7 };
110
111            super::DateTimeOffset::new(super::DateTime2::new(date, time), offset)
112        });
113);
114
115#[cfg(not(feature = "tds73"))]
116to_sql!(self_,
117        PrimitiveDateTime: (ColumnData::DateTime, {
118            let date = self_.date();
119            let time = self_.time();
120
121            let days = to_days(date, 1900) as i32;
122            let seconds_fragments = to_sec_fragments(time);
123
124            super::DateTime::new(days, seconds_fragments as u32)
125        });
126);
127
128#[cfg(not(feature = "tds73"))]
129from_sql!(
130    PrimitiveDateTime:
131    ColumnData::DateTime(ref dt) => dt.map(|dt| {
132        from_days(dt.days as u64, 1900).with_time(from_sec_fragments(dt.seconds_fragments as u64))
133    })
134);