sqlx_sqlite/types/
time.rs

1use crate::value::ValueRef;
2use crate::{
3    decode::Decode,
4    encode::{Encode, IsNull},
5    error::BoxDynError,
6    type_info::DataType,
7    types::Type,
8    Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef,
9};
10use time::format_description::{well_known::Rfc3339, BorrowedFormatItem};
11use time::macros::format_description as fd;
12use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
13
14impl Type<Sqlite> for OffsetDateTime {
15    fn type_info() -> SqliteTypeInfo {
16        SqliteTypeInfo(DataType::Datetime)
17    }
18
19    fn compatible(ty: &SqliteTypeInfo) -> bool {
20        <PrimitiveDateTime as Type<Sqlite>>::compatible(ty)
21    }
22}
23
24impl Type<Sqlite> for PrimitiveDateTime {
25    fn type_info() -> SqliteTypeInfo {
26        SqliteTypeInfo(DataType::Datetime)
27    }
28
29    fn compatible(ty: &SqliteTypeInfo) -> bool {
30        matches!(
31            ty.0,
32            DataType::Datetime | DataType::Text | DataType::Integer | DataType::Int4
33        )
34    }
35}
36
37impl Type<Sqlite> for Date {
38    fn type_info() -> SqliteTypeInfo {
39        SqliteTypeInfo(DataType::Date)
40    }
41
42    fn compatible(ty: &SqliteTypeInfo) -> bool {
43        matches!(ty.0, DataType::Date | DataType::Text)
44    }
45}
46
47impl Type<Sqlite> for Time {
48    fn type_info() -> SqliteTypeInfo {
49        SqliteTypeInfo(DataType::Time)
50    }
51
52    fn compatible(ty: &SqliteTypeInfo) -> bool {
53        matches!(ty.0, DataType::Time | DataType::Text)
54    }
55}
56
57impl Encode<'_, Sqlite> for OffsetDateTime {
58    fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
59        Encode::<Sqlite>::encode(self.format(&Rfc3339)?, buf)
60    }
61}
62
63impl Encode<'_, Sqlite> for PrimitiveDateTime {
64    fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
65        let format = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
66        Encode::<Sqlite>::encode(self.format(&format)?, buf)
67    }
68}
69
70impl Encode<'_, Sqlite> for Date {
71    fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
72        let format = fd!("[year]-[month]-[day]");
73        Encode::<Sqlite>::encode(self.format(&format)?, buf)
74    }
75}
76
77impl Encode<'_, Sqlite> for Time {
78    fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
79        let format = fd!("[hour]:[minute]:[second].[subsecond]");
80        Encode::<Sqlite>::encode(self.format(&format)?, buf)
81    }
82}
83
84impl<'r> Decode<'r, Sqlite> for OffsetDateTime {
85    fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
86        decode_offset_datetime(value)
87    }
88}
89
90impl<'r> Decode<'r, Sqlite> for PrimitiveDateTime {
91    fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
92        decode_datetime(value)
93    }
94}
95
96impl<'r> Decode<'r, Sqlite> for Date {
97    fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
98        Ok(Date::parse(value.text()?, &fd!("[year]-[month]-[day]"))?)
99    }
100}
101
102impl<'r> Decode<'r, Sqlite> for Time {
103    fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
104        let value = value.text()?;
105
106        let sqlite_time_formats = &[
107            fd!("[hour]:[minute]:[second].[subsecond]"),
108            fd!("[hour]:[minute]:[second]"),
109            fd!("[hour]:[minute]"),
110        ];
111
112        for format in sqlite_time_formats {
113            if let Ok(dt) = Time::parse(value, &format) {
114                return Ok(dt);
115            }
116        }
117
118        Err(format!("invalid time: {value}").into())
119    }
120}
121
122fn decode_offset_datetime(value: SqliteValueRef<'_>) -> Result<OffsetDateTime, BoxDynError> {
123    let dt = match value.type_info().0 {
124        DataType::Text => decode_offset_datetime_from_text(value.text()?),
125        DataType::Int4 | DataType::Integer => {
126            Some(OffsetDateTime::from_unix_timestamp(value.int64())?)
127        }
128
129        _ => None,
130    };
131
132    if let Some(dt) = dt {
133        Ok(dt)
134    } else {
135        Err(format!("invalid offset datetime: {}", value.text()?).into())
136    }
137}
138
139fn decode_offset_datetime_from_text(value: &str) -> Option<OffsetDateTime> {
140    if let Ok(dt) = OffsetDateTime::parse(value, &Rfc3339) {
141        return Some(dt);
142    }
143
144    if let Ok(dt) = OffsetDateTime::parse(value, formats::OFFSET_DATE_TIME) {
145        return Some(dt);
146    }
147
148    if let Some(dt) = decode_datetime_from_text(value) {
149        return Some(dt.assume_utc());
150    }
151
152    None
153}
154
155fn decode_datetime(value: SqliteValueRef<'_>) -> Result<PrimitiveDateTime, BoxDynError> {
156    let dt = match value.type_info().0 {
157        DataType::Text => decode_datetime_from_text(value.text()?),
158        DataType::Int4 | DataType::Integer => {
159            let parsed = OffsetDateTime::from_unix_timestamp(value.int64()).unwrap();
160            Some(PrimitiveDateTime::new(parsed.date(), parsed.time()))
161        }
162
163        _ => None,
164    };
165
166    if let Some(dt) = dt {
167        Ok(dt)
168    } else {
169        Err(format!("invalid datetime: {}", value.text()?).into())
170    }
171}
172
173fn decode_datetime_from_text(value: &str) -> Option<PrimitiveDateTime> {
174    let default_format = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
175    if let Ok(dt) = PrimitiveDateTime::parse(value, &default_format) {
176        return Some(dt);
177    }
178
179    let formats = [
180        BorrowedFormatItem::Compound(formats::PRIMITIVE_DATE_TIME_SPACE_SEPARATED),
181        BorrowedFormatItem::Compound(formats::PRIMITIVE_DATE_TIME_T_SEPARATED),
182    ];
183
184    if let Ok(dt) = PrimitiveDateTime::parse(value, &BorrowedFormatItem::First(&formats)) {
185        return Some(dt);
186    }
187
188    None
189}
190
191mod formats {
192    use time::format_description::BorrowedFormatItem::{Component, Literal, Optional};
193    use time::format_description::{modifier, BorrowedFormatItem, Component::*};
194
195    const YEAR: BorrowedFormatItem<'_> = Component(Year({
196        let mut value = modifier::Year::default();
197        value.padding = modifier::Padding::Zero;
198        value.repr = modifier::YearRepr::Full;
199        value.iso_week_based = false;
200        value.sign_is_mandatory = false;
201        value
202    }));
203
204    const MONTH: BorrowedFormatItem<'_> = Component(Month({
205        let mut value = modifier::Month::default();
206        value.padding = modifier::Padding::Zero;
207        value.repr = modifier::MonthRepr::Numerical;
208        value.case_sensitive = true;
209        value
210    }));
211
212    const DAY: BorrowedFormatItem<'_> = Component(Day({
213        let mut value = modifier::Day::default();
214        value.padding = modifier::Padding::Zero;
215        value
216    }));
217
218    const HOUR: BorrowedFormatItem<'_> = Component(Hour({
219        let mut value = modifier::Hour::default();
220        value.padding = modifier::Padding::Zero;
221        value.is_12_hour_clock = false;
222        value
223    }));
224
225    const MINUTE: BorrowedFormatItem<'_> = Component(Minute({
226        let mut value = modifier::Minute::default();
227        value.padding = modifier::Padding::Zero;
228        value
229    }));
230
231    const SECOND: BorrowedFormatItem<'_> = Component(Second({
232        let mut value = modifier::Second::default();
233        value.padding = modifier::Padding::Zero;
234        value
235    }));
236
237    const SUBSECOND: BorrowedFormatItem<'_> = Component(Subsecond({
238        let mut value = modifier::Subsecond::default();
239        value.digits = modifier::SubsecondDigits::OneOrMore;
240        value
241    }));
242
243    const OFFSET_HOUR: BorrowedFormatItem<'_> = Component(OffsetHour({
244        let mut value = modifier::OffsetHour::default();
245        value.sign_is_mandatory = true;
246        value.padding = modifier::Padding::Zero;
247        value
248    }));
249
250    const OFFSET_MINUTE: BorrowedFormatItem<'_> = Component(OffsetMinute({
251        let mut value = modifier::OffsetMinute::default();
252        value.padding = modifier::Padding::Zero;
253        value
254    }));
255
256    pub(super) const OFFSET_DATE_TIME: &[BorrowedFormatItem<'_>] = {
257        &[
258            YEAR,
259            Literal(b"-"),
260            MONTH,
261            Literal(b"-"),
262            DAY,
263            Optional(&Literal(b" ")),
264            Optional(&Literal(b"T")),
265            HOUR,
266            Literal(b":"),
267            MINUTE,
268            Optional(&Literal(b":")),
269            Optional(&SECOND),
270            Optional(&Literal(b".")),
271            Optional(&SUBSECOND),
272            Optional(&OFFSET_HOUR),
273            Optional(&Literal(b":")),
274            Optional(&OFFSET_MINUTE),
275        ]
276    };
277
278    pub(super) const PRIMITIVE_DATE_TIME_SPACE_SEPARATED: &[BorrowedFormatItem<'_>] = {
279        &[
280            YEAR,
281            Literal(b"-"),
282            MONTH,
283            Literal(b"-"),
284            DAY,
285            Literal(b" "),
286            HOUR,
287            Literal(b":"),
288            MINUTE,
289            Optional(&Literal(b":")),
290            Optional(&SECOND),
291            Optional(&Literal(b".")),
292            Optional(&SUBSECOND),
293            Optional(&Literal(b"Z")),
294        ]
295    };
296
297    pub(super) const PRIMITIVE_DATE_TIME_T_SEPARATED: &[BorrowedFormatItem<'_>] = {
298        &[
299            YEAR,
300            Literal(b"-"),
301            MONTH,
302            Literal(b"-"),
303            DAY,
304            Literal(b"T"),
305            HOUR,
306            Literal(b":"),
307            MINUTE,
308            Optional(&Literal(b":")),
309            Optional(&SECOND),
310            Optional(&Literal(b".")),
311            Optional(&SUBSECOND),
312            Optional(&Literal(b"Z")),
313        ]
314    };
315}