1use crate::prelude::*;
2
3use toasty_core::{
4 driver::Operation,
5 stmt::{ExprSet, InsertTarget, Statement, Value},
6};
7
8#[driver_test(id(ID))]
9pub async fn ty_timestamp(test: &mut Test) -> Result<(), BoxError> {
10 use jiff::Timestamp;
11
12 #[derive(Debug, toasty::Model)]
13 #[allow(dead_code)]
14 struct Item {
15 #[key]
16 #[auto]
17 id: ID,
18 val: Timestamp,
19 }
20
21 let mut db = test.setup_db(models!(Item)).await;
22
23 let ts = Timestamp::from_second(946684800)?; test.log().clear();
26
27 let created = Item::create().val(ts).exec(&mut db).await?;
28
29 let (op, _) = test.log().pop();
33
34 let expected_val = if test.capability().native_timestamp {
35 Value::Timestamp(ts)
36 } else {
37 Value::String(format!("{ts:.9}"))
38 };
39
40 assert_struct!(op, Operation::QuerySql(_ {
41 stmt: Statement::Insert(_ {
42 target: InsertTarget::Table(_ {
43 table: == table_id(&db, "items"),
44 columns: == columns(&db, "items", &["id", "val"]),
45 ..
46 }),
47 source.body: ExprSet::Values(_ {
48 rows: [=~ (Any, expected_val)],
49 ..
50 }),
51 ..
52 }),
53 ..
54 }));
55
56 let read = Item::get_by_id(&mut db, &created.id).await?;
58 assert_eq!(read.val, ts);
59
60 let more_values = vec![
61 Timestamp::from_second(1609459200)?, Timestamp::from_second(1735689600)?, ];
64 for val in &more_values {
65 let created = Item::create().val(*val).exec(&mut db).await?;
66 let read = Item::get_by_id(&mut db, &created.id).await?;
67 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
68 }
69 Ok(())
70}
71
72#[driver_test(id(ID))]
73pub async fn ty_zoned(test: &mut Test) -> Result<(), BoxError> {
74 use jiff::Zoned;
75
76 #[derive(Debug, toasty::Model)]
77 #[allow(dead_code)]
78 struct Item {
79 #[key]
80 #[auto]
81 id: ID,
82 val: Zoned,
83 }
84
85 let mut db = test.setup_db(models!(Item)).await;
86
87 let test_values = vec![
88 "2000-01-01T00:00:00+00:00[UTC]".parse::<Zoned>()?,
89 "2021-06-15T14:30:00-04:00[America/New_York]".parse::<Zoned>()?,
90 "2025-12-31T23:59:59+09:00[Asia/Tokyo]".parse::<Zoned>()?,
91 "1970-01-01T00:00:00+00:00[UTC]".parse::<Zoned>()?,
92 "2024-11-03T01:30:00-04:00[America/New_York]".parse::<Zoned>()?, ];
94
95 for val in &test_values {
96 let created = Item::create().val(val.clone()).exec(&mut db).await?;
97 let read = Item::get_by_id(&mut db, &created.id).await?;
98 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
99 }
100 Ok(())
101}
102
103#[driver_test(id(ID))]
104pub async fn ty_date(test: &mut Test) -> Result<()> {
105 use jiff::civil::Date;
106
107 #[derive(Debug, toasty::Model)]
108 #[allow(dead_code)]
109 struct Item {
110 #[key]
111 #[auto]
112 id: ID,
113 val: Date,
114 }
115
116 let mut db = test.setup_db(models!(Item)).await;
117
118 let test_values = vec![
119 Date::constant(2000, 1, 1),
120 Date::constant(2021, 6, 15),
121 Date::constant(2025, 12, 31),
122 Date::constant(1970, 1, 1),
123 Date::constant(1900, 2, 28),
124 Date::constant(2024, 2, 29), Date::constant(9999, 12, 31),
126 Date::constant(1, 1, 1),
127 ];
128
129 for val in &test_values {
130 let created = Item::create().val(*val).exec(&mut db).await?;
131 let read = Item::get_by_id(&mut db, &created.id).await?;
132 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
133 }
134 Ok(())
135}
136
137#[driver_test(id(ID))]
138pub async fn ty_time(test: &mut Test) -> Result<()> {
139 use jiff::civil::Time;
140
141 #[derive(Debug, toasty::Model)]
142 #[allow(dead_code)]
143 struct Item {
144 #[key]
145 #[auto]
146 id: ID,
147 val: Time,
148 }
149
150 let mut db = test.setup_db(models!(Item)).await;
151
152 let test_values = vec![
153 Time::constant(0, 0, 0, 0),
154 Time::constant(12, 0, 0, 0),
155 Time::constant(23, 59, 59, 999_999_000), Time::constant(9, 30, 15, 0),
157 Time::constant(14, 45, 30, 500_000_000),
158 Time::constant(6, 0, 0, 123_456_000), ];
160
161 for val in &test_values {
162 let created = Item::create().val(*val).exec(&mut db).await?;
163 let read = Item::get_by_id(&mut db, &created.id).await?;
164 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
165 }
166 Ok(())
167}
168
169#[driver_test(id(ID))]
170pub async fn ty_datetime(test: &mut Test) -> Result<()> {
171 use jiff::civil::DateTime;
172
173 #[derive(Debug, toasty::Model)]
174 #[allow(dead_code)]
175 struct Item {
176 #[key]
177 #[auto]
178 id: ID,
179 val: DateTime,
180 }
181
182 let mut db = test.setup_db(models!(Item)).await;
183
184 let test_values = vec![
185 DateTime::constant(2000, 1, 1, 0, 0, 0, 0),
186 DateTime::constant(2021, 6, 15, 14, 30, 0, 0),
187 DateTime::constant(2025, 12, 31, 23, 59, 59, 999_999_000), DateTime::constant(1970, 1, 1, 0, 0, 0, 0),
189 DateTime::constant(1900, 2, 28, 12, 0, 0, 0),
190 DateTime::constant(2024, 2, 29, 6, 30, 15, 123_456_000), DateTime::constant(2099, 12, 31, 23, 59, 59, 0),
192 DateTime::constant(1901, 1, 1, 0, 0, 0, 0),
193 ];
194
195 for val in &test_values {
196 let created = Item::create().val(*val).exec(&mut db).await?;
197 let read = Item::get_by_id(&mut db, &created.id).await?;
198 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
199 }
200 Ok(())
201}
202
203#[driver_test(id(ID), requires(native_timestamp))]
204pub async fn ty_timestamp_precision_2(test: &mut Test) -> Result<(), BoxError> {
205 use jiff::Timestamp;
206
207 #[derive(Debug, toasty::Model)]
208 #[allow(dead_code)]
209 struct Item {
210 #[key]
211 #[auto]
212 id: ID,
213 #[column(type = timestamp(2))]
214 val: Timestamp,
215 }
216
217 let mut db = test.setup_db(models!(Item)).await;
218
219 let original = Timestamp::from_second(946684800)?
221 .checked_add(jiff::Span::new().nanoseconds(123_456_789))?;
222
223 let expected = Timestamp::from_second(946684800)?
226 .checked_add(jiff::Span::new().nanoseconds(120_000_000))?;
227
228 let created = Item::create().val(original).exec(&mut db).await?;
229 let read = Item::get_by_id(&mut db, &created.id).await?;
230
231 assert_eq!(
232 read.val, expected,
233 "Precision truncation failed: original={}, read={}, expected={}",
234 original, read.val, expected
235 );
236 Ok(())
237}
238
239#[driver_test(id(ID), requires(native_time))]
240pub async fn ty_time_precision_2(test: &mut Test) -> Result<()> {
241 use jiff::civil::Time;
242
243 #[derive(Debug, toasty::Model)]
244 #[allow(dead_code)]
245 struct Item {
246 #[key]
247 #[auto]
248 id: ID,
249 #[column(type = time(2))]
250 val: Time,
251 }
252
253 let mut db = test.setup_db(models!(Item)).await;
254
255 let original = Time::constant(14, 30, 45, 123_456_789);
257
258 let expected = Time::constant(14, 30, 45, 120_000_000);
261
262 let created = Item::create().val(original).exec(&mut db).await?;
263 let read = Item::get_by_id(&mut db, &created.id).await?;
264
265 assert_eq!(
266 read.val, expected,
267 "Precision truncation failed: original={}, read={}, expected={}",
268 original, read.val, expected
269 );
270 Ok(())
271}
272
273#[driver_test(id(ID), requires(native_datetime))]
274pub async fn ty_datetime_precision_2(test: &mut Test) -> Result<()> {
275 use jiff::civil::DateTime;
276
277 #[derive(Debug, toasty::Model)]
278 #[allow(dead_code)]
279 struct Item {
280 #[key]
281 #[auto]
282 id: ID,
283 #[column(type = datetime(2))]
284 val: DateTime,
285 }
286
287 let mut db = test.setup_db(models!(Item)).await;
288
289 let original = DateTime::constant(2024, 6, 15, 14, 30, 45, 123_456_789);
291
292 let expected = DateTime::constant(2024, 6, 15, 14, 30, 45, 120_000_000);
295
296 let created = Item::create().val(original).exec(&mut db).await?;
297 let read = Item::get_by_id(&mut db, &created.id).await?;
298
299 assert_eq!(
300 read.val, expected,
301 "Precision truncation failed: original={}, read={}, expected={}",
302 original, read.val, expected
303 );
304 Ok(())
305}
306
307#[driver_test(id(ID))]
308pub async fn ty_timestamp_as_text(test: &mut Test) -> Result<(), BoxError> {
309 use jiff::Timestamp;
310
311 #[derive(Debug, toasty::Model)]
312 #[allow(dead_code)]
313 struct Item {
314 #[key]
315 #[auto]
316 id: ID,
317 #[column(type = text)]
318 val: Timestamp,
319 }
320
321 let mut db = test.setup_db(models!(Item)).await;
322
323 let ts = Timestamp::from_second(946684800)?; let ts_text = format!("{ts:.9}");
325
326 test.log().clear();
327
328 let created = Item::create().val(ts).exec(&mut db).await?;
329
330 let (op, _) = test.log().pop();
333 assert_struct!(op, Operation::QuerySql(_ {
334 stmt: Statement::Insert(_ {
335 target: InsertTarget::Table(_ {
336 table: == table_id(&db, "items"),
337 columns: == columns(&db, "items", &["id", "val"]),
338 ..
339 }),
340 source.body: ExprSet::Values(_ {
341 rows: [=~ (Any, ts_text)],
342 ..
343 }),
344 ..
345 }),
346 ..
347 }));
348
349 let read = Item::get_by_id(&mut db, &created.id).await?;
351 assert_eq!(read.val, ts);
352
353 Ok(())
354}
355
356#[driver_test(id(ID))]
357pub async fn ty_date_as_text(test: &mut Test) -> Result<()> {
358 use jiff::civil::Date;
359
360 #[derive(Debug, toasty::Model)]
361 #[allow(dead_code)]
362 struct Item {
363 #[key]
364 #[auto]
365 id: ID,
366 #[column(type = text)]
367 val: Date,
368 }
369
370 let mut db = test.setup_db(models!(Item)).await;
371
372 let test_values = vec![
373 Date::constant(2000, 1, 1),
374 Date::constant(2021, 6, 15),
375 Date::constant(2025, 12, 31),
376 ];
377
378 for val in &test_values {
379 let created = Item::create().val(*val).exec(&mut db).await?;
380 let read = Item::get_by_id(&mut db, &created.id).await?;
381 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
382 }
383 Ok(())
384}
385
386#[driver_test(id(ID))]
387pub async fn ty_time_as_text(test: &mut Test) -> Result<()> {
388 use jiff::civil::Time;
389
390 #[derive(Debug, toasty::Model)]
391 #[allow(dead_code)]
392 struct Item {
393 #[key]
394 #[auto]
395 id: ID,
396 #[column(type = text)]
397 val: Time,
398 }
399
400 let mut db = test.setup_db(models!(Item)).await;
401
402 let test_values = vec![
403 Time::constant(0, 0, 0, 0),
404 Time::constant(12, 0, 0, 0),
405 Time::constant(23, 59, 59, 999_999_000), Time::constant(9, 30, 15, 0),
407 ];
408
409 for val in &test_values {
410 let created = Item::create().val(*val).exec(&mut db).await?;
411 let read = Item::get_by_id(&mut db, &created.id).await?;
412 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
413 }
414 Ok(())
415}
416
417#[driver_test(id(ID))]
418pub async fn ty_datetime_as_text(test: &mut Test) -> Result<()> {
419 use jiff::civil::DateTime;
420
421 #[derive(Debug, toasty::Model)]
422 #[allow(dead_code)]
423 struct Item {
424 #[key]
425 #[auto]
426 id: ID,
427 #[column(type = text)]
428 val: DateTime,
429 }
430
431 let mut db = test.setup_db(models!(Item)).await;
432
433 let test_values = vec![
434 DateTime::constant(2000, 1, 1, 0, 0, 0, 0),
435 DateTime::constant(2021, 6, 15, 14, 30, 0, 0),
436 DateTime::constant(2025, 12, 31, 23, 59, 59, 999_999_000), ];
438
439 for val in &test_values {
440 let created = Item::create().val(*val).exec(&mut db).await?;
441 let read = Item::get_by_id(&mut db, &created.id).await?;
442 assert_eq!(read.val, *val, "Round-trip failed for: {}", val);
443 }
444 Ok(())
445}
446
447#[driver_test(id(ID), requires(sql))]
448pub async fn order_by_timestamp(test: &mut Test) -> Result<(), BoxError> {
449 use jiff::Timestamp;
450
451 #[derive(Debug, toasty::Model)]
452 #[allow(dead_code)]
453 struct Item {
454 #[key]
455 #[auto]
456 id: ID,
457
458 #[column(type = text)]
459 val: Timestamp,
460 }
461
462 let mut db = test.setup_db(models!(Item)).await;
463
464 let timestamps = vec![
465 Timestamp::from_second(1609459200)?, Timestamp::from_second(946684800)?, Timestamp::from_second(1735689600)?, ];
469
470 for val in ×tamps {
471 Item::create().val(*val).exec(&mut db).await?;
472 }
473
474 let asc: Vec<_> = Item::all()
475 .order_by(Item::fields().val().asc())
476 .exec(&mut db)
477 .await?;
478
479 assert_eq!(asc.len(), 3);
480 assert!(asc[0].val < asc[1].val);
481 assert!(asc[1].val < asc[2].val);
482
483 let desc: Vec<_> = Item::all()
484 .order_by(Item::fields().val().desc())
485 .exec(&mut db)
486 .await?;
487
488 assert_eq!(desc.len(), 3);
489 assert!(desc[0].val > desc[1].val);
490 assert!(desc[1].val > desc[2].val);
491
492 Ok(())
493}
494
495#[driver_test(id(ID))]
496pub async fn filter_by_timestamp(test: &mut Test) -> Result<(), BoxError> {
497 use jiff::Timestamp;
498
499 #[derive(Debug, toasty::Model)]
500 #[allow(dead_code)]
501 struct Event {
502 #[key]
503 #[auto]
504 id: ID,
505 #[index]
506 at: Timestamp,
507 name: String,
508 }
509
510 let mut db = test.setup_db(models!(Event)).await;
511
512 let ts1 = Timestamp::from_second(946684800)?; let ts2 = Timestamp::from_second(1609459200)?; let ts3 = Timestamp::from_second(1735689600)?; Event::create().at(ts1).name("a").exec(&mut db).await?;
517 Event::create().at(ts2).name("b").exec(&mut db).await?;
518 Event::create().at(ts3).name("c").exec(&mut db).await?;
519
520 let results = Event::filter_by_at(ts2).exec(&mut db).await?;
521 assert_struct!(results, [{ name: "b", at: == ts2 }]);
522
523 let results = Event::filter_by_at(Timestamp::from_second(0)?)
525 .exec(&mut db)
526 .await?;
527 assert!(results.is_empty());
528
529 Ok(())
530}