datetime/
duckdb.rs

1//! Integration with DuckDB.
2
3use duckdb::Result;
4use duckdb::types::FromSql;
5use duckdb::types::FromSqlError;
6use duckdb::types::FromSqlResult;
7use duckdb::types::TimeUnit;
8use duckdb::types::ToSql;
9use duckdb::types::ToSqlOutput;
10use duckdb::types::ValueRef;
11
12use crate::DateTime;
13use crate::Precision;
14
15macro_rules! from_sql {
16  ($($precision:ident($var:ident) => $e:expr)*) => {
17    impl FromSql for DateTime {
18      fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
19        match value {
20          $(ValueRef::Timestamp(TimeUnit::$precision, $var) => Ok($e),
21          ValueRef::Time64(TimeUnit::$precision, $var) => Ok($e),)*
22          _ => Err(FromSqlError::InvalidType),
23        }
24      }
25    }
26  }
27}
28
29from_sql! {
30  Second(seconds) => DateTime::from_timestamp(seconds, 0)
31  Millisecond(ms) => DateTime::from_timestamp(ms / 1_000, (ms % 1_000) as u32 * 1_000_000)
32  Microsecond(us) => DateTime::from_timestamp(us / 1_000_000, (us % 1_000_000) as u32 * 1_000)
33  Nanosecond(ns)  => DateTime::from_timestamp(ns / 1_000_000_000, (ns % 1_000_000_000) as u32)
34}
35
36impl ToSql for DateTime {
37  fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
38    match self.precision() {
39      Precision::Second =>
40        Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(TimeUnit::Second, self.as_seconds()))),
41      Precision::Millisecond => Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(
42        TimeUnit::Millisecond,
43        self.as_milliseconds(),
44      ))),
45      Precision::Microsecond => Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(
46        TimeUnit::Microsecond,
47        self.as_microseconds(),
48      ))),
49      Precision::Nanosecond => Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(
50        TimeUnit::Nanosecond,
51        (self.as_nanoseconds().try_into())
52          .map_err(|e| duckdb::Error::ToSqlConversionFailure(Box::new(e)))?,
53      ))),
54    }
55  }
56}
57
58#[cfg(test)]
59mod tests {
60  use assert2::check;
61
62  use super::*;
63  use crate::datetime;
64
65  #[test]
66  fn test_from_sql() -> FromSqlResult<()> {
67    use TimeUnit::*;
68    for (precision, multiplier) in
69      [(Second, 1), (Millisecond, 1_000), (Microsecond, 1_000_000), (Nanosecond, 1_000_000_000)]
70    {
71      let input = ValueRef::Timestamp(precision, 1335020400 * multiplier);
72      let dt = DateTime::column_result(input)?;
73      check!(dt == datetime! { 2012-04-21 15:00:00 });
74      let input = ValueRef::Time64(precision, 1335020400 * multiplier);
75      let dt = DateTime::column_result(input)?;
76      check!(dt == datetime! { 2012-04-21 15:00:00 });
77    }
78    Ok(())
79  }
80
81  #[test]
82  fn test_to_sql() -> Result<()> {
83    let dt = datetime! { 2012-04-21 15:00:00 };
84    let output = dt.to_sql()?;
85    if let ToSqlOutput::Borrowed(ValueRef::Timestamp(TimeUnit::Second, seconds)) = output {
86      check!(seconds == 1335020400);
87    } else {
88      check!(false, "Incorrect type");
89    }
90    Ok(())
91  }
92}