use crate::haystack::val::{ConversionError, Value};
use crate::timezone::{
DateTimeType, is_utc, make_date_time, make_date_time_with_tz, timezone_short_name, utc_now,
};
use chrono::{DateTime as DateTimeImpl, FixedOffset, Utc};
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Formatter};
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub struct DateTime {
value: DateTimeType,
}
impl DateTime {
pub fn parse_from_rfc3339(arg: &str) -> Result<DateTime, String> {
match DateTimeImpl::<FixedOffset>::parse_from_rfc3339(arg) {
Ok(value) => Ok(DateTime {
value: make_date_time(value)?,
}),
Err(err) => Err(format!("Can't parse date time {err}")),
}
}
pub fn parse_from_rfc3339_with_timezone(datetime: &str, tz: &str) -> Result<DateTime, String> {
match DateTimeImpl::<FixedOffset>::parse_from_rfc3339(datetime) {
Ok(value) => Ok(DateTime {
value: make_date_time_with_tz(&value, tz)?,
}),
Err(err) => Err(format!("Can't parse date time {err}")),
}
}
pub fn utc_now() -> Self {
DateTime { value: utc_now() }
}
pub fn is_utc(&self) -> bool {
is_utc(&self.value)
}
pub fn timezone_short_name(&self) -> String {
timezone_short_name(&self.value)
}
}
impl PartialOrd for DateTime {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DateTime {
fn cmp(&self, other: &Self) -> Ordering {
self.value.cmp(&other.value)
}
}
impl Deref for DateTime {
type Target = DateTimeType;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl DerefMut for DateTime {
#[inline]
fn deref_mut(&mut self) -> &mut DateTimeType {
&mut self.value
}
}
impl FromStr for DateTime {
type Err = String;
fn from_str(val: &str) -> Result<Self, Self::Err> {
DateTime::parse_from_rfc3339(val)
}
}
impl Display for DateTime {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(
f,
"{}{}",
self.naive_local().format("%Y-%m-%dT%H:%M:%S"),
self.timezone_short_name()
)
}
}
impl From<DateTime> for Value {
fn from(value: DateTime) -> Self {
Value::DateTime(value)
}
}
#[cfg(feature = "timezone-db")]
impl From<DateTimeImpl<Utc>> for Value {
fn from(from: DateTimeImpl<Utc>) -> Self {
Value::DateTime(DateTime::from(from))
}
}
#[cfg(not(feature = "timezone-db"))]
impl From<DateTimeImpl<Utc>> for Value {
fn from(from: DateTimeImpl<Utc>) -> Self {
Value::DateTime(DateTime { value: from.into() })
}
}
#[cfg(not(feature = "timezone-db"))]
impl From<DateTimeImpl<Utc>> for DateTime {
fn from(from: DateTimeImpl<Utc>) -> Self {
DateTime { value: from.into() }
}
}
#[cfg(not(feature = "timezone-db"))]
impl From<DateTimeImpl<FixedOffset>> for DateTime {
fn from(from: DateTimeImpl<FixedOffset>) -> Self {
DateTime { value: from }
}
}
#[cfg(feature = "timezone-db")]
impl From<DateTimeImpl<Utc>> for DateTime {
fn from(from: DateTimeImpl<Utc>) -> Self {
DateTime {
value: from.with_timezone(&chrono_tz::UTC),
}
}
}
#[cfg(feature = "timezone-db")]
impl From<DateTimeImpl<chrono_tz::Tz>> for DateTime {
fn from(value: DateTimeImpl<chrono_tz::Tz>) -> Self {
DateTime { value }
}
}
impl TryFrom<&Value> for DateTime {
type Error = ConversionError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::DateTime(v) => Ok(*v),
_ => Err("Value is not an `DateTime`"),
}
}
}