sqlx_rxqlite/types/
chrono.rs1use std::fmt::Display;
2
3use crate::value::ValueRef;
4use crate::{
5 decode::Decode,
6 encode::{Encode, IsNull},
7 error::BoxDynError,
8 type_info::DataType,
9 types::Type,
10 RXQLite, RXQLiteTypeInfo, RXQLiteValueRef,
11 RXQLiteValueData,
12};
13use chrono::FixedOffset;
14use chrono::{
15 DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, Offset, SecondsFormat, TimeZone, Utc,
16};
17
18impl<Tz: TimeZone> Type<RXQLite> for DateTime<Tz> {
19 fn type_info() -> RXQLiteTypeInfo {
20 RXQLiteTypeInfo(DataType::Datetime)
21 }
22
23 fn compatible(ty: &RXQLiteTypeInfo) -> bool {
24 <NaiveDateTime as Type<RXQLite>>::compatible(ty)
25 }
26}
27
28impl Type<RXQLite> for NaiveDateTime {
29 fn type_info() -> RXQLiteTypeInfo {
30 RXQLiteTypeInfo(DataType::Datetime)
31 }
32
33 fn compatible(ty: &RXQLiteTypeInfo) -> bool {
34 matches!(
35 ty.0,
36 DataType::Datetime | DataType::Text | DataType::Int64 | DataType::Int | DataType::Float
37 )
38 }
39}
40
41impl Type<RXQLite> for NaiveDate {
42 fn type_info() -> RXQLiteTypeInfo {
43 RXQLiteTypeInfo(DataType::Date)
44 }
45
46 fn compatible(ty: &RXQLiteTypeInfo) -> bool {
47 matches!(ty.0, DataType::Date | DataType::Text)
48 }
49}
50
51impl Type<RXQLite> for NaiveTime {
52 fn type_info() -> RXQLiteTypeInfo {
53 RXQLiteTypeInfo(DataType::Time)
54 }
55
56 fn compatible(ty: &RXQLiteTypeInfo) -> bool {
57 matches!(ty.0, DataType::Time | DataType::Text)
58 }
59}
60
61impl<Tz: TimeZone> Encode<'_, RXQLite> for DateTime<Tz>
62where
63 Tz::Offset: Display,
64{
65 fn encode_by_ref(&self, buf: &mut Vec<rxqlite_common::Value>) -> IsNull {
66 Encode::<RXQLite>::encode(self.to_rfc3339_opts(SecondsFormat::AutoSi, false), buf)
67 }
68}
69
70impl Encode<'_, RXQLite> for NaiveDateTime {
71 fn encode_by_ref(&self, buf: &mut Vec<rxqlite_common::Value>) -> IsNull {
72 Encode::<RXQLite>::encode(self.format("%F %T%.f").to_string(), buf)
73 }
74}
75
76impl Encode<'_, RXQLite> for NaiveDate {
77 fn encode_by_ref(&self, buf: &mut Vec<rxqlite_common::Value>) -> IsNull {
78 Encode::<RXQLite>::encode(self.format("%F").to_string(), buf)
79 }
80}
81
82impl Encode<'_, RXQLite> for NaiveTime {
83 fn encode_by_ref(&self, buf: &mut Vec<rxqlite_common::Value>) -> IsNull {
84 Encode::<RXQLite>::encode(self.format("%T%.f").to_string(), buf)
85 }
86}
87
88impl<'r> Decode<'r, RXQLite> for DateTime<Utc> {
89 fn decode(value: RXQLiteValueRef<'r>) -> Result<Self, BoxDynError> {
90 Ok(Utc.from_utc_datetime(&decode_datetime(value)?.naive_utc()))
91 }
92}
93
94impl<'r> Decode<'r, RXQLite> for DateTime<Local> {
95 fn decode(value: RXQLiteValueRef<'r>) -> Result<Self, BoxDynError> {
96 Ok(Local.from_utc_datetime(&decode_datetime(value)?.naive_utc()))
97 }
98}
99
100impl<'r> Decode<'r, RXQLite> for DateTime<FixedOffset> {
101 fn decode(value: RXQLiteValueRef<'r>) -> Result<Self, BoxDynError> {
102 decode_datetime(value)
103 }
104}
105
106fn decode_datetime(value: RXQLiteValueRef<'_>) -> Result<DateTime<FixedOffset>, BoxDynError> {
107 let dt = match value.type_info().0 {
108 DataType::Null => decode_datetime_from_text(&value.text()?),
109 DataType::Text => decode_datetime_from_text(&value.text()?),
110 DataType::Int | DataType::Int64 => decode_datetime_from_int(value.int64()?),
111 DataType::Float => decode_datetime_from_float(value.double()?),
112 DataType::Datetime => {
113 match value.0 {
114 RXQLiteValueData::Value(value)=>{
115 match &*value.handle {
116 rxqlite_common::Value::DateTime(utc) => {
117 let fixed_offset_time = utc.with_timezone(&Utc.fix());
118 Some(fixed_offset_time)
119 }
120 _=>None,
121 }
122 }
123 }
124 }
125 _ => None,
126 };
127
128 if let Some(dt) = dt {
129 Ok(dt)
130 } else {
131 Err(format!("invalid datetime: {}", value.text()?).into())
132 }
133}
134
135fn decode_datetime_from_text(value: &str) -> Option<DateTime<FixedOffset>> {
136 if let Ok(dt) = DateTime::parse_from_rfc3339(value) {
137 return Some(dt);
138 }
139 if let Ok::<DateTime<FixedOffset>,_>(dt) = value.parse() {
140 return Some(dt);
141 }
142 let sqlite_datetime_formats = &[
145 "%F %T%.f",
147 "%F %R",
149 "%F %RZ",
150 "%F %R%:z",
151 "%F %T%.fZ",
152 "%F %T%.f%:z",
153 "%FT%R",
154 "%FT%RZ",
155 "%FT%R%:z",
156 "%FT%T%.f",
157 "%FT%T%.fZ",
158 "%FT%T%.f%:z",
159 ];
160
161 for format in sqlite_datetime_formats {
162 if let Ok(dt) = DateTime::parse_from_str(value, format) {
163 return Some(dt);
164 }
165
166 if let Ok(dt) = NaiveDateTime::parse_from_str(value, format) {
167 return Some(Utc.fix().from_utc_datetime(&dt));
168 }
169 }
170
171 None
172}
173
174fn decode_datetime_from_int(value: i64) -> Option<DateTime<FixedOffset>> {
175 Utc.fix().timestamp_opt(value, 0).single()
176}
177
178fn decode_datetime_from_float(value: f64) -> Option<DateTime<FixedOffset>> {
179 let epoch_in_julian_days = 2_440_587.5;
180 let seconds_in_day = 86400.0;
181 let timestamp = (value - epoch_in_julian_days) * seconds_in_day;
182 let seconds = timestamp as i64;
183 let nanos = (timestamp.fract() * 1E9) as u32;
184
185 Utc.fix().timestamp_opt(seconds, nanos).single()
186}
187
188impl<'r> Decode<'r, RXQLite> for NaiveDateTime {
189 fn decode(value: RXQLiteValueRef<'r>) -> Result<Self, BoxDynError> {
190 Ok(decode_datetime(value)?.naive_local())
191 }
192}
193
194impl<'r> Decode<'r, RXQLite> for NaiveDate {
195 fn decode(value: RXQLiteValueRef<'r>) -> Result<Self, BoxDynError> {
196 Ok(NaiveDate::parse_from_str(&value.text()?, "%F")?)
197 }
198}
199
200impl<'r> Decode<'r, RXQLite> for NaiveTime {
201 fn decode(value: RXQLiteValueRef<'r>) -> Result<Self, BoxDynError> {
202 let value = value.text()?;
203
204 #[rustfmt::skip] let sqlite_time_formats = &[
208 "%T.f", "%T%.f",
210 "%R", "%RZ", "%T%.fZ", "%R%:z", "%T%.f%:z",
212 ];
213
214 for format in sqlite_time_formats {
215 if let Ok(dt) = NaiveTime::parse_from_str(&value, format) {
216 return Ok(dt);
217 }
218 }
219
220 Err(format!("invalid time: {value}").into())
221 }
222}