1use std::{
2 convert::TryInto,
3 fmt::Display,
4 io::Write,
5 marker::PhantomData,
6 ops::{Div, Mul, Rem},
7};
8
9use arrow::{
10 array::{Array, PrimitiveArray},
11 datatypes::{
12 ArrowPrimitiveType, Time32MillisecondType, Time64MicrosecondType, Time64NanosecondType,
13 },
14};
15use chrono::{Datelike, NaiveDate};
16use odbc_api::{
17 buffers::{AnySliceMut, BufferDesc, TextColumnSliceMut},
18 sys::{Date, Time, Timestamp},
19};
20
21use crate::{WriterError, odbc_writer::WriteStrategy, reader::MappingError};
22
23pub fn days_since_epoch(date: &Date) -> i32 {
25 let unix_epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
26 let date =
27 NaiveDate::from_ymd_opt(date.year as i32, date.month as u32, date.day as u32).unwrap();
28 let duration = date.signed_duration_since(unix_epoch);
29 duration.num_days().try_into().unwrap()
30}
31
32pub fn seconds_since_epoch(from: &Timestamp) -> i64 {
33 let ndt = NaiveDate::from_ymd_opt(from.year as i32, from.month as u32, from.day as u32)
34 .unwrap()
35 .and_hms_opt(from.hour as u32, from.minute as u32, from.second as u32)
36 .unwrap();
37 ndt.and_utc().timestamp()
38}
39
40pub fn ms_since_epoch(from: &Timestamp) -> i64 {
41 let ndt = NaiveDate::from_ymd_opt(from.year as i32, from.month as u32, from.day as u32)
42 .unwrap()
43 .and_hms_nano_opt(
44 from.hour as u32,
45 from.minute as u32,
46 from.second as u32,
47 from.fraction,
48 )
49 .unwrap();
50 ndt.and_utc().timestamp_millis()
51}
52
53pub fn us_since_epoch(from: &Timestamp) -> i64 {
54 let ndt = NaiveDate::from_ymd_opt(from.year as i32, from.month as u32, from.day as u32)
55 .unwrap()
56 .and_hms_nano_opt(
57 from.hour as u32,
58 from.minute as u32,
59 from.second as u32,
60 from.fraction,
61 )
62 .unwrap();
63 ndt.and_utc().timestamp_micros()
64}
65
66pub fn ns_since_epoch(from: &Timestamp) -> Result<i64, MappingError> {
67 let ndt = NaiveDate::from_ymd_opt(from.year as i32, from.month as u32, from.day as u32)
68 .unwrap()
69 .and_hms_nano_opt(
70 from.hour as u32,
71 from.minute as u32,
72 from.second as u32,
73 from.fraction,
74 )
75 .unwrap();
76
77 ndt.and_utc()
80 .timestamp_nanos_opt()
81 .ok_or(MappingError::OutOfRangeTimestampNs { value: ndt })
82}
83
84pub fn epoch_to_date(from: i32) -> Date {
85 const OFFSET: i32 = 719_163;
87 let nd = NaiveDate::from_num_days_from_ce_opt(from + OFFSET).unwrap();
88 Date {
89 year: nd.year().try_into().unwrap(),
90 month: nd.month().try_into().unwrap(),
91 day: nd.day().try_into().unwrap(),
92 }
93}
94
95pub fn sec_since_midnight_to_time(from: i32) -> Time {
96 let unit_min = 60;
97 let unit_hour = unit_min * 60;
98 let hour = from / unit_hour;
99 let minute = (from % unit_hour) / unit_min;
100 let second = from % unit_min;
101 Time {
102 hour: hour.try_into().unwrap(),
103 minute: minute.try_into().unwrap(),
104 second: second.try_into().unwrap(),
105 }
106}
107
108pub struct NullableTimeAsText<P> {
109 _phantom: PhantomData<P>,
110}
111
112impl<P> NullableTimeAsText<P> {
113 pub fn new() -> Self {
114 Self {
115 _phantom: PhantomData,
116 }
117 }
118}
119
120pub trait TimePrimitive {
121 type Integer: From<i32>
122 + Copy
123 + Mul<Output = Self::Integer>
124 + Div<Output = Self::Integer>
125 + Rem<Output = Self::Integer>
126 + Display;
127 const SCALE: usize;
128 const PRECISION_FACTOR: Self::Integer;
129 const STR_LEN: usize;
130
131 fn insert_at(index: usize, from: Self::Integer, to: &mut TextColumnSliceMut<u8>) {
132 let sixty: Self::Integer = 60.into();
133 let unit_min = sixty * Self::PRECISION_FACTOR;
134 let unit_hour = unit_min * sixty;
135 let hour = from / unit_hour;
136 let minute = (from % unit_hour) / unit_min;
137 let second = (from % unit_min) / Self::PRECISION_FACTOR;
138 let fraction = from % Self::PRECISION_FACTOR;
139 write!(
140 to.set_mut(index, Self::STR_LEN),
141 "{hour:02}:{minute:02}:{second:02}.{fraction:0s$}",
142 s = Self::SCALE
143 )
144 .unwrap();
145 }
146}
147
148impl TimePrimitive for Time32MillisecondType {
149 type Integer = i32;
150 const SCALE: usize = 3;
151 const PRECISION_FACTOR: i32 = 1_000;
152 const STR_LEN: usize = 12;
154}
155
156impl TimePrimitive for Time64MicrosecondType {
157 type Integer = i64;
158
159 const SCALE: usize = 6;
160 const PRECISION_FACTOR: i64 = 1_000_000;
161 const STR_LEN: usize = 15;
163}
164
165impl TimePrimitive for Time64NanosecondType {
166 type Integer = i64;
167 const SCALE: usize = 9;
169 const PRECISION_FACTOR: i64 = 1_000_000_000;
170 const STR_LEN: usize = 18;
172}
173
174impl<P> WriteStrategy for NullableTimeAsText<P>
175where
176 P: ArrowPrimitiveType + TimePrimitive<Integer = <P as ArrowPrimitiveType>::Native>,
177{
178 fn buffer_desc(&self) -> BufferDesc {
179 BufferDesc::Text {
180 max_str_len: P::STR_LEN,
181 }
182 }
183
184 fn write_rows(
185 &self,
186 param_offset: usize,
187 column_buf: AnySliceMut<'_>,
188 array: &dyn Array,
189 ) -> Result<(), WriterError> {
190 let from = array.as_any().downcast_ref::<PrimitiveArray<P>>().unwrap();
191 let mut to = column_buf.as_text_view().unwrap();
192 for (index, elapsed_since_midnight) in from.iter().enumerate() {
193 if let Some(from) = elapsed_since_midnight {
194 P::insert_at(index + param_offset, from, &mut to)
195 } else {
196 to.set_cell(index + param_offset, None)
197 }
198 }
199 Ok(())
200 }
201}