1use chrono::{FixedOffset, NaiveDateTime, Timelike};
2use fastdate::offset_sec;
3use rbdc::datetime::DateTime;
4use rbdc::Error;
5use rbs::{value, Value};
6use tiberius::numeric::BigDecimal;
7use tiberius::ColumnData;
8
9pub trait Decode {
10 fn decode(row: &ColumnData<'static>) -> Result<Value, Error>;
11}
12
13impl Decode for Value {
14 fn decode(row: &ColumnData<'static>) -> Result<Value, Error> {
15 Ok(match row {
16 ColumnData::U8(v) => match v {
17 None => Value::Null,
18 Some(v) => Value::U32(v.clone() as u32),
19 },
20 ColumnData::I16(v) => match v {
21 None => Value::Null,
22 Some(v) => Value::I32(v.clone() as i32),
23 },
24 ColumnData::I32(v) => match v {
25 None => Value::Null,
26 Some(v) => Value::I32(v.clone()),
27 },
28 ColumnData::I64(v) => match v {
29 None => Value::Null,
30 Some(v) => Value::I64(v.clone()),
31 },
32 ColumnData::F32(v) => match v {
33 None => Value::Null,
34 Some(v) => Value::F32(v.clone()),
35 },
36 ColumnData::F64(v) => match v {
37 None => Value::Null,
38 Some(v) => Value::F64(v.clone()),
39 },
40 ColumnData::Bit(v) => match v {
41 None => Value::Null,
42 Some(v) => Value::Bool(v.clone()),
43 },
44 ColumnData::String(v) => match v {
45 None => Value::Null,
46 Some(v) => Value::String(v.to_string()),
47 },
48 ColumnData::Guid(v) => match v {
49 None => Value::Null,
50 Some(v) => Value::String(v.to_string()).into_ext("Uuid"),
51 },
52 ColumnData::Binary(v) => match v {
53 None => Value::Null,
54 Some(v) => Value::Binary(v.to_vec()),
55 },
56 ColumnData::Numeric(v) => match v {
57 None => Value::Null,
58 Some(_) => {
59 let v: tiberius::Result<Option<BigDecimal>> = tiberius::FromSql::from_sql(row);
60 match v {
61 Ok(v) => match v {
62 None => Value::Null,
63 Some(v) => Value::String(v.to_string()).into_ext("Decimal"),
64 },
65 Err(e) => {
66 return Err(Error::from(e.to_string()));
67 }
68 }
69 }
70 },
71 ColumnData::Xml(v) => match v {
72 None => Value::Null,
73 Some(v) => Value::String(v.to_string()).into_ext("Xml"),
74 },
75 ColumnData::DateTime(v) => match v {
76 None => Value::Null,
77 Some(_) => {
78 let v: tiberius::Result<Option<NaiveDateTime>> =
79 tiberius::FromSql::from_sql(row);
80 match v {
81 Ok(v) => match v {
82 None => Value::Null,
83 Some(v) => value!(DateTime(
84 <fastdate::DateTime as DateTimeFromNativeDatetime>::from(v)
85 )),
86 },
87 Err(e) => {
88 return Err(Error::from(e.to_string()));
89 }
90 }
91 }
92 },
93 ColumnData::SmallDateTime(m) => match m {
94 None => Value::Null,
95 Some(_) => {
96 let v: tiberius::Result<Option<NaiveDateTime>> =
97 tiberius::FromSql::from_sql(row);
98 match v {
99 Ok(v) => match v {
100 None => Value::Null,
101 Some(v) => value!(DateTime(
102 <fastdate::DateTime as DateTimeFromNativeDatetime>::from(v)
103 )),
104 },
105 Err(e) => {
106 return Err(Error::from(e.to_string()));
107 }
108 }
109 }
110 },
111 ColumnData::Time(v) => match v {
112 None => Value::Null,
113 Some(_) => {
114 let v: tiberius::Result<Option<chrono::NaiveTime>> =
115 tiberius::FromSql::from_sql(row);
116 match v {
117 Ok(v) => match v {
118 None => Value::Null,
119 Some(v) => Value::String(v.to_string()).into_ext("Time"),
120 },
121 Err(e) => {
122 return Err(Error::from(e.to_string()));
123 }
124 }
125 }
126 },
127 ColumnData::Date(v) => match v {
128 None => Value::Null,
129 Some(_) => {
130 let v: tiberius::Result<Option<chrono::NaiveDate>> =
131 tiberius::FromSql::from_sql(row);
132 match v {
133 Ok(v) => match v {
134 None => Value::Null,
135 Some(v) => Value::String(v.to_string()).into_ext("Date"),
136 },
137 Err(e) => {
138 return Err(Error::from(e.to_string()));
139 }
140 }
141 }
142 },
143 ColumnData::DateTime2(v) => match v {
144 None => Value::Null,
145 Some(_) => {
146 let v: tiberius::Result<Option<NaiveDateTime>> =
147 tiberius::FromSql::from_sql(row);
148 match v {
149 Ok(v) => match v {
150 None => Value::Null,
151 Some(v) => value!(DateTime(
152 <fastdate::DateTime as DateTimeFromNativeDatetime>::from(v)
153 )),
154 },
155 Err(e) => {
156 return Err(Error::from(e.to_string()));
157 }
158 }
159 }
160 },
161 ColumnData::DateTimeOffset(v) => match v {
162 None => Value::Null,
163 Some(_) => {
164 let v: tiberius::Result<Option<chrono::DateTime<FixedOffset>>> =
165 tiberius::FromSql::from_sql(row);
166 match v {
167 Ok(v) => match v {
168 None => Value::Null,
169 Some(v) => {
170 let dt = match v.timestamp_nanos_opt() {
172 Some(nanos) => DateTime(
173 fastdate::DateTime::from_timestamp_nano(
174 nanos as i128
175 - (v.offset().utc_minus_local() * 60) as i128,
176 )
177 .set_offset(v.offset().utc_minus_local() * 60),
178 ),
179 None => {
180 let timestamp_secs = v.timestamp();
182 let subsec_nanos = v.nanosecond();
183 DateTime(
184 fastdate::DateTime::from_timestamp_nano(
185 (timestamp_secs as i128) * 1_000_000_000
186 + (subsec_nanos as i128)
187 - (v.offset().utc_minus_local() * 60) as i128,
188 )
189 .set_offset(v.offset().utc_minus_local() * 60),
190 )
191 }
192 };
193 value!(dt)
194 }
195 },
196 Err(e) => {
197 return Err(Error::from(e.to_string()));
198 }
199 }
200 }
201 },
202 })
203 }
204}
205
206pub trait DateTimeFromNativeDatetime {
207 fn from(arg: chrono::NaiveDateTime) -> Self;
208}
209
210pub trait DateTimeFromDateTimeFixedOffset {
211 fn from(arg: chrono::DateTime<FixedOffset>) -> Self;
212}
213
214impl DateTimeFromNativeDatetime for fastdate::DateTime {
215 fn from(arg: NaiveDateTime) -> Self {
216 match arg.and_utc().timestamp_nanos_opt() {
218 Some(nanos) => fastdate::DateTime::from_timestamp_nano(nanos as i128)
219 .set_offset(offset_sec())
220 .add_sub_sec(-offset_sec() as i64),
221 None => {
222 let timestamp_secs = arg.and_utc().timestamp();
224 let subsec_nanos = arg.nanosecond();
225 fastdate::DateTime::from_timestamp_nano(
226 (timestamp_secs as i128) * 1_000_000_000 + (subsec_nanos as i128),
227 )
228 .set_offset(offset_sec())
229 .add_sub_sec(-offset_sec() as i64)
230 }
231 }
232 }
233}
234
235impl DateTimeFromDateTimeFixedOffset for fastdate::DateTime {
236 fn from(arg: chrono::DateTime<FixedOffset>) -> Self {
237 match arg.timestamp_nanos_opt() {
239 Some(nanos) => fastdate::DateTime::from_timestamp_nano(nanos as i128)
240 .set_offset(arg.offset().local_minus_utc()),
241 None => {
242 let timestamp_secs = arg.timestamp();
244 let subsec_nanos = arg.nanosecond();
245 fastdate::DateTime::from_timestamp_nano(
246 (timestamp_secs as i128) * 1_000_000_000 + (subsec_nanos as i128),
247 )
248 .set_offset(arg.offset().local_minus_utc())
249 }
250 }
251 }
252}
253
254#[cfg(test)]
255mod test {
256 use crate::decode::{DateTimeFromDateTimeFixedOffset, DateTimeFromNativeDatetime};
257 use chrono::{FixedOffset, NaiveDateTime};
258 use fastdate::DateTime;
259
260 #[test]
261 fn test_decode_far_future_date() {
262 use chrono::{NaiveDate, NaiveTime};
265
266 let date = NaiveDate::from_ymd_opt(2500, 12, 31).unwrap();
267 let time = NaiveTime::from_hms_opt(23, 59, 59).unwrap();
268 let dt = NaiveDateTime::new(date, time);
269
270 let de = <DateTime as DateTimeFromNativeDatetime>::from(dt);
272 println!("Far future date: {}", de.to_string());
273
274 assert!(de.to_string().contains("2500"));
276 }
277
278 #[test]
279 fn test_decode_far_future_date_with_offset() {
280 use chrono::{NaiveDate, NaiveTime};
282
283 let date = NaiveDate::from_ymd_opt(2500, 6, 15).unwrap(); let time = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
285 let dt = NaiveDateTime::new(date, time);
286 let offset = FixedOffset::east_opt(8 * 60 * 60).unwrap(); let dt_with_offset = chrono::DateTime::from_naive_utc_and_offset(dt, offset);
288
289 let de = <DateTime as DateTimeFromDateTimeFixedOffset>::from(dt_with_offset);
291 println!("Far future date with offset: {}", de.to_string());
292
293 assert!(de.to_string().contains("2500"));
295 }
296}