2022-09-22 01:25:45 +00:00
|
|
|
use crate::sql::common::{take_digits, take_digits_range, take_u32_len};
|
2022-09-22 23:48:49 +00:00
|
|
|
use crate::sql::duration::Duration;
|
2022-01-16 20:31:50 +00:00
|
|
|
use crate::sql::error::IResult;
|
2022-05-21 00:35:59 +00:00
|
|
|
use crate::sql::serde::is_internal_serialization;
|
2020-06-29 15:36:01 +00:00
|
|
|
use chrono::{DateTime, FixedOffset, TimeZone, Utc};
|
|
|
|
use nom::branch::alt;
|
2022-03-16 23:52:25 +00:00
|
|
|
use nom::character::complete::char;
|
2020-06-29 15:36:01 +00:00
|
|
|
use nom::combinator::map;
|
|
|
|
use nom::sequence::delimited;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2022-05-05 05:01:00 +00:00
|
|
|
use std::ops::Deref;
|
2020-06-29 15:36:01 +00:00
|
|
|
use std::str;
|
2022-09-22 23:48:49 +00:00
|
|
|
use std::{fmt, ops};
|
2020-06-29 15:36:01 +00:00
|
|
|
|
2022-06-26 13:37:11 +00:00
|
|
|
const SINGLE: char = '\'';
|
|
|
|
const DOUBLE: char = '"';
|
|
|
|
|
2021-03-29 15:43:37 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Deserialize)]
|
2022-05-05 05:01:00 +00:00
|
|
|
pub struct Datetime(pub DateTime<Utc>);
|
2020-06-29 15:36:01 +00:00
|
|
|
|
|
|
|
impl Default for Datetime {
|
|
|
|
fn default() -> Self {
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(Utc::now())
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 17:36:41 +00:00
|
|
|
impl From<i64> for Datetime {
|
|
|
|
fn from(v: i64) -> Self {
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(Utc.timestamp(v, 0))
|
2022-01-13 17:36:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DateTime<Utc>> for Datetime {
|
|
|
|
fn from(v: DateTime<Utc>) -> Self {
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(v)
|
2022-01-13 17:36:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-25 09:42:10 +00:00
|
|
|
impl From<&str> for Datetime {
|
2020-06-29 15:36:01 +00:00
|
|
|
fn from(s: &str) -> Self {
|
2021-03-29 15:43:37 +00:00
|
|
|
match datetime_raw(s) {
|
|
|
|
Ok((_, v)) => v,
|
|
|
|
Err(_) => Datetime::default(),
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 05:01:00 +00:00
|
|
|
impl Deref for Datetime {
|
|
|
|
type Target = DateTime<Utc>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 15:36:01 +00:00
|
|
|
impl fmt::Display for Datetime {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2022-05-05 05:01:00 +00:00
|
|
|
write!(f, "\"{:?}\"", self.0)
|
2021-03-29 15:43:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serialize for Datetime {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: serde::Serializer,
|
|
|
|
{
|
2022-05-21 00:35:59 +00:00
|
|
|
if is_internal_serialization() {
|
2022-05-05 05:01:00 +00:00
|
|
|
serializer.serialize_newtype_struct("Datetime", &self.0)
|
2022-05-21 00:35:59 +00:00
|
|
|
} else {
|
|
|
|
serializer.serialize_some(&self.0)
|
2021-03-29 15:43:37 +00:00
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 23:48:49 +00:00
|
|
|
impl ops::Sub<Datetime> for Datetime {
|
|
|
|
type Output = Duration;
|
|
|
|
fn sub(self, other: Datetime) -> Duration {
|
|
|
|
match (self.0 - other.0).to_std() {
|
|
|
|
Ok(d) => Duration::from(d),
|
|
|
|
Err(_) => Duration::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 15:36:01 +00:00
|
|
|
pub fn datetime(i: &str) -> IResult<&str, Datetime> {
|
|
|
|
alt((
|
2022-06-26 13:37:11 +00:00
|
|
|
delimited(char(DOUBLE), datetime_raw, char(DOUBLE)),
|
|
|
|
delimited(char(SINGLE), datetime_raw, char(SINGLE)),
|
2020-06-29 15:36:01 +00:00
|
|
|
))(i)
|
|
|
|
}
|
|
|
|
|
2022-06-26 13:37:11 +00:00
|
|
|
fn datetime_raw(i: &str) -> IResult<&str, Datetime> {
|
2020-06-29 15:36:01 +00:00
|
|
|
alt((nano, time, date))(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn date(i: &str) -> IResult<&str, Datetime> {
|
|
|
|
let (i, year) = year(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('-')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, mon) = month(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('-')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, day) = day(i)?;
|
|
|
|
|
|
|
|
let d = Utc.ymd(year, mon, day).and_hms(0, 0, 0);
|
2022-05-05 05:01:00 +00:00
|
|
|
Ok((i, Datetime(d)))
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn time(i: &str) -> IResult<&str, Datetime> {
|
|
|
|
let (i, year) = year(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('-')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, mon) = month(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('-')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, day) = day(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('T')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, hour) = hour(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char(':')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, min) = minute(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char(':')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, sec) = second(i)?;
|
|
|
|
let (i, zone) = zone(i)?;
|
|
|
|
|
|
|
|
let v = match zone {
|
|
|
|
Some(z) => {
|
|
|
|
let d = z.ymd(year, mon, day).and_hms(hour, min, sec);
|
|
|
|
let d = d.with_timezone(&Utc);
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(d)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
let d = Utc.ymd(year, mon, day).and_hms(hour, min, sec);
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(d)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((i, v))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn nano(i: &str) -> IResult<&str, Datetime> {
|
|
|
|
let (i, year) = year(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('-')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, mon) = month(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('-')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, day) = day(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('T')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, hour) = hour(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char(':')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, min) = minute(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char(':')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, sec) = second(i)?;
|
|
|
|
let (i, nano) = nanosecond(i)?;
|
|
|
|
let (i, zone) = zone(i)?;
|
|
|
|
|
|
|
|
let v = match zone {
|
|
|
|
Some(z) => {
|
|
|
|
let d = z.ymd(year, mon, day).and_hms_nano(hour, min, sec, nano);
|
|
|
|
let d = d.with_timezone(&Utc);
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(d)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
let d = Utc.ymd(year, mon, day).and_hms_nano(hour, min, sec, nano);
|
2022-05-05 05:01:00 +00:00
|
|
|
Datetime(d)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((i, v))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn year(i: &str) -> IResult<&str, i32> {
|
|
|
|
let (i, s) = sign(i).unwrap_or((i, 1));
|
|
|
|
let (i, y) = take_digits(i, 4)?;
|
|
|
|
let v = s * y as i32;
|
|
|
|
Ok((i, v))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn month(i: &str) -> IResult<&str, u32> {
|
|
|
|
take_digits_range(i, 2, 1..=12)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn day(i: &str) -> IResult<&str, u32> {
|
|
|
|
take_digits_range(i, 2, 1..=31)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn hour(i: &str) -> IResult<&str, u32> {
|
|
|
|
take_digits_range(i, 2, 0..=24)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn minute(i: &str) -> IResult<&str, u32> {
|
|
|
|
take_digits_range(i, 2, 0..=59)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn second(i: &str) -> IResult<&str, u32> {
|
|
|
|
take_digits_range(i, 2, 0..=59)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn nanosecond(i: &str) -> IResult<&str, u32> {
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('.')(i)?;
|
2022-09-22 01:25:45 +00:00
|
|
|
let (i, (v, l)) = take_u32_len(i)?;
|
|
|
|
let v = match l {
|
|
|
|
l if l <= 2 => v * 10000000,
|
|
|
|
l if l <= 3 => v * 1000000,
|
|
|
|
l if l <= 4 => v * 100000,
|
|
|
|
l if l <= 5 => v * 10000,
|
|
|
|
l if l <= 6 => v * 1000,
|
|
|
|
l if l <= 7 => v * 100,
|
|
|
|
l if l <= 8 => v * 10,
|
|
|
|
_ => v,
|
|
|
|
};
|
2020-06-29 15:36:01 +00:00
|
|
|
Ok((i, v))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn zone(i: &str) -> IResult<&str, Option<FixedOffset>> {
|
|
|
|
alt((zone_utc, zone_all))(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn zone_utc(i: &str) -> IResult<&str, Option<FixedOffset>> {
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('Z')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
Ok((i, None))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn zone_all(i: &str) -> IResult<&str, Option<FixedOffset>> {
|
|
|
|
let (i, s) = sign(i)?;
|
|
|
|
let (i, h) = hour(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char(':')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, m) = minute(i)?;
|
|
|
|
if h == 0 && m == 0 {
|
|
|
|
Ok((i, None))
|
|
|
|
} else if s < 0 {
|
|
|
|
Ok((i, { Some(FixedOffset::west((h * 3600 + m) as i32)) }))
|
|
|
|
} else if s > 0 {
|
|
|
|
Ok((i, { Some(FixedOffset::east((h * 3600 + m) as i32)) }))
|
|
|
|
} else {
|
|
|
|
Ok((i, None))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sign(i: &str) -> IResult<&str, i32> {
|
2022-03-16 23:52:25 +00:00
|
|
|
map(alt((char('-'), char('+'))), |s: char| match s {
|
|
|
|
'-' => -1,
|
2020-06-29 15:36:01 +00:00
|
|
|
_ => 1,
|
|
|
|
})(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date() {
|
|
|
|
let sql = "2012-04-23";
|
|
|
|
let res = datetime_raw(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
|
|
|
assert_eq!("\"2012-04-23T00:00:00Z\"", format!("{}", out));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date_time() {
|
|
|
|
let sql = "2012-04-23T18:25:43Z";
|
|
|
|
let res = datetime_raw(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
|
|
|
assert_eq!("\"2012-04-23T18:25:43Z\"", format!("{}", out));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date_time_nanos() {
|
|
|
|
let sql = "2012-04-23T18:25:43.5631Z";
|
|
|
|
let res = datetime_raw(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
2022-09-22 01:25:45 +00:00
|
|
|
assert_eq!("\"2012-04-23T18:25:43.563100Z\"", format!("{}", out));
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date_time_timezone_utc() {
|
2022-09-22 01:25:45 +00:00
|
|
|
let sql = "2012-04-23T18:25:43.0000511Z";
|
2020-06-29 15:36:01 +00:00
|
|
|
let res = datetime_raw(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
2022-09-22 01:25:45 +00:00
|
|
|
assert_eq!("\"2012-04-23T18:25:43.000051100Z\"", format!("{}", out));
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date_time_timezone_pacific() {
|
|
|
|
let sql = "2012-04-23T18:25:43.511-08:00";
|
|
|
|
let res = datetime_raw(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
2022-09-22 01:25:45 +00:00
|
|
|
assert_eq!("\"2012-04-24T02:25:43.511Z\"", format!("{}", out));
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|