Repository: alibaba/GraphScope (language: python)

Issue:
Support Datatype of `Date`/`Time`/`DateTime` in GIE
**Is your feature request related to a problem? Please describe.**
As titled. 


Discussion / hints:

Fix patch:
diff --git a/interactive_engine/executor/common/dyn_type/Cargo.toml b/interactive_engine/executor/common/dyn_type/Cargo.toml
index fa560ddb627a..9523dd559d6f 100644
--- a/interactive_engine/executor/common/dyn_type/Cargo.toml
+++ b/interactive_engine/executor/common/dyn_type/Cargo.toml
@@ -9,3 +9,4 @@ itertools = "0.10"
 lazy_static = "1.3.0"
 pegasus_common = { path = "../../engine/pegasus/common" }
 rustversion = "1.0"
+chrono = "0.4"
diff --git a/interactive_engine/executor/common/dyn_type/src/error.rs b/interactive_engine/executor/common/dyn_type/src/error.rs
index 9df8ec0c7199..5208a0f32887 100644
--- a/interactive_engine/executor/common/dyn_type/src/error.rs
+++ b/interactive_engine/executor/common/dyn_type/src/error.rs
@@ -44,6 +44,10 @@ impl fmt::Display for CastError {
             RawType::KV => write!(f, "can't cast KV into {}", self.target),
             RawType::None => write!(f, "can't cast None into {}", self.target),
             RawType::Unknown => write!(f, "can't cast unknown dyn type into {}", self.target),
+            RawType::Date => write!(f, "can't cast date into {}", self.target),
+            RawType::Time => write!(f, "can't cast time into {}", self.target),
+            RawType::DateTime => write!(f, "can't cast datetime into {}", self.target),
+            RawType::DateTimeWithTz => write!(f, "can't cast datetime with tz into {}", self.target),
         }
     }
 }
diff --git a/interactive_engine/executor/common/dyn_type/src/lib.rs b/interactive_engine/executor/common/dyn_type/src/lib.rs
index 74bd21069161..e545596823c9 100644
--- a/interactive_engine/executor/common/dyn_type/src/lib.rs
+++ b/interactive_engine/executor/common/dyn_type/src/lib.rs
@@ -17,6 +17,7 @@
 extern crate lazy_static;
 extern crate pegasus_common;
 
+extern crate chrono;
 extern crate dyn_clonable;
 
 pub mod arith;
@@ -33,7 +34,7 @@ use std::io;
 
 use dyn_clonable::*;
 pub use error::CastError;
-pub use object::{BorrowObject, Object, OwnedOrRef, Primitives};
+pub use object::{BorrowObject, DateTimeFormats, Object, OwnedOrRef, Primitives};
 pub use serde_dyn::{de_dyn_obj, register_type};
 
 #[clonable]
diff --git a/interactive_engine/executor/common/dyn_type/src/object.rs b/interactive_engine/executor/common/dyn_type/src/object.rs
index 1da3b7946113..51ca9d763f74 100644
--- a/interactive_engine/executor/common/dyn_type/src/object.rs
+++ b/interactive_engine/executor/common/dyn_type/src/object.rs
@@ -23,6 +23,7 @@ use std::fmt::Debug;
 use std::hash::{Hash, Hasher};
 use std::ops::Deref;
 
+use chrono::{DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
 use itertools::Itertools;
 use pegasus_common::downcast::*;
 use pegasus_common::impl_as_any;
@@ -37,6 +38,10 @@ pub enum RawType {
     ULLong,
     Float,
     String,
+    Date,
+    Time,
+    DateTime,
+    DateTimeWithTz,
     Blob(usize),
     Vector,
     KV,
@@ -409,6 +414,190 @@ impl PartialOrd for Primitives {
     }
 }
 
+#[derive(Clone, Debug, Hash)]
+pub enum DateTimeFormats {
+    // preserve a date format (ISO format) like 2019-01-01
+    Date(NaiveDate),
+    // preserve a time format (ISO format) like 00:00:00.000
+    Time(NaiveTime),
+    // preserve a date time format (ISO format) with UTC timezone, like 2019-01-01 00:00:00.000
+    DateTime(NaiveDateTime),
+    // preserve a date time format (ISO format) with specified timezone, like 2019-01-01T00:00:00.000+08:00
+    DateTimeWithTz(DateTime<FixedOffset>),
+}
+
+impl DateTimeFormats {
+    #[inline]
+    pub fn raw_type(&self) -> RawType {
+        match self {
+            DateTimeFormats::Date(_) => RawType::Date,
+            DateTimeFormats::Time(_) => RawType::Time,
+            DateTimeFormats::DateTime(_) => RawType::DateTime,
+            DateTimeFormats::DateTimeWithTz(_) => RawType::DateTimeWithTz,
+        }
+    }
+
+    #[inline]
+    pub fn as_date(&self) -> Result<NaiveDate, CastError> {
+        match self {
+            DateTimeFormats::Date(d) => Ok(*d),
+            DateTimeFormats::Time(_) => Err(CastError::new::<NaiveTime>(RawType::Time)),
+            DateTimeFormats::DateTime(dt) => Ok(dt.date()),
+            DateTimeFormats::DateTimeWithTz(dt) => Ok(dt.date_naive()),
+        }
+    }
+
+    #[inline]
+    pub fn as_time(&self) -> Result<NaiveTime, CastError> {
+        match self {
+            DateTimeFormats::Date(_) => Err(CastError::new::<NaiveDate>(RawType::Date)),
+            DateTimeFormats::Time(t) => Ok(*t),
+            DateTimeFormats::DateTime(dt) => Ok(dt.time()),
+            DateTimeFormats::DateTimeWithTz(dt) => Ok(dt.time()),
+        }
+    }
+
+    #[inline]
+    pub fn as_date_time(&self) -> Result<NaiveDateTime, CastError> {
+        match self {
+            DateTimeFormats::Date(_) => Err(CastError::new::<NaiveDate>(RawType::DateTime)),
+            DateTimeFormats::Time(_) => Err(CastError::new::<NaiveTime>(RawType::DateTime)),
+            DateTimeFormats::DateTime(dt) => Ok(*dt),
+            DateTimeFormats::DateTimeWithTz(dt) => Ok(dt.naive_local()),
+        }
+    }
+
+    #[inline]
+    pub fn as_date_time_with_tz(&self) -> Result<DateTime<FixedOffset>, CastError> {
+        match self {
+            DateTimeFormats::Date(_) => Err(CastError::new::<NaiveDate>(RawType::DateTimeWithTz)),
+            DateTimeFormats::Time(_) => Err(CastError::new::<NaiveTime>(RawType::DateTimeWithTz)),
+            DateTimeFormats::DateTime(dt) => dt
+                .and_local_timezone(FixedOffset::east_opt(0).unwrap())
+                .single()
+                .ok_or(CastError::new::<NaiveDateTime>(RawType::DateTimeWithTz)),
+            DateTimeFormats::DateTimeWithTz(dt) => Ok(*dt),
+        }
+    }
+
+    pub fn year(&self) -> Option<i32> {
+        match self {
+            DateTimeFormats::Date(d) => Some(d.year()),
+            DateTimeFormats::Time(_) => None,
+            DateTimeFormats::DateTime(dt) => Some(dt.year()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.year()),
+        }
+    }
+
+    pub fn month(&self) -> Option<u32> {
+        match self {
+            DateTimeFormats::Date(d) => Some(d.month()),
+            DateTimeFormats::Time(_) => None,
+            DateTimeFormats::DateTime(dt) => Some(dt.month()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.month()),
+        }
+    }
+
+    pub fn day(&self) -> Option<u32> {
+        match self {
+            DateTimeFormats::Date(d) => Some(d.day()),
+            DateTimeFormats::Time(_) => None,
+            DateTimeFormats::DateTime(dt) => Some(dt.day()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.day()),
+        }
+    }
+
+    pub fn hour(&self) -> Option<u32> {
+        match self {
+            DateTimeFormats::Date(_) => None,
+            DateTimeFormats::Time(t) => Some(t.hour()),
+            DateTimeFormats::DateTime(dt) => Some(dt.hour()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.hour()),
+        }
+    }
+
+    pub fn minute(&self) -> Option<u32> {
+        match self {
+            DateTimeFormats::Date(_) => None,
+            DateTimeFormats::Time(t) => Some(t.minute()),
+            DateTimeFormats::DateTime(dt) => Some(dt.minute()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.minute()),
+        }
+    }
+
+    pub fn second(&self) -> Option<u32> {
+        match self {
+            DateTimeFormats::Date(_) => None,
+            DateTimeFormats::Time(t) => Some(t.second()),
+            DateTimeFormats::DateTime(dt) => Some(dt.second()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.second()),
+        }
+    }
+
+    pub fn millisecond(&self) -> Option<u32> {
+        match self {
+            DateTimeFormats::Date(_) => None,
+            DateTimeFormats::Time(t) => Some(t.nanosecond() / 1_000_000),
+            DateTimeFormats::DateTime(dt) => Some(dt.nanosecond() / 1_000_000),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.nanosecond() / 1_000_000),
+        }
+    }
+
+    pub fn timestamp(&self) -> Option<i64> {
+        match self {
+            DateTimeFormats::Date(d) => d.and_hms_opt(0, 0, 0).map(|dt| dt.timestamp()),
+            DateTimeFormats::Time(_) => None,
+            DateTimeFormats::DateTime(dt) => Some(dt.timestamp()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.timestamp()),
+        }
+    }
+
+    pub fn timestamp_millis(&self) -> Option<i64> {
+        match self {
+            DateTimeFormats::Date(d) => d
+                .and_hms_opt(0, 0, 0)
+                .map(|dt| dt.timestamp_millis()),
+            DateTimeFormats::Time(_) => None,
+            DateTimeFormats::DateTime(dt) => Some(dt.timestamp_millis()),
+            DateTimeFormats::DateTimeWithTz(dt) => Some(dt.timestamp_millis()),
+        }
+    }
+}
+
+impl PartialEq for DateTimeFormats {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (DateTimeFormats::Date(l), DateTimeFormats::Date(r)) => l.eq(r),
+            (DateTimeFormats::Time(l), DateTimeFormats::Time(r)) => l.eq(r),
+            (DateTimeFormats::DateTime(l), DateTimeFormats::DateTime(r)) => l.eq(r),
+            (DateTimeFormats::DateTimeWithTz(l), DateTimeFormats::DateTimeWithTz(r)) => l.eq(r),
+            (DateTimeFormats::DateTime(l), DateTimeFormats::DateTimeWithTz(r)) => l.eq(&r.naive_utc()),
+            (DateTimeFormats::DateTimeWithTz(l), DateTimeFormats::DateTime(r)) => l.naive_utc().eq(r),
+            _ => false,
+        }
+    }
+}
+
+impl Eq for DateTimeFormats {}
+
+impl PartialOrd for DateTimeFormats {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        match (self, other) {
+            (DateTimeFormats::Date(l), DateTimeFormats::Date(r)) => l.partial_cmp(r),
+            (DateTimeFormats::Time(l), DateTimeFormats::Time(r)) => l.partial_cmp(r),
+            (DateTimeFormats::DateTime(l), DateTimeFormats::DateTime(r)) => l.partial_cmp(r),
+            (DateTimeFormats::DateTimeWithTz(l), DateTimeFormats::DateTimeWithTz(r)) => l.partial_cmp(r),
+            (DateTimeFormats::DateTime(l), DateTimeFormats::DateTimeWithTz(r)) => {
+                l.partial_cmp(&r.naive_utc())
+            }
+            (DateTimeFormats::DateTimeWithTz(l), DateTimeFormats::DateTime(r)) => {
+                l.naive_utc().partial_cmp(r)
+            }
+            _ => None,
+        }
+    }
+}
+
 #[derive(Clone, Debug)]
 pub enum Object {
     Primitive(Primitives),
@@ -416,6 +605,7 @@ pub enum Object {
     Vector(Vec<Object>),
     KV(BTreeMap<Object, Object>),
     Blob(Box<[u8]>),
+    DateFormat(DateTimeFormats),
     DynOwned(Box<dyn DynType>),
     None,
 }
@@ -430,6 +620,7 @@ impl ToString for Object {
             Object::Vector(v) => format!("{:?}", v),
             Object::KV(kv) => format!("{:?}", kv),
             Object::Blob(b) => format!("{:?}", b),
+            Object::DateFormat(d) => format!("{:?}", d),
             Object::DynOwned(_) => "unknown dynamic type".to_string(),
             Object::None => "".to_string(),
         }
@@ -444,6 +635,7 @@ pub enum BorrowObject<'a> {
     Vector(&'a [Object]),
     KV(&'a BTreeMap<Object, Object>),
     Blob(&'a [u8]),
+    DateFormat(&'a DateTimeFormats),
     /// To borrow from `Object::DynOwned`, and it can be cloned back to `Object::DynOwned`
     DynRef(&'a Box<dyn DynType>),
     None,
@@ -458,6 +650,7 @@ impl<'a> ToString for BorrowObject<'a> {
             Vector(v) => format!("{:?}", v),
             KV(kv) => format!("{:?}", kv),
             Blob(b) => format!("{:?}", b),
+            DateFormat(df) => format!("{:?}", df),
             DynRef(_) => "unknown dynamic type".to_string(),
             None => "None".to_string(),
         }
@@ -495,6 +688,7 @@ impl Object {
             Object::Vector(_) => RawType::Vector,
             Object::KV(_) => RawType::KV,
             Object::Blob(b) => RawType::Blob(b.len()),
+            Object::DateFormat(df) => df.raw_type(),
             Object::DynOwned(_) => RawType::Unknown,
             Object::None => RawType::None,
         }
@@ -507,6 +701,7 @@ impl Object {
             Object::Vector(v) => BorrowObject::Vector(v.as_slice()),
             Object::KV(kv) => BorrowObject::KV(kv),
             Object::Blob(v) => BorrowObject::Blob(v.as_ref()),
+            Object::DateFormat(d) => BorrowObject::DateFormat(d),
             Object::DynOwned(v) => BorrowObject::DynRef(v),
             Object::None => BorrowObject::None,
         }
@@ -648,6 +843,15 @@ impl Object {
         }
     }
 
+    #[inline]
+    pub fn as_date_format(&self) -> Result<&DateTimeFormats, CastError> {
+        match self {
+            Object::DateFormat(d) => Ok(d),
+            Object::DynOwned(x) => try_downcast_ref!(x, DateTimeFormats),
+            _ => Err(CastError::new::<DateTimeFormats>(self.raw_type())),
+        }
+    }
+
     pub fn get<T: DynType + Clone>(&self) -> Result<OwnedOrRef<T>, CastError> {
         match self {
             Object::Primitive(p) => {
@@ -703,6 +907,7 @@ impl<'a> BorrowObject<'a> {
             BorrowObject::Vector(_) => RawType::Vector,
             BorrowObject::KV(_) => RawType::KV,
             BorrowObject::Blob(b) => RawType::Blob(b.len()),
+            BorrowObject::DateFormat(df) => df.raw_type(),
             BorrowObject::DynRef(_) => RawType::Unknown,
             BorrowObject::None => RawType::None,
         }
@@ -844,6 +1049,15 @@ impl<'a> BorrowObject<'a> {
         }
     }
 
+    #[inline]
+    pub fn as_date_format(&self) -> Result<&DateTimeFormats, CastError> {
+        match self {
+            BorrowObject::DateFormat(d) => Ok(d),
+            BorrowObject::DynRef(x) => try_downcast_ref!(x, DateTimeFormats),
+            _ => Err(CastError::new::<DateTimeFormats>(self.raw_type())),
+        }
+    }
+
     pub fn try_to_owned(&self) -> Option<Object> {
         match *self {
             BorrowObject::Primitive(p) => Some(Object::Primitive(p)),
@@ -853,6 +1067,7 @@ impl<'a> BorrowObject<'a> {
             BorrowObject::Blob(b) => Some(Object::Blob(b.to_vec().into_boxed_slice())),
             BorrowObject::DynRef(d) => Some(Object::DynOwned((*d).clone())),
             BorrowObject::None => Some(Object::None),
+            BorrowObject::DateFormat(df) => Some(Object::DateFormat(df.clone())),
         }
     }
 
@@ -1079,6 +1294,9 @@ macro_rules! hash {
             $crate::$ty::None => {
                 "".hash($state);
             }
+            $crate::$ty::DateFormat(df) => {
+                df.hash($state);
+            }
             _ => unimplemented!(),
         }
     };
@@ -1284,6 +1502,30 @@ impl<K: Into<Object>, V: Into<Object>> From<Vec<(K, V)>> for Object {
     }
 }
 
+impl From<NaiveDate> for Object {
+    fn from(date: NaiveDate) -> Self {
+        Object::DateFormat(DateTimeFormats::Date(date))
+    }
+}
+
+impl From<NaiveDateTime> for Object {
+    fn from(date_time: NaiveDateTime) -> Self {
+        Object::DateFormat(DateTimeFormats::DateTime(date_time))
+    }
+}
+
+impl From<NaiveTime> for Object {
+    fn from(time: NaiveTime) -> Self {
+        Object::DateFormat(DateTimeFormats::Time(time))
+    }
+}
+
+impl From<DateTime<FixedOffset>> for Object {
+    fn from(date_time_with_tz: DateTime<FixedOffset>) -> Self {
+        Object::DateFormat(DateTimeFormats::DateTimeWithTz(date_time_with_tz))
+    }
+}
+
 pub enum OwnedOrRef<'a, T> {
     Owned(T),
     Ref(&'a T),
diff --git a/interactive_engine/executor/common/dyn_type/src/serde.rs b/interactive_engine/executor/common/dyn_type/src/serde.rs
index 62506dca2635..1d34005ab57c 100644
--- a/interactive_engine/executor/common/dyn_type/src/serde.rs
+++ b/interactive_engine/executor/common/dyn_type/src/serde.rs
@@ -19,7 +19,8 @@ use std::io;
 
 use pegasus_common::codec::{Decode, Encode, ReadExt, WriteExt};
 
-use crate::{de_dyn_obj, Object, Primitives};
+use crate::{de_dyn_obj, DateTimeFormats, Object, Primitives};
+use chrono::{Datelike, Timelike};
 
 impl Encode for Primitives {
     fn write_to<W: WriteExt>(&self, writer: &mut W) -> io::Result<()> {
@@ -90,6 +91,106 @@ fn type_id_from_bytes<R: ReadExt>(reader: &mut R) -> io::Result<TypeId> {
     Ok(unsafe { std::mem::transmute(number) })
 }
 
+impl Encode for DateTimeFormats {
+    fn write_to<W: WriteExt>(&self, writer: &mut W) -> io::Result<()> {
+        match self {
+            DateTimeFormats::Date(d) => {
+                writer.write_u8(0)?;
+                writer.write_i16(d.year() as i16)?;
+                writer.write_u8(d.month() as u8)?;
+                writer.write_u8(d.day() as u8)?;
+            }
+            DateTimeFormats::Time(t) => {
+                writer.write_u8(1)?;
+                writer.write_u8(t.hour() as u8)?;
+                writer.write_u8(t.minute() as u8)?;
+                writer.write_u8(t.second() as u8)?;
+                writer.write_u32(t.nanosecond() as u32)?;
+            }
+            DateTimeFormats::DateTime(datetime) => {
+                writer.write_u8(2)?;
+                writer.write_i64(datetime.timestamp_millis())?;
+            }
+            DateTimeFormats::DateTimeWithTz(datetime_with_tz) => {
+                writer.write_u8(3)?;
+                writer.write_i64(
+                    datetime_with_tz
+                        .naive_local()
+                        .timestamp_millis(),
+                )?;
+                writer.write_i32(datetime_with_tz.offset().local_minus_utc())?;
+            }
+        }
+        Ok(())
+    }
+}
+
+impl Decode for DateTimeFormats {
+    fn read_from<R: ReadExt>(reader: &mut R) -> io::Result<Self> {
+        let e = reader.read_u8()?;
+        match e {
+            0 => {
+                let year = <i16>::read_from(reader)?;
+                let month = <u8>::read_from(reader)?;
+                let day = <u8>::read_from(reader)?;
+                let date = chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).ok_or(
+                    io::Error::new(
+                        io::ErrorKind::Other,
+                        format!("invalid date {:?}-{:?}-{:?}", year, month, day),
+                    ),
+                )?;
+                Ok(DateTimeFormats::Date(date))
+            }
+            1 => {
+                let hour = <u8>::read_from(reader)?;
+                let minute = <u8>::read_from(reader)?;
+                let second = <u8>::read_from(reader)?;
+                let nano = <u32>::read_from(reader)?;
+                let time =
+                    chrono::NaiveTime::from_hms_nano_opt(hour as u32, minute as u32, second as u32, nano)
+                        .ok_or(io::Error::new(
+                        io::ErrorKind::Other,
+                        format!("invalid time {:?}:{:?}:{:?}.{:?}", hour, minute, second, nano / 1000_000),
+                    ))?;
+                Ok(DateTimeFormats::Time(time))
+            }
+            2 => {
+                let timestamp_millis = <i64>::read_from(reader)?;
+                let date_time = chrono::NaiveDateTime::from_timestamp_millis(timestamp_millis).ok_or(
+                    io::Error::new(
+                        io::ErrorKind::Other,
+                        format!("invalid datetime {:?}", timestamp_millis),
+                    ),
+                )?;
+                Ok(DateTimeFormats::DateTime(date_time))
+            }
+            3 => {
+                let native_local_timestamp_millis = <i64>::read_from(reader)?;
+                let offset = <i32>::read_from(reader)?;
+                let tz = chrono::FixedOffset::east_opt(offset)
+                    .ok_or(io::Error::new(io::ErrorKind::Other, format!("invalid offset {:?}", offset)))?;
+                let date_time = chrono::NaiveDateTime::from_timestamp_millis(native_local_timestamp_millis)
+                    .ok_or(io::Error::new(
+                        io::ErrorKind::Other,
+                        format!("invalid datetime {:?}", native_local_timestamp_millis),
+                    ))?
+                    .and_local_timezone(tz)
+                    .single()
+                    .ok_or(io::Error::new(
+                        io::ErrorKind::Other,
+                        format!(
+                            "invalid datetime with timezone {:?} {:?}",
+                            native_local_timestamp_millis, tz
+                        ),
+                    ))?;
+
+                Ok(DateTimeFormats::DateTimeWithTz(date_time))
+            }
+            _ => Err(io::Error::new(io::ErrorKind::Other, "unreachable")),
+        }
+    }
+}
+
 impl Encode for Object {
     fn write_to<W: WriteExt>(&self, writer: &mut W) -> io::Result<()> {
         match self {
@@ -131,6 +232,11 @@ impl Encode for Object {
                 Ok(())
             }
             Object::None => writer.write_u8(6),
+            Object::DateFormat(date) => {
+                writer.write_u8(7)?;
+                date.write_to(writer)?;
+                Ok(())
+            }
         }
     }
 }
@@ -178,6 +284,10 @@ impl Decode for Object {
                 Ok(Object::DynOwned(obj))
             }
             6 => Ok(Object::None),
+            7 => {
+                let date = <DateTimeFormats>::read_from(reader)?;
+                Ok(Object::DateFormat(date))
+            }
             _ => Err(io::Error::new(io::ErrorKind::Other, "not supported")),
         }
     }


Test patch:
diff --git a/interactive_engine/executor/common/dyn_type/tests/datetime_formats.rs b/interactive_engine/executor/common/dyn_type/tests/datetime_formats.rs
new file mode 100644
index 000000000000..523a217792cb
--- /dev/null
+++ b/interactive_engine/executor/common/dyn_type/tests/datetime_formats.rs
@@ -0,0 +1,269 @@
+#[cfg(test)]
+mod tests {
+    use std::vec;
+
+    use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};
+    use dyn_type::DateTimeFormats;
+    use pegasus_common::codec::{Decode, Encode};
+
+    #[test]
+    fn test_dateformat_as() {
+        // date as date
+        let date = NaiveDate::from_ymd_opt(2020, 10, 10).unwrap();
+        let date_format = DateTimeFormats::Date(date);
+        let date_recovered = date_format.as_date().unwrap();
+        assert_eq!(date_recovered, date);
+
+        // time as time
+        let time = NaiveTime::from_hms_milli_opt(10, 10, 10, 100).unwrap();
+        let time_format = DateTimeFormats::Time(time);
+        let time_recovered = time_format.as_time().unwrap();
+        assert_eq!(time_recovered, time);
+
+        // datetime as datetime
+        let date_time =
+            NaiveDateTime::parse_from_str("2020-10-10 10:10:10.100", "%Y-%m-%d %H:%M:%S%.f").unwrap();
+        let date_time_format = DateTimeFormats::DateTime(date_time);
+        let date_time_recovered = date_time_format.as_date_time().unwrap();
+        assert_eq!(date_time_recovered, date_time);
+
+        // datetime_with_tz as datetime_with_tz
+        let date_time_with_tz = DateTime::parse_from_rfc3339("2020-10-09T23:10:10.100-11:00").unwrap();
+        let date_time_with_tz_format = DateTimeFormats::DateTimeWithTz(date_time_with_tz);
+        let date_time_with_tz_recovered = date_time_with_tz_format
+            .as_date_time_with_tz()
+            .unwrap();
+        assert_eq!(date_time_with_tz_recovered, date_time_with_tz);
+
+        // date_time as date
+        let date_time_to_date = date_time_format.as_date().unwrap();
+        assert_eq!(date_time_to_date, date);
+
+        // date_time as time
+        let date_time_to_time = date_time_format.as_time().unwrap();
+        assert_eq!(date_time_to_time, time);
+
+        // date_time_with_tz as date, notice the returned date is in local timezone
+        // 2020-10-09T23:10:10.100-11:00
+        let date_time_with_tz_to_date = date_time_with_tz_format.as_date().unwrap();
+        assert_eq!(date_time_with_tz_to_date, NaiveDate::from_ymd_opt(2020, 10, 9).unwrap());
+
+        // date_time_with_tz as time, notice that the returned time is in local timezone
+        let date_time_with_tz_to_time = date_time_with_tz_format.as_time().unwrap();
+        assert_eq!(date_time_with_tz_to_time, NaiveTime::from_hms_milli_opt(23, 10, 10, 100).unwrap());
+
+        // date_time_with_tz as date_time, notice that the returned date_time is in local timezone
+        let date_time_with_tz_to_date_time = date_time_with_tz_format.as_date_time().unwrap();
+        assert_eq!(
+            date_time_with_tz_to_date_time,
+            NaiveDateTime::parse_from_str("2020-10-09 23:10:10.100", "%Y-%m-%d %H:%M:%S%.f").unwrap()
+        );
+
+        //  date_time as date_time_with_tz
+        let date_time_to_date_time_with_tz = date_time_format.as_date_time_with_tz().unwrap();
+        assert_eq!(date_time_to_date_time_with_tz, date_time_with_tz);
+    }
+
+    #[test]
+    fn test_dateformat_extract() {
+        // Date: 2020-10-10
+        let date = NaiveDate::from_ymd_opt(2020, 10, 10).unwrap();
+        let date_format = DateTimeFormats::Date(date);
+        let year = date_format.year().unwrap();
+        assert_eq!(year, 2020);
+        let month = date_format.month().unwrap();
+        assert_eq!(month, 10);
+        let day = date_format.day().unwrap();
+        assert_eq!(day, 10);
+        let timestamp = date_format.timestamp().unwrap();
+        // 2020-10-10 00:00:00
+        assert_eq!(timestamp, 1602288000);
+
+        // Time: 10:10:10.100
+        let time = NaiveTime::from_hms_milli_opt(10, 10, 10, 100).unwrap();
+        let time_format = DateTimeFormats::Time(time);
+        let hour = time_format.hour().unwrap();
+        assert_eq!(hour, 10);
+        let minute = time_format.minute().unwrap();
+        assert_eq!(minute, 10);
+        let second = time_format.second().unwrap();
+        assert_eq!(second, 10);
+        let millisecond = time_format.millisecond().unwrap();
+        assert_eq!(millisecond, 100);
+
+        // DateTime: 2020-10-10 10:10:10.100
+        let date_time =
+            NaiveDateTime::parse_from_str("2020-10-10 10:10:10.100", "%Y-%m-%d %H:%M:%S%.f").unwrap();
+        let date_time_format = DateTimeFormats::DateTime(date_time);
+        let year = date_time_format.year().unwrap();
+        assert_eq!(year, 2020);
+        let month = date_time_format.month().unwrap();
+        assert_eq!(month, 10);
+        let day = date_time_format.day().unwrap();
+        assert_eq!(day, 10);
+        let hour = date_time_format.hour().unwrap();
+        assert_eq!(hour, 10);
+        let minute = date_time_format.minute().unwrap();
+        assert_eq!(minute, 10);
+        let second = date_time_format.second().unwrap();
+        assert_eq!(second, 10);
+        let millisecond = date_time_format.millisecond().unwrap();
+        assert_eq!(millisecond, 100);
+        let timestamp = date_time_format.timestamp().unwrap();
+        assert_eq!(timestamp, 1602324610);
+        let timestamp_millis = date_time_format.timestamp_millis().unwrap();
+        assert_eq!(timestamp_millis, 1602324610100);
+
+        // DateTimeWithTz: 2020-10-09T23:10:10.100-11:00
+        let date_time_with_tz = DateTime::parse_from_rfc3339("2020-10-09T23:10:10.100-11:00").unwrap();
+        let date_time_with_tz_format = DateTimeFormats::DateTimeWithTz(date_time_with_tz);
+        let year = date_time_with_tz_format.year().unwrap();
+        assert_eq!(year, 2020);
+        let month = date_time_with_tz_format.month().unwrap();
+        assert_eq!(month, 10);
+        let day = date_time_with_tz_format.day().unwrap();
+        assert_eq!(day, 9);
+        let hour = date_time_with_tz_format.hour().unwrap();
+        assert_eq!(hour, 23);
+        let minute = date_time_with_tz_format.minute().unwrap();
+        assert_eq!(minute, 10);
+        let second = date_time_with_tz_format.second().unwrap();
+        assert_eq!(second, 10);
+        let millisecond = date_time_with_tz_format.millisecond().unwrap();
+        assert_eq!(millisecond, 100);
+        let timestamp = date_time_with_tz_format.timestamp().unwrap();
+        assert_eq!(timestamp, 1602324610);
+        let timestamp_millis = date_time_with_tz_format
+            .timestamp_millis()
+            .unwrap();
+        assert_eq!(timestamp_millis, 1602324610100);
+    }
+
+    #[test]
+    fn test_dateformat_cmp() {
+        // Date comparason
+        // 2020-10-10
+        let date = NaiveDate::from_ymd_opt(2020, 10, 10).unwrap();
+        let date_format = DateTimeFormats::Date(date);
+        // 2020-10-11
+        let date2 = NaiveDate::from_ymd_opt(2020, 10, 11).unwrap();
+        let date_format2 = DateTimeFormats::Date(date2);
+        assert!(date_format < date_format2);
+
+        // Time comparason
+        // 10:10:10
+        let time = NaiveTime::from_hms_opt(10, 10, 10).unwrap();
+        let time_format = DateTimeFormats::Time(time);
+        // 10:10:11
+        let time2 = NaiveTime::from_hms_opt(10, 10, 11).unwrap();
+        let time_format2 = DateTimeFormats::Time(time2);
+        assert!(time_format < time_format2);
+
+        // DateTime comparason
+        // 2020-10-10 10:10:10.100
+        let date_time =
+            NaiveDateTime::parse_from_str("2020-10-10 10:10:10.100", "%Y-%m-%d %H:%M:%S%.f").unwrap();
+        let date_time_format = DateTimeFormats::DateTime(date_time);
+        // 2020-10-10 10:10:11.100
+        let date_time2 =
+            NaiveDateTime::parse_from_str("2020-10-10 10:10:11.100", "%Y-%m-%d %H:%M:%S%.f").unwrap();
+        let date_time_format2 = DateTimeFormats::DateTime(date_time2);
+        assert!(date_time_format < date_time_format2);
+
+        // DateTimeWithTz comparason
+        // 2020-10-09T23:10:10.100-11:00
+        let date_time_with_tz = DateTime::parse_from_rfc3339("2020-10-09T23:10:10.100-11:00").unwrap();
+        let date_time_with_tz_format = DateTimeFormats::DateTimeWithTz(date_time_with_tz);
+        // 2020-10-09T23:10:11.100-11:00
+        let date_time_with_tz2 = DateTime::parse_from_rfc3339("2020-10-09T23:10:11.100-11:00").unwrap();
+        let date_time_with_tz_format2 = DateTimeFormats::DateTimeWithTz(date_time_with_tz2);
+        assert!(date_time_with_tz_format < date_time_with_tz_format2);
+
+        // DateTime and DateTimeWithTz comparison
+        assert!(date_time_with_tz_format < date_time_format2);
+        assert!(date_time_format < date_time_with_tz_format2);
+
+        // uncomparable cases
+        assert_eq!(date_format < date_time_format || date_format > date_time_format, false);
+        assert_eq!(date_format < date_time_with_tz_format || date_format > date_time_with_tz_format, false);
+        assert_eq!(time_format < date_time_format || time_format > date_time_format, false);
+        assert_eq!(time_format < date_time_with_tz_format || time_format > date_time_with_tz_format, false);
+    }
+
+    #[test]
+    fn test_dateformat_eq() {
+        // Date equality
+        // 2020-10-10
+        let date = NaiveDate::from_ymd_opt(2020, 10, 10).unwrap();
+        let date_format = DateTimeFormats::Date(date);
+        // 2020-10-10
+        let date2 = NaiveDate::from_yo_opt(2020, 284).unwrap();
+        let date_format2 = DateTimeFormats::Date(date2);
+        assert_eq!(date_format, date_format2);
+
+        // Time equality
+        // 10:10:10
+        let time = NaiveTime::from_hms_opt(10, 10, 10).unwrap();
+        let time_format = DateTimeFormats::Time(time);
+        // 10:10:10.000
+        let time2 = NaiveTime::from_hms_milli_opt(10, 10, 10, 0).unwrap();
+        let time_format2 = DateTimeFormats::Time(time2);
+        assert_eq!(time_format, time_format2);
+
+        // DateTime equality
+        // 2020-10-10 10:10:10.100
+        let date_time =
+            NaiveDateTime::parse_from_str("2020-10-10 10:10:10.100", "%Y-%m-%d %H:%M:%S%.f").unwrap();
+        let date_time_format = DateTimeFormats::DateTime(date_time);
+        // 2020-10-10 10:10:10.100
+        let date_time2 = NaiveDateTime::from_timestamp_millis(1602324610100).unwrap();
+        let date_time_format2 = DateTimeFormats::DateTime(date_time2);
+        assert_eq!(date_time_format, date_time_format2);
+
+        // DateTimeWithTz equality
+        // 2020-10-09T23:10:10.100-11:00
+        let date_time_with_tz = DateTime::parse_from_rfc3339("2020-10-09T23:10:10.100-11:00").unwrap();
+        let date_time_with_tz_format = DateTimeFormats::DateTimeWithTz(date_time_with_tz);
+        // 2020-10-10T18:10:10.100+08:00
+        let date_time_with_tz2 = DateTime::parse_from_rfc3339("2020-10-10T18:10:10.100+08:00").unwrap();
+        let date_time_with_tz_format2 = DateTimeFormats::DateTimeWithTz(date_time_with_tz2);
+        assert_eq!(date_time_with_tz_format, date_time_with_tz_format2);
+
+        // DateTime v.s.  DateTimeWithTz
+        assert_eq!(date_time_format, date_time_with_tz_format);
+    }
+
+    #[test]
+    fn test_dateformat_ser_de() {
+        let date: NaiveDate = NaiveDate::from_ymd_opt(2020, 10, 10).unwrap();
+        let date_format = DateTimeFormats::Date(date);
+        let mut bytes = vec![];
+        date_format.write_to(&mut bytes).unwrap();
+        let date_format_de = <DateTimeFormats>::read_from(&mut bytes.as_slice()).unwrap();
+        assert_eq!(date_format, date_format_de);
+
+        let time = NaiveTime::from_hms_milli_opt(10, 10, 10, 100).unwrap();
+        let time_format = DateTimeFormats::Time(time);
+        let mut bytes = vec![];
+        time_format.write_to(&mut bytes).unwrap();
+        let time_format_de = <DateTimeFormats>::read_from(&mut bytes.as_slice()).unwrap();
+        assert_eq!(time_format, time_format_de);
+
+        let date_time =
+            NaiveDateTime::parse_from_str("2020-10-10 10:10:10.100", "%Y-%m-%d %H:%M:%S%.f").unwrap();
+        let date_time_format = DateTimeFormats::DateTime(date_time);
+        let mut bytes = vec![];
+        date_time_format.write_to(&mut bytes).unwrap();
+        let date_time_format_de = <DateTimeFormats>::read_from(&mut bytes.as_slice()).unwrap();
+        assert_eq!(date_time_format, date_time_format_de);
+
+        let date_time_with_tz = DateTime::parse_from_rfc3339("2020-10-09T23:10:10.100-11:00").unwrap();
+        let date_time_with_tz_format = DateTimeFormats::DateTimeWithTz(date_time_with_tz);
+        let mut bytes = vec![];
+        date_time_with_tz_format
+            .write_to(&mut bytes)
+            .unwrap();
+        let date_time_with_tz_format_de = <DateTimeFormats>::read_from(&mut bytes.as_slice()).unwrap();
+        assert_eq!(date_time_with_tz_format, date_time_with_tz_format_de);
+    }
+}
diff --git a/interactive_engine/executor/common/dyn_type/tests/object.rs b/interactive_engine/executor/common/dyn_type/tests/object.rs
index ee10178fd820..f286ef258fbe 100644
--- a/interactive_engine/executor/common/dyn_type/tests/object.rs
+++ b/interactive_engine/executor/common/dyn_type/tests/object.rs
@@ -144,4 +144,46 @@ mod tests {
         assert_eq!(right.partial_cmp(&left), Some(Ordering::Greater));
         assert_eq!(*&*right, 8_u128);
     }
+
+    #[test]
+    fn test_object_as_dateformat() {
+        let date = chrono::NaiveDate::from_ymd_opt(2020, 10, 10).unwrap();
+        let date_obj = Object::from(date);
+        let date_recovered = date_obj
+            .as_date_format()
+            .unwrap()
+            .as_date()
+            .unwrap();
+        assert_eq!(date_recovered, date);
+
+        let time = chrono::NaiveTime::from_hms_opt(10, 10, 10).unwrap();
+        let time_obj = Object::from(time);
+        let time_recovered = time_obj
+            .as_date_format()
+            .unwrap()
+            .as_time()
+            .unwrap();
+        assert_eq!(time_recovered, time);
+
+        let date_time =
+            chrono::NaiveDateTime::parse_from_str("2020-10-10 10:10:10.100", "%Y-%m-%d %H:%M:%S%.f")
+                .unwrap();
+        let date_time_obj = Object::from(date_time);
+        let date_time_recovered = date_time_obj
+            .as_date_format()
+            .unwrap()
+            .as_date_time()
+            .unwrap();
+        assert_eq!(date_time_recovered, date_time);
+
+        let date_time_with_tz =
+            chrono::DateTime::parse_from_rfc3339("2020-10-09T23:10:10.100-11:00").unwrap();
+        let date_time_with_tz_obj = Object::from(date_time_with_tz);
+        let date_time_with_tz_recovered = date_time_with_tz_obj
+            .as_date_format()
+            .unwrap()
+            .as_date_time_with_tz()
+            .unwrap();
+        assert_eq!(date_time_with_tz_recovered, date_time_with_tz);
+    }
 }