mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 05:48:56 +08:00 
			
		
		
		
	Fix DateTime format
This commit is contained in:
		| @@ -1,7 +1,5 @@ | |||||||
| //! Convert most of the [Time Strings](http://sqlite.org/lang_datefunc.html) to chrono types. | //! 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 chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; | ||||||
|  |  | ||||||
| use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; | use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; | ||||||
| @@ -11,7 +9,7 @@ use crate::Result; | |||||||
| impl ToSql for NaiveDate { | impl ToSql for NaiveDate { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn to_sql(&self) -> Result<ToSqlOutput<'_>> { |     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)) |         Ok(ToSqlOutput::from(date_str)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -22,7 +20,7 @@ impl FromSql for NaiveDate { | |||||||
|     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { |     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { | ||||||
|         value |         value | ||||||
|             .as_str() |             .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), |                 Ok(dt) => Ok(dt), | ||||||
|                 Err(err) => Err(FromSqlError::Other(Box::new(err))), |                 Err(err) => Err(FromSqlError::Other(Box::new(err))), | ||||||
|             }) |             }) | ||||||
| @@ -33,7 +31,7 @@ impl FromSql for NaiveDate { | |||||||
| impl ToSql for NaiveTime { | impl ToSql for NaiveTime { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn to_sql(&self) -> Result<ToSqlOutput<'_>> { |     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)) |         Ok(ToSqlOutput::from(date_str)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -44,8 +42,8 @@ impl FromSql for NaiveTime { | |||||||
|         value.as_str().and_then(|s| { |         value.as_str().and_then(|s| { | ||||||
|             let fmt = match s.len() { |             let fmt = match s.len() { | ||||||
|                 5 => "%H:%M", |                 5 => "%H:%M", | ||||||
|                 8 => "%H:%M:%S", |                 8 => "%T", | ||||||
|                 _ => "%H:%M:%S%.f", |                 _ => "%T%.f", | ||||||
|             }; |             }; | ||||||
|             match NaiveTime::parse_from_str(s, fmt) { |             match NaiveTime::parse_from_str(s, fmt) { | ||||||
|                 Ok(dt) => Ok(dt), |                 Ok(dt) => Ok(dt), | ||||||
| @@ -56,11 +54,11 @@ impl FromSql for NaiveTime { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// ISO 8601 combined date and time without timezone => | /// 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 { | impl ToSql for NaiveDateTime { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn to_sql(&self) -> Result<ToSqlOutput<'_>> { |     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)) |         Ok(ToSqlOutput::from(date_str)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -72,9 +70,9 @@ impl FromSql for NaiveDateTime { | |||||||
|     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { |     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { | ||||||
|         value.as_str().and_then(|s| { |         value.as_str().and_then(|s| { | ||||||
|             let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { |             let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { | ||||||
|                 "%Y-%m-%dT%H:%M:%S%.f" |                 "%FT%T%.f" | ||||||
|             } else { |             } else { | ||||||
|                 "%Y-%m-%d %H:%M:%S%.f" |                 "%F %T%.f" | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             match NaiveDateTime::parse_from_str(s, fmt) { |             match NaiveDateTime::parse_from_str(s, fmt) { | ||||||
| @@ -86,34 +84,29 @@ impl FromSql for NaiveDateTime { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Date and time with time zone => UTC RFC3339 timestamp | /// 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> { | impl<Tz: TimeZone> ToSql for DateTime<Tz> { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn to_sql(&self) -> Result<ToSqlOutput<'_>> { |     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> { | impl FromSql for DateTime<Utc> { | ||||||
|     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { |     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { | ||||||
|         { |         { | ||||||
|             // Try to parse value as rfc3339 first. |             // Try to parse value as rfc3339 first. | ||||||
|             let s = value.as_str()?; |             let s = value.as_str()?; | ||||||
|  |  | ||||||
|             // If timestamp looks space-separated, make a copy and replace it with 'T'. |             let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { | ||||||
|             let s = if s.len() >= 11 && s.as_bytes()[10] == b' ' { |                 "%FT%T%.f%:z" | ||||||
|                 let mut s = s.to_string(); |  | ||||||
|                 unsafe { |  | ||||||
|                     let sbytes = s.as_mut_vec(); |  | ||||||
|                     sbytes[10] = b'T'; |  | ||||||
|                 } |  | ||||||
|                 Cow::Owned(s) |  | ||||||
|             } else { |             } 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)); |                 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> { | impl FromSql for DateTime<Local> { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { |     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { | ||||||
| @@ -179,7 +172,7 @@ mod test { | |||||||
|         db.execute("INSERT INTO foo (t) VALUES (?)", [dt])?; |         db.execute("INSERT INTO foo (t) VALUES (?)", [dt])?; | ||||||
|  |  | ||||||
|         let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; |         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))?; |         let v: NaiveDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; | ||||||
|         assert_eq!(dt, v); |         assert_eq!(dt, v); | ||||||
|  |  | ||||||
| @@ -200,7 +193,7 @@ mod test { | |||||||
|         db.execute("INSERT INTO foo (t) VALUES (?)", [utc])?; |         db.execute("INSERT INTO foo (t) VALUES (?)", [utc])?; | ||||||
|  |  | ||||||
|         let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; |         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))?; |         let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; | ||||||
|         assert_eq!(utc, v1); |         assert_eq!(utc, v1); | ||||||
| @@ -252,4 +245,20 @@ mod test { | |||||||
|         assert!(result.is_ok()); |         assert!(result.is_ok()); | ||||||
|         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(()) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ use crate::Result; | |||||||
| use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset}; | use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset}; | ||||||
|  |  | ||||||
| const CURRENT_TIMESTAMP_FMT: &str = "%Y-%m-%d %H:%M:%S"; | 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"; | const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%N %z"; | ||||||
|  |  | ||||||
| impl ToSql for OffsetDateTime { | impl ToSql for OffsetDateTime { | ||||||
| @@ -79,4 +79,12 @@ mod test { | |||||||
|         assert!(result.is_ok()); |         assert!(result.is_ok()); | ||||||
|         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(()) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user