2020-06-29 15:36:01 +00:00
|
|
|
use crate::sql::common::{take_digits, take_digits_range, take_u32};
|
2022-01-16 20:31:50 +00:00
|
|
|
use crate::sql::error::IResult;
|
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;
|
2021-03-29 15:43:37 +00:00
|
|
|
use serde::ser::SerializeStruct;
|
2020-06-29 15:36:01 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::fmt;
|
|
|
|
use std::str;
|
|
|
|
|
2021-03-29 15:43:37 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Deserialize)]
|
|
|
|
pub struct Datetime {
|
|
|
|
pub value: DateTime<Utc>,
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
|
|
|
|
impl Default for Datetime {
|
|
|
|
fn default() -> Self {
|
2021-03-29 15:43:37 +00:00
|
|
|
Datetime {
|
|
|
|
value: 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 {
|
|
|
|
Datetime {
|
|
|
|
value: Utc.timestamp(v, 0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DateTime<Utc>> for Datetime {
|
|
|
|
fn from(v: DateTime<Utc>) -> Self {
|
|
|
|
Datetime {
|
|
|
|
value: v,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 15:36:01 +00:00
|
|
|
impl<'a> From<&'a str> for Datetime {
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Datetime {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2021-03-29 15:43:37 +00:00
|
|
|
write!(f, "\"{:?}\"", self.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serialize for Datetime {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: serde::Serializer,
|
|
|
|
{
|
|
|
|
if serializer.is_human_readable() {
|
|
|
|
serializer.serialize_some(&self.value)
|
|
|
|
} else {
|
|
|
|
let mut val = serializer.serialize_struct("Datetime", 1)?;
|
|
|
|
val.serialize_field("value", &self.value)?;
|
|
|
|
val.end()
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn datetime(i: &str) -> IResult<&str, Datetime> {
|
|
|
|
alt((
|
2022-03-16 23:52:25 +00:00
|
|
|
delimited(char('\"'), datetime_raw, char('\"')),
|
|
|
|
delimited(char('\''), datetime_raw, char('\'')),
|
2020-06-29 15:36:01 +00:00
|
|
|
))(i)
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:43:37 +00:00
|
|
|
pub 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);
|
2021-03-29 15:43:37 +00:00
|
|
|
Ok((
|
|
|
|
i,
|
|
|
|
Datetime {
|
|
|
|
value: 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);
|
2021-03-29 15:43:37 +00:00
|
|
|
Datetime {
|
|
|
|
value: d,
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
let d = Utc.ymd(year, mon, day).and_hms(hour, min, sec);
|
2021-03-29 15:43:37 +00:00
|
|
|
Datetime {
|
|
|
|
value: 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);
|
2021-03-29 15:43:37 +00:00
|
|
|
Datetime {
|
|
|
|
value: d,
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
let d = Utc.ymd(year, mon, day).and_hms_nano(hour, min, sec, nano);
|
2021-03-29 15:43:37 +00:00
|
|
|
Datetime {
|
|
|
|
value: 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)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, v) = take_u32(i)?;
|
|
|
|
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;
|
|
|
|
assert_eq!("\"2012-04-23T18:25:43.000005631Z\"", format!("{}", out));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn date_time_timezone_utc() {
|
|
|
|
let sql = "2012-04-23T18:25:43.511Z";
|
|
|
|
let res = datetime_raw(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
|
|
|
assert_eq!("\"2012-04-23T18:25:43.000000511Z\"", format!("{}", out));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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;
|
|
|
|
assert_eq!("\"2012-04-24T02:25:43.000000511Z\"", format!("{}", out));
|
|
|
|
}
|
|
|
|
}
|