Fix DateTime format

This commit is contained in:
gwenn 2021-01-29 22:03:50 +01:00
parent d5b5f43015
commit 4f175d5748
2 changed files with 45 additions and 28 deletions

View File

@ -1,7 +1,5 @@
//! Convert most of the [Time Strings](http://sqlite.org/lang_datefunc.html) to chrono types.
use std::borrow::Cow;
use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
@ -11,7 +9,7 @@ use crate::Result;
impl ToSql for NaiveDate {
#[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let date_str = self.format("%Y-%m-%d").to_string();
let date_str = self.format("%F").to_string();
Ok(ToSqlOutput::from(date_str))
}
}
@ -22,7 +20,7 @@ impl FromSql for NaiveDate {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value
.as_str()
.and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
.and_then(|s| match NaiveDate::parse_from_str(s, "%F") {
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
})
@ -33,7 +31,7 @@ impl FromSql for NaiveDate {
impl ToSql for NaiveTime {
#[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let date_str = self.format("%H:%M:%S%.f").to_string();
let date_str = self.format("%T%.f").to_string();
Ok(ToSqlOutput::from(date_str))
}
}
@ -44,8 +42,8 @@ impl FromSql for NaiveTime {
value.as_str().and_then(|s| {
let fmt = match s.len() {
5 => "%H:%M",
8 => "%H:%M:%S",
_ => "%H:%M:%S%.f",
8 => "%T",
_ => "%T%.f",
};
match NaiveTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt),
@ -56,11 +54,11 @@ impl FromSql for NaiveTime {
}
/// ISO 8601 combined date and time without timezone =>
/// "YYYY-MM-DDTHH:MM:SS.SSS"
/// "YYYY-MM-DD HH:MM:SS.SSS"
impl ToSql for NaiveDateTime {
#[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
let date_str = self.format("%F %T%.f").to_string();
Ok(ToSqlOutput::from(date_str))
}
}
@ -72,9 +70,9 @@ impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_str().and_then(|s| {
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%Y-%m-%dT%H:%M:%S%.f"
"%FT%T%.f"
} else {
"%Y-%m-%d %H:%M:%S%.f"
"%F %T%.f"
};
match NaiveDateTime::parse_from_str(s, fmt) {
@ -86,34 +84,29 @@ impl FromSql for NaiveDateTime {
}
/// Date and time with time zone => UTC RFC3339 timestamp
/// ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
/// ("YYYY-MM-DD HH:MM:SS.SSS+00:00").
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
#[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.with_timezone(&Utc).to_rfc3339()))
let date_str = self.with_timezone(&Utc).format("%F %T%.f%:z").to_string();
Ok(ToSqlOutput::from(date_str))
}
}
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Utc>`.
/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") into `DateTime<Utc>`.
impl FromSql for DateTime<Utc> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
{
// Try to parse value as rfc3339 first.
let s = value.as_str()?;
// If timestamp looks space-separated, make a copy and replace it with 'T'.
let s = if s.len() >= 11 && s.as_bytes()[10] == b' ' {
let mut s = s.to_string();
unsafe {
let sbytes = s.as_mut_vec();
sbytes[10] = b'T';
}
Cow::Owned(s)
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%FT%T%.f%:z"
} else {
Cow::Borrowed(s)
"%F %T%.f%:z"
};
if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
if let Ok(dt) = DateTime::parse_from_str(s, fmt) {
return Ok(dt.with_timezone(&Utc));
}
}
@ -123,7 +116,7 @@ impl FromSql for DateTime<Utc> {
}
}
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Local>`.
/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") into `DateTime<Local>`.
impl FromSql for DateTime<Local> {
#[inline]
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
@ -179,7 +172,7 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", [dt])?;
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
assert_eq!("2016-02-23T23:56:04", s);
assert_eq!("2016-02-23 23:56:04", s);
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
assert_eq!(dt, v);
@ -200,7 +193,7 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", [utc])?;
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
assert_eq!("2016-02-23T23:56:04.789+00:00", s);
assert_eq!("2016-02-23 23:56:04.789+00:00", s);
let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
assert_eq!(utc, v1);
@ -252,4 +245,20 @@ mod test {
assert!(result.is_ok());
Ok(())
}
#[test]
fn test_naive_date_time_param() -> Result<()> {
let db = checked_memory_handle()?;
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 seconds') AND datetime('now', '+1 seconds')", [Utc::now().naive_utc()], |r| r.get(0));
assert!(result.is_ok());
Ok(())
}
#[test]
fn test_date_time_param() -> Result<()> {
let db = checked_memory_handle()?;
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 seconds') AND datetime('now', '+1 seconds')", [Utc::now()], |r| r.get(0));
assert!(result.is_ok());
Ok(())
}
}

View File

@ -4,7 +4,7 @@ use crate::Result;
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
const CURRENT_TIMESTAMP_FMT: &str = "%Y-%m-%d %H:%M:%S";
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%NZ";
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%d %H:%M:%S.%NZ";
const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%N %z";
impl ToSql for OffsetDateTime {
@ -79,4 +79,12 @@ mod test {
assert!(result.is_ok());
Ok(())
}
#[test]
fn test_param() -> Result<()> {
let db = checked_memory_handle()?;
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 seconds') AND datetime('now', '+1 seconds')", [OffsetDateTime::now_utc()], |r| r.get(0));
assert!(result.is_ok());
Ok(())
}
}