surrealpatch/lib/src/sql/datetime.rs

304 lines
6.7 KiB
Rust
Raw Normal View History

use crate::sql::common::{take_digits, take_digits_range, take_u32_len};
use crate::sql::duration::Duration;
2022-01-16 20:31:50 +00:00
use crate::sql::error::IResult;
use crate::sql::escape::escape_str;
use crate::sql::serde::is_internal_serialization;
use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone, Utc};
2020-06-29 15:36:01 +00:00
use nom::branch::alt;
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};
use std::fmt::{self, Display, Formatter};
use std::ops;
use std::ops::Deref;
2020-06-29 15:36:01 +00:00
use std::str;
2021-03-29 15:43:37 +00:00
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Datetime(pub DateTime<Utc>);
2020-06-29 15:36:01 +00:00
impl Default for Datetime {
fn default() -> Self {
Self(Utc::now())
2020-06-29 15:36:01 +00:00
}
}
impl From<i64> for Datetime {
fn from(v: i64) -> Self {
Self(Utc.timestamp(v, 0))
}
}
impl From<DateTime<Utc>> for Datetime {
fn from(v: DateTime<Utc>) -> Self {
Self(v)
}
}
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(_) => Self::default(),
2021-03-29 15:43:37 +00:00
}
2020-06-29 15:36:01 +00:00
}
}
impl Deref for Datetime {
type Target = DateTime<Utc>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Datetime {
pub fn to_raw(&self) -> String {
self.0.to_rfc3339_opts(SecondsFormat::AutoSi, true)
}
}
impl Display for Datetime {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&escape_str(&self.0.to_rfc3339_opts(SecondsFormat::AutoSi, true)), f)
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,
{
if is_internal_serialization() {
serializer.serialize_newtype_struct("Datetime", &self.0)
} else {
serializer.serialize_some(&self.0)
2021-03-29 15:43:37 +00:00
}
2020-06-29 15:36:01 +00:00
}
}
impl ops::Sub<Self> for Datetime {
type Output = Duration;
fn sub(self, other: Self) -> 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-10-16 20:35:50 +00:00
delimited(char('\''), datetime_raw, char('\'')),
delimited(char('\"'), datetime_raw, char('\"')),
2020-06-29 15:36:01 +00:00
))(i)
}
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)?;
let (i, _) = char('-')(i)?;
2020-06-29 15:36:01 +00:00
let (i, mon) = month(i)?;
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);
Ok((i, Datetime(d)))
2020-06-29 15:36:01 +00:00
}
fn time(i: &str) -> IResult<&str, Datetime> {
let (i, year) = year(i)?;
let (i, _) = char('-')(i)?;
2020-06-29 15:36:01 +00:00
let (i, mon) = month(i)?;
let (i, _) = char('-')(i)?;
2020-06-29 15:36:01 +00:00
let (i, day) = day(i)?;
let (i, _) = char('T')(i)?;
2020-06-29 15:36:01 +00:00
let (i, hour) = hour(i)?;
let (i, _) = char(':')(i)?;
2020-06-29 15:36:01 +00:00
let (i, min) = minute(i)?;
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);
Datetime(d)
2020-06-29 15:36:01 +00:00
}
None => {
let d = Utc.ymd(year, mon, day).and_hms(hour, min, sec);
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)?;
let (i, _) = char('-')(i)?;
2020-06-29 15:36:01 +00:00
let (i, mon) = month(i)?;
let (i, _) = char('-')(i)?;
2020-06-29 15:36:01 +00:00
let (i, day) = day(i)?;
let (i, _) = char('T')(i)?;
2020-06-29 15:36:01 +00:00
let (i, hour) = hour(i)?;
let (i, _) = char(':')(i)?;
2020-06-29 15:36:01 +00:00
let (i, min) = minute(i)?;
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);
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);
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> {
let (i, _) = char('.')(i)?;
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>> {
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)?;
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 * 60) as i32)) }))
2020-06-29 15:36:01 +00:00
} else if s > 0 {
Ok((i, { Some(FixedOffset::east((h * 3600 + m * 60) as i32)) }))
2020-06-29 15:36:01 +00:00
} else {
Ok((i, None))
}
}
fn sign(i: &str) -> IResult<&str, i32> {
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.563100Z\"", format!("{}", out));
2020-06-29 15:36:01 +00:00
}
#[test]
fn date_time_timezone_utc() {
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;
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;
assert_eq!("\"2012-04-24T02:25:43.511Z\"", format!("{}", out));
2020-06-29 15:36:01 +00:00
}
#[test]
fn date_time_timezone_pacific_partial() {
let sql = "2012-04-23T18:25:43.511-08:30";
let res = datetime_raw(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("\"2012-04-24T02:55:43.511Z\"", format!("{}", out));
}
2020-06-29 15:36:01 +00:00
}