Skip to main content

rusqlite/types/
jiff.rs

1//! Convert some `jiff` types.
2
3use jiff::{
4    civil::{Date, DateTime, Time},
5    Timestamp,
6};
7
8use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Type, ValueRef};
9use crate::Result;
10
11/// Gregorian calendar date => "YYYY-MM-DD"
12impl ToSql for Date {
13    #[inline]
14    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
15        let s = self.to_string();
16        Ok(ToSqlOutput::from(s))
17    }
18}
19
20/// "YYYY-MM-DD" => Gregorian calendar date.
21impl FromSql for Date {
22    #[inline]
23    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
24        value
25            .as_str()
26            .and_then(|s| s.parse().map_err(FromSqlError::other))
27    }
28}
29/// time => "HH:MM:SS.SSS"
30impl ToSql for Time {
31    #[inline]
32    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
33        let date_str = self.to_string();
34        Ok(ToSqlOutput::from(date_str))
35    }
36}
37
38/// "HH:MM:SS.SSS" => time.
39impl FromSql for Time {
40    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
41        value
42            .as_str()
43            .and_then(|s| s.parse().map_err(FromSqlError::other))
44    }
45}
46
47/// Gregorian datetime => "YYYY-MM-DDTHH:MM:SS.SSS"
48impl ToSql for DateTime {
49    #[inline]
50    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
51        let s = self.to_string();
52        Ok(ToSqlOutput::from(s))
53    }
54}
55
56/// "YYYY-MM-DDTHH:MM:SS.SSS" => Gregorian datetime.
57impl FromSql for DateTime {
58    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
59        value
60            .as_str()
61            .and_then(|s| s.parse().map_err(FromSqlError::other))
62    }
63}
64
65/// UTC time => UTC RFC3339 timestamp
66/// ("YYYY-MM-DDTHH:MM:SS.SSSZ").
67impl ToSql for Timestamp {
68    #[inline]
69    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
70        Ok(ToSqlOutput::from(self.to_string()))
71    }
72}
73
74/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") or unix timestamp (in seconds) into `Timestamp`.
75impl FromSql for Timestamp {
76    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
77        if value.data_type() == Type::Integer {
78            return value
79                .as_i64()
80                .and_then(|i| Timestamp::from_second(i).map_err(FromSqlError::other));
81        }
82        value
83            .as_str()?
84            .parse::<Timestamp>()
85            .map_err(FromSqlError::other)
86    }
87}
88
89#[cfg(test)]
90mod test {
91    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
92    use wasm_bindgen_test::wasm_bindgen_test as test;
93
94    use crate::{Connection, Result};
95    use jiff::{
96        civil::{Date, DateTime, Time},
97        Timestamp,
98    };
99
100    fn checked_memory_handle() -> Result<Connection> {
101        let db = Connection::open_in_memory()?;
102        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER AS (strftime('%s', t)), b BLOB)")?;
103        Ok(db)
104    }
105
106    #[test]
107    fn test_date() -> Result<()> {
108        let db = checked_memory_handle()?;
109        let date = Date::constant(2016, 2, 23);
110        db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?;
111
112        let s: String = db.one_column("SELECT t FROM foo", [])?;
113        assert_eq!("2016-02-23", s);
114        let t: Date = db.one_column("SELECT t FROM foo", [])?;
115        assert_eq!(date, t);
116
117        db.execute("UPDATE foo set b = date(t)", [])?;
118        let t: Date = db.one_column("SELECT b FROM foo", [])?;
119        assert_eq!(date, t);
120
121        let r: Result<Date> = db.one_column("SELECT '2023-02-29'", []);
122        assert!(r.is_err());
123        Ok(())
124    }
125
126    #[test]
127    fn test_time() -> Result<()> {
128        let db = checked_memory_handle()?;
129        let time = Time::constant(23, 56, 4, 0);
130        db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
131
132        let s: String = db.one_column("SELECT t FROM foo", [])?;
133        assert_eq!("23:56:04", s);
134        let v: Time = db.one_column("SELECT t FROM foo", [])?;
135        assert_eq!(time, v);
136
137        db.execute("UPDATE foo set b = time(t)", [])?;
138        let v: Time = db.one_column("SELECT b FROM foo", [])?;
139        assert_eq!(time, v);
140
141        let r: Result<Time> = db.one_column("SELECT '25:22:45'", []);
142        assert!(r.is_err());
143        Ok(())
144    }
145
146    #[test]
147    fn test_date_time() -> Result<()> {
148        let db = checked_memory_handle()?;
149        let dt = DateTime::constant(2016, 2, 23, 23, 56, 4, 0);
150
151        db.execute("INSERT INTO foo (t) VALUES (?1)", [dt])?;
152
153        let s: String = db.one_column("SELECT t FROM foo", [])?;
154        assert_eq!("2016-02-23T23:56:04", s);
155        let v: DateTime = db.one_column("SELECT t FROM foo", [])?;
156        assert_eq!(dt, v);
157
158        db.execute("UPDATE foo set b = datetime(t)", [])?;
159        let v: DateTime = db.one_column("SELECT b FROM foo", [])?;
160        assert_eq!(dt, v);
161
162        let r: Result<DateTime> = db.one_column("SELECT '2023-02-29T00:00:00'", []);
163        assert!(r.is_err());
164        Ok(())
165    }
166
167    #[test]
168    fn test_timestamp() -> Result<()> {
169        let db = checked_memory_handle()?;
170        let ts: Timestamp = "2016-02-23 23:56:04Z".parse().unwrap();
171
172        db.execute("INSERT INTO foo (t) VALUES (?1)", [ts])?;
173
174        let s: String = db.one_column("SELECT t FROM foo", [])?;
175        assert_eq!("2016-02-23T23:56:04Z", s);
176        let v: Timestamp = db.one_column("SELECT t FROM foo", [])?;
177        assert_eq!(ts, v);
178        let v: Timestamp = db.one_column("SELECT i FROM foo", [])?;
179        assert_eq!(ts, v);
180
181        let r: Result<Timestamp> = db.one_column("SELECT '2023-02-29T00:00:00Z'", []);
182        assert!(r.is_err());
183
184        Ok(())
185    }
186
187    #[test]
188    fn test_timestamp_various_formats() -> Result<()> {
189        let db = Connection::open_in_memory()?;
190        // Copied over from a test in `src/types/time.rs`. The format numbers
191        // come from <https://sqlite.org/lang_datefunc.html>.
192        let tests = vec![
193            // Rfc3339
194            "2013-10-07T08:23:19.123456789Z",
195            "2013-10-07 08:23:19.123456789Z",
196            // Format 2
197            "2013-10-07 08:23Z",
198            "2013-10-07 08:23+04:00",
199            // Format 3
200            "2013-10-07 08:23:19Z",
201            "2013-10-07 08:23:19+04:00",
202            // Format 4
203            "2013-10-07 08:23:19.123Z",
204            "2013-10-07 08:23:19.123+04:00",
205            // Format 5
206            "2013-10-07T08:23Z",
207            "2013-10-07T08:23+04:00",
208            // Format 6
209            "2013-10-07T08:23:19Z",
210            "2013-10-07T08:23:19+04:00",
211            // Format 7
212            "2013-10-07T08:23:19.123Z",
213            "2013-10-07T08:23:19.123+04:00",
214        ];
215
216        for string in tests {
217            let expected: Timestamp = string.parse().unwrap();
218            let result: Timestamp = db.one_column("SELECT ?1", [string])?;
219            assert_eq!(result, expected);
220        }
221        Ok(())
222    }
223}