1use jiff::{
4 civil::{Date, DateTime, Time},
5 Timestamp,
6};
7
8use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Type, ValueRef};
9use crate::Result;
10
11impl 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
20impl 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}
29impl 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
38impl 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
47impl 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
56impl 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
65impl ToSql for Timestamp {
68 #[inline]
69 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
70 Ok(ToSqlOutput::from(self.to_string()))
71 }
72}
73
74impl 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 let tests = vec![
193 "2013-10-07T08:23:19.123456789Z",
195 "2013-10-07 08:23:19.123456789Z",
196 "2013-10-07 08:23Z",
198 "2013-10-07 08:23+04:00",
199 "2013-10-07 08:23:19Z",
201 "2013-10-07 08:23:19+04:00",
202 "2013-10-07 08:23:19.123Z",
204 "2013-10-07 08:23:19.123+04:00",
205 "2013-10-07T08:23Z",
207 "2013-10-07T08:23+04:00",
208 "2013-10-07T08:23:19Z",
210 "2013-10-07T08:23:19+04:00",
211 "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}