#[cfg(feature = "std")]
use core::fmt::{Debug, Display, Formatter};
use core::ops::Deref;
#[cfg(feature = "arrow")]
use arrow2::{
array::MutablePrimitiveArray,
datatypes::{DataType, TimeUnit},
};
#[cfg(feature = "arrow")]
use arrow2_convert::{field::ArrowField, serialize::ArrowSerialize};
use bincode::BorrowDecode;
#[cfg(feature = "std")]
use bincode::{Decode, Encode};
#[cfg(feature = "pg")]
use postgres_types::{FromSql, ToSql};
use rapira::{Rapira, RapiraError};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use serde_json::Value as JsonValue;
#[cfg(feature = "ts-types")]
use specta::Type;
#[cfg(feature = "std")]
use thiserror::Error;
#[cfg(feature = "std")]
use time::format_description::well_known::Rfc3339;
use time::{Duration as TimeDuration, OffsetDateTime};
#[cfg(feature = "ts-types")]
use typescript_type_def::{
type_expr::{DefinedTypeInfo, Docs, Ident, TypeDefinition, TypeExpr, TypeInfo},
TypeDef,
};
#[cfg(feature = "wa_proto")]
use wa_proto::{Incoming, Outcoming};
use crate::{GetType, Typ, Value};
#[cfg_attr(feature = "std", derive(Error, Debug))]
pub enum DatetimeError {
#[cfg_attr(feature = "std", error("string parse error"))]
ParseError,
#[cfg_attr(feature = "std", error("json parse error"))]
JsonError,
}
#[derive(PartialEq, Clone, Copy, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "std", derive(Deserialize, Debug))]
#[cfg_attr(feature = "ts-types", derive(Type))]
#[cfg_attr(feature = "wa_proto", derive(Outcoming, Incoming))]
#[cfg_attr(feature = "pg", derive(FromSql, ToSql))]
#[cfg_attr(feature = "pg", postgres(transparent))]
#[repr(transparent)]
pub struct Datetime(OffsetDateTime);
impl Datetime {
pub fn new(dt: OffsetDateTime) -> Self {
Self(dt)
}
#[cfg(feature = "std")]
pub fn now() -> Self {
Self(OffsetDateTime::now_utc())
}
pub fn get_native_dt(&self) -> OffsetDateTime {
self.0
}
#[cfg(feature = "std")]
pub fn to_str(self) -> String {
let dt = self.0.replace_millisecond(0).unwrap();
dt.format(&Rfc3339).expect("cannot format datetime")
}
#[cfg(feature = "std")]
pub fn to_json(self) -> JsonValue {
JsonValue::String(self.to_str())
}
#[cfg(feature = "std")]
pub fn from_string(value: &str) -> Result<Self, DatetimeError> {
OffsetDateTime::parse(value, &Rfc3339)
.map(Datetime)
.map_err(|_| DatetimeError::ParseError)
}
#[cfg(feature = "std")]
pub fn from_json(value: JsonValue) -> Result<Self, DatetimeError> {
if let JsonValue::String(s) = value {
Self::from_string(&s)
} else {
Err(DatetimeError::JsonError)
}
}
#[inline]
pub fn to_i64(self) -> i64 {
let ts = self.0.unix_timestamp() * 1000;
let ms = self.0.millisecond();
ts + ms as i64
}
#[inline]
pub fn from_i64(i: i64) -> Result<Self, time::error::ComponentRange> {
let ms = i % 1000;
let ts = i / 1000;
let dt = OffsetDateTime::from_unix_timestamp(ts)? + TimeDuration::milliseconds(ms);
Ok(Datetime(dt))
}
}
impl Default for Datetime {
fn default() -> Self {
#[cfg(feature = "std")]
let created = OffsetDateTime::now_utc();
#[cfg(not(feature = "std"))]
let created = OffsetDateTime::UNIX_EPOCH;
Datetime(created)
}
}
#[cfg(feature = "std")]
impl Serialize for Datetime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = self.to_str();
serializer.serialize_str(&s)
}
}
#[cfg(feature = "std")]
impl Display for Datetime {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_str())
}
}
impl Deref for Datetime {
type Target = OffsetDateTime;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "std")]
impl Encode for Datetime {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> core::result::Result<(), bincode::error::EncodeError> {
let tsm = self.to_i64();
bincode::Encode::encode(&tsm, encoder)?;
Ok(())
}
}
#[cfg(feature = "std")]
impl Decode for Datetime {
fn decode<D: bincode::de::Decoder>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let tsm: i64 = bincode::de::Decode::decode(decoder)?;
Self::from_i64(tsm).map_err(|_| {
bincode::error::DecodeError::OtherString(
"OffsetDateTime::from_unix_timestamp error".to_owned(),
)
})
}
}
impl<'de> BorrowDecode<'de> for Datetime {
fn borrow_decode<D: bincode::de::BorrowDecoder<'de>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let tsm: i64 = bincode::de::Decode::decode(decoder)?;
Self::from_i64(tsm).map_err(|_| {
bincode::error::DecodeError::OtherString(
"OffsetDateTime::from_unix_timestamp error".to_owned(),
)
})
}
}
impl Rapira for Datetime {
const STATIC_SIZE: Option<usize> = Some(8);
#[inline]
fn from_slice(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
where
Self: Sized,
{
let i = i64::from_slice(slice)?;
Self::from_i64(i).map_err(|_| RapiraError::DatetimeError)
}
#[inline]
fn check_bytes(slice: &mut &[u8]) -> Result<(), rapira::RapiraError>
where
Self: Sized,
{
let i = i64::from_slice(slice)?;
let ts = i / 1000;
OffsetDateTime::from_unix_timestamp(ts).map_err(|_| RapiraError::DatetimeError)?;
Ok(())
}
#[inline]
unsafe fn from_slice_unsafe(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
where
Self: Sized,
{
let u = i64::from_slice_unsafe(slice)?;
Self::from_i64(u).map_err(|_| RapiraError::DatetimeError)
}
#[inline]
fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
let tsm = self.to_i64();
tsm.convert_to_bytes(slice, cursor);
}
#[inline]
fn size(&self) -> usize {
8
}
}
impl GetType for Datetime {
const TYPE: Typ = Typ::Datetime;
}
impl From<Datetime> for Value {
fn from(val: Datetime) -> Self {
Value::from(val.get_native_dt())
}
}
#[cfg(feature = "ts-types")]
impl TypeDef for Datetime {
const INFO: TypeInfo = TypeInfo::Defined(DefinedTypeInfo {
def: TypeDefinition {
docs: Some(Docs("1985-04-12T23:20:50.52Z")),
path: &[],
name: Ident("Datetime"),
generic_vars: &[],
def: TypeExpr::Ref(&String::INFO),
},
generic_args: &[],
});
}
#[cfg(feature = "arrow")]
impl ArrowField for Datetime {
type Type = Datetime;
fn data_type() -> DataType {
DataType::Timestamp(TimeUnit::Millisecond, None)
}
}
#[cfg(feature = "arrow")]
impl ArrowSerialize for Datetime {
type MutableArrayType = MutablePrimitiveArray<i64>;
fn new_array() -> Self::MutableArrayType {
MutablePrimitiveArray::new()
}
fn arrow_serialize(
v: &<Self as ArrowField>::Type,
array: &mut Self::MutableArrayType,
) -> arrow2::error::Result<()> {
array.push(Some(v.to_i64()));
Ok(())
}
}