tiberius/tds/time/
chrono.rs

1//! Mappings between TDS and and Chrono types (with `chrono` feature flag
2//! enabled).
3//!
4//! The chrono library offers better ergonomy, but is known to hold certain
5//! security vulnerabilities. The code here is for legacy purposes, please use
6//! `time` crate for greenfield projects.
7
8#[cfg(not(feature = "tds73"))]
9use super::DateTime as DateTime1;
10#[cfg(feature = "tds73")]
11use super::{Date, DateTime2, DateTimeOffset, Time};
12use crate::tds::codec::ColumnData;
13#[cfg(feature = "tds73")]
14#[cfg_attr(feature = "docs", doc(cfg(feature = "tds73")))]
15pub use chrono::offset::{FixedOffset, Utc};
16pub use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};
17#[cfg(feature = "tds73")]
18use std::ops::Sub;
19
20#[inline]
21fn from_days(days: i64, start_year: i32) -> NaiveDate {
22    NaiveDate::from_ymd_opt(start_year, 1, 1).unwrap() + chrono::Duration::days(days)
23}
24
25#[inline]
26fn from_sec_fragments(sec_fragments: i64) -> NaiveTime {
27    NaiveTime::from_hms_opt(0, 0, 0).unwrap()
28        + chrono::Duration::nanoseconds(sec_fragments * (1e9 as i64) / 300)
29}
30
31#[inline]
32#[cfg(feature = "tds73")]
33fn from_mins(mins: u32) -> NaiveTime {
34    NaiveTime::from_num_seconds_from_midnight_opt(mins, 0).unwrap()
35}
36
37#[inline]
38fn to_days(date: NaiveDate, start_year: i32) -> i64 {
39    date.signed_duration_since(NaiveDate::from_ymd_opt(start_year, 1, 1).unwrap())
40        .num_days()
41}
42
43#[inline]
44#[cfg(not(feature = "tds73"))]
45fn to_sec_fragments(time: NaiveTime) -> i64 {
46    time.signed_duration_since(NaiveTime::from_hms_opt(0, 0, 0).unwrap())
47        .num_nanoseconds()
48        .unwrap()
49        * 300
50        / (1e9 as i64)
51}
52
53#[cfg(feature = "tds73")]
54from_sql!(
55    NaiveDateTime:
56        ColumnData::SmallDateTime(ref dt) => dt.map(|dt| NaiveDateTime::new(
57            from_days(dt.days as i64, 1900),
58            from_mins(dt.seconds_fragments as u32 * 60),
59        )),
60        ColumnData::DateTime2(ref dt) => dt.map(|dt| NaiveDateTime::new(
61            from_days(dt.date.days() as i64, 1),
62            NaiveTime::from_hms_opt(0,0,0).unwrap() + chrono::Duration::nanoseconds(dt.time.increments as i64 * 10i64.pow(9 - dt.time.scale as u32))
63        )),
64        ColumnData::DateTime(ref dt) => dt.map(|dt| NaiveDateTime::new(
65            from_days(dt.days as i64, 1900),
66            from_sec_fragments(dt.seconds_fragments as i64)
67        ));
68    NaiveTime:
69        ColumnData::Time(ref time) => time.map(|time| {
70            let ns = time.increments as i64 * 10i64.pow(9 - time.scale as u32);
71            NaiveTime::from_hms_opt(0,0,0).unwrap() + chrono::Duration::nanoseconds(ns)
72        });
73    NaiveDate:
74        ColumnData::Date(ref date) => date.map(|date| from_days(date.days() as i64, 1));
75    chrono::DateTime<Utc>:
76        ColumnData::DateTimeOffset(ref dto) => dto.map(|dto| {
77            let date = from_days(dto.datetime2.date.days() as i64, 1);
78            let ns = dto.datetime2.time.increments as i64 * 10i64.pow(9 - dto.datetime2.time.scale as u32);
79            let time = NaiveTime::from_hms_opt(0,0,0).unwrap() + chrono::Duration::nanoseconds(ns);
80
81            let offset = chrono::Duration::minutes(dto.offset as i64);
82            let naive = NaiveDateTime::new(date, time).sub(offset);
83
84            chrono::DateTime::from_naive_utc_and_offset(naive, Utc)
85        }),
86        ColumnData::DateTime2(ref dt2) => dt2.map(|dt2| {
87            let date = from_days(dt2.date.days() as i64, 1);
88            let ns = dt2.time.increments as i64 * 10i64.pow(9 - dt2.time.scale as u32);
89            let time = NaiveTime::from_hms_opt(0,0,0).unwrap() + chrono::Duration::nanoseconds(ns);
90            let naive = NaiveDateTime::new(date, time);
91
92            chrono::DateTime::from_naive_utc_and_offset(naive, Utc)
93        });
94    chrono::DateTime<FixedOffset>: ColumnData::DateTimeOffset(ref dto) => dto.map(|dto| {
95        let date = from_days(dto.datetime2.date.days() as i64, 1);
96        let ns = dto.datetime2.time.increments as i64 * 10i64.pow(9 - dto.datetime2.time.scale as u32);
97        let time = NaiveTime::from_hms_opt(0,0,0).unwrap() + chrono::Duration::nanoseconds(ns);
98
99        let offset = FixedOffset::east_opt((dto.offset as i32) * 60).unwrap();
100        let naive = NaiveDateTime::new(date, time);
101
102        chrono::DateTime::from_naive_utc_and_offset(naive, offset)
103    })
104);
105
106#[cfg(feature = "tds73")]
107to_sql!(self_,
108        NaiveDate: (ColumnData::Date, Date::new(to_days(*self_, 1) as u32));
109        NaiveTime: (ColumnData::Time, {
110            use chrono::Timelike;
111
112            let nanos = self_.num_seconds_from_midnight() as u64 * 1e9 as u64 + self_.nanosecond() as u64;
113            let increments = nanos / 100;
114
115            Time {increments, scale: 7}
116        });
117        NaiveDateTime: (ColumnData::DateTime2, {
118            use chrono::Timelike;
119
120            let time = self_.time();
121            let nanos = time.num_seconds_from_midnight() as u64 * 1e9 as u64 + time.nanosecond() as u64;
122            let increments = nanos / 100;
123
124            let date = Date::new(to_days(self_.date(), 1) as u32);
125            let time = Time {increments, scale: 7};
126
127            DateTime2::new(date, time)
128        });
129        chrono::DateTime<Utc>: (ColumnData::DateTime2, {
130            use chrono::Timelike;
131
132            let naive = self_.naive_utc();
133            let time = naive.time();
134            let nanos = time.num_seconds_from_midnight() as u64 * 1e9 as u64 + time.nanosecond() as u64;
135
136            let date = Date::new(to_days(naive.date(), 1) as u32);
137            let time = Time {increments: nanos / 100, scale: 7};
138
139            DateTime2::new(date, time)
140        });
141        chrono::DateTime<FixedOffset>: (ColumnData::DateTimeOffset, {
142            use chrono::Timelike;
143
144            let naive = self_.naive_utc();
145            let time = naive.time();
146            let nanos = time.num_seconds_from_midnight() as u64 * 1e9 as u64 + time.nanosecond() as u64;
147
148            let date = Date::new(to_days(naive.date(), 1) as u32);
149            let time = Time { increments: nanos / 100, scale: 7 };
150
151            let tz = self_.timezone();
152            let offset = (tz.local_minus_utc() / 60) as i16;
153
154            DateTimeOffset::new(DateTime2::new(date, time), offset)
155        });
156);
157
158#[cfg(feature = "tds73")]
159into_sql!(self_,
160        NaiveDate: (ColumnData::Date, Date::new(to_days(self_, 1) as u32));
161        NaiveTime: (ColumnData::Time, {
162            use chrono::Timelike;
163
164            let nanos = self_.num_seconds_from_midnight() as u64 * 1e9 as u64 + self_.nanosecond() as u64;
165            let increments = nanos / 100;
166
167            Time {increments, scale: 7}
168        });
169        NaiveDateTime: (ColumnData::DateTime2, {
170            use chrono::Timelike;
171
172            let time = self_.time();
173            let nanos = time.num_seconds_from_midnight() as u64 * 1e9 as u64 + time.nanosecond() as u64;
174            let increments = nanos / 100;
175
176            let date = Date::new(to_days(self_.date(), 1) as u32);
177            let time = Time {increments, scale: 7};
178
179            DateTime2::new(date, time)
180        });
181        chrono::DateTime<Utc>: (ColumnData::DateTime2, {
182            use chrono::Timelike;
183
184            let naive = self_.naive_utc();
185            let time = naive.time();
186            let nanos = time.num_seconds_from_midnight() as u64 * 1e9 as u64 + time.nanosecond() as u64;
187
188            let date = Date::new(to_days(naive.date(), 1) as u32);
189            let time = Time {increments: nanos / 100, scale: 7};
190
191            DateTime2::new(date, time)
192        });
193        chrono::DateTime<FixedOffset>: (ColumnData::DateTimeOffset, {
194            use chrono::Timelike;
195
196            let naive = self_.naive_utc();
197            let time = naive.time();
198            let nanos = time.num_seconds_from_midnight() as u64 * 1e9 as u64 + time.nanosecond() as u64;
199
200            let date = Date::new(to_days(naive.date(), 1) as u32);
201            let time = Time { increments: nanos / 100, scale: 7 };
202
203            let tz = self_.timezone();
204            let offset = (tz.local_minus_utc() / 60) as i16;
205
206            DateTimeOffset::new(DateTime2::new(date, time), offset)
207        });
208);
209
210#[cfg(not(feature = "tds73"))]
211to_sql!(self_,
212        NaiveDateTime: (ColumnData::DateTime, {
213            let date = self_.date();
214            let time = self_.time();
215
216            let days = to_days(date, 1900) as i32;
217            let seconds_fragments = to_sec_fragments(time);
218
219            DateTime1::new(days, seconds_fragments as u32)
220        });
221);
222
223#[cfg(not(feature = "tds73"))]
224into_sql!(self_,
225        NaiveDateTime: (ColumnData::DateTime, {
226            let date = self_.date();
227            let time = self_.time();
228
229            let days = to_days(date, 1900) as i32;
230            let seconds_fragments = to_sec_fragments(time);
231
232            DateTime1::new(days, seconds_fragments as u32)
233        });
234);
235
236#[cfg(not(feature = "tds73"))]
237from_sql!(
238    NaiveDateTime:
239        ColumnData::DateTime(ref dt) => dt.map(|dt| NaiveDateTime::new(
240            from_days(dt.days as i64, 1900),
241            from_sec_fragments(dt.seconds_fragments as i64)
242        ))
243);