use super::datetime_support::*;
use super::{DateTimeTypeVisitor, Interval, TimeWithTimeZone, Timestamp, TimestampWithTimeZone};
use crate::datum::{FromDatum, IntoDatum};
use crate::{direct_function_call, pg_sys};
use pgrx_pg_sys::PgTryBuilder;
use pgrx_pg_sys::errcodes::PgSqlErrorCode;
use pgrx_sql_entity_graph::metadata::{
ArgumentError, ReturnsError, ReturnsRef, SqlMappingRef, SqlTranslatable,
};
use std::num::TryFromIntError;
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct Time(pg_sys::TimeADT);
const MICROSECONDS_PER_DAY: pg_sys::TimeADT = 24 * 60 * 60 * 1000 * 1000 ;
impl From<Time> for pg_sys::TimeADT {
#[inline]
fn from(value: Time) -> Self {
value.0
}
}
impl From<Timestamp> for Time {
fn from(value: Timestamp) -> Self {
unsafe { direct_function_call(pg_sys::timestamp_time, &[value.into_datum()]).unwrap() }
}
}
impl From<TimestampWithTimeZone> for Time {
#[inline]
fn from(value: TimestampWithTimeZone) -> Self {
unsafe { direct_function_call(pg_sys::timestamptz_time, &[value.into_datum()]).unwrap() }
}
}
impl From<Interval> for Time {
fn from(value: Interval) -> Self {
unsafe { direct_function_call(pg_sys::interval_time, &[value.into_datum()]).unwrap() }
}
}
impl From<TimeWithTimeZone> for Time {
fn from(value: TimeWithTimeZone) -> Self {
unsafe { direct_function_call(pg_sys::timetz_time, &[value.into_datum()]).unwrap() }
}
}
impl TryFrom<pg_sys::TimeADT> for Time {
type Error = pg_sys::TimeADT;
#[inline]
fn try_from(raw: pg_sys::TimeADT) -> Result<Self, Self::Error> {
const MORE_THAN_A_DAY: i64 = MICROSECONDS_PER_DAY + 1;
match raw {
0..=MICROSECONDS_PER_DAY => Ok(Time(raw)),
i64::MIN..=-1 | MORE_THAN_A_DAY.. => Err(raw),
}
}
}
impl TryFrom<pg_sys::Datum> for Time {
type Error = TryFromIntError;
#[inline]
fn try_from(d: pg_sys::Datum) -> Result<Self, Self::Error> {
let t: pg_sys::TimeADT = d.value().try_into()?;
Ok(Time(t))
}
}
impl FromDatum for Time {
#[inline]
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
is_null: bool,
_typoid: pg_sys::Oid,
) -> Option<Time> {
if is_null { None } else { Some(Time::modular_from_raw(datum.value() as i64)) }
}
}
impl IntoDatum for Time {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(self.0))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::TIMEOID
}
}
impl Time {
pub const ALLBALLS: Self = Time(0);
pub fn new(hour: u8, minute: u8, second: f64) -> Result<Time, DateTimeConversionError> {
let hour: i32 = hour as _;
let minute: i32 = minute as _;
PgTryBuilder::new(|| unsafe {
Ok(direct_function_call(
pg_sys::make_time,
&[hour.into_datum(), minute.into_datum(), second.into_datum()],
)
.unwrap())
})
.catch_when(PgSqlErrorCode::ERRCODE_DATETIME_FIELD_OVERFLOW, |_| {
Err(DateTimeConversionError::FieldOverflow)
})
.catch_when(PgSqlErrorCode::ERRCODE_INVALID_DATETIME_FORMAT, |_| {
Err(DateTimeConversionError::InvalidFormat)
})
.execute()
}
pub fn new_unchecked(hour: u8, minute: u8, second: f64) -> Time {
let hour: i32 = hour.try_into().expect("invalid hour");
let minute: i32 = minute.try_into().expect("invalid minute");
unsafe {
direct_function_call(
pg_sys::make_time,
&[hour.into_datum(), minute.into_datum(), second.into_datum()],
)
.unwrap()
}
}
pub fn modular_from_raw(time: i64) -> Self {
Self(time.rem_euclid(MICROSECONDS_PER_DAY))
}
pub fn hour(&self) -> u8 {
self.extract_part(DateTimeParts::Hour).unwrap().try_into().unwrap()
}
pub fn minute(&self) -> u8 {
self.extract_part(DateTimeParts::Minute).unwrap().try_into().unwrap()
}
pub fn second(&self) -> f64 {
self.extract_part(DateTimeParts::Second).unwrap().try_into().unwrap()
}
pub fn microseconds(&self) -> u32 {
self.extract_part(DateTimeParts::Microseconds).unwrap().try_into().unwrap()
}
pub fn to_hms_micro(&self) -> (u8, u8, u8, u32) {
(self.hour(), self.minute(), self.second() as u8, self.microseconds())
}
#[inline]
pub fn into_inner(self) -> pg_sys::TimeADT {
self.0
}
}
impl serde::Serialize for Time {
fn serialize<S>(
&self,
serializer: S,
) -> std::result::Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>
where
S: serde::Serializer,
{
serializer
.serialize_str(&self.to_iso_string())
.map_err(|err| serde::ser::Error::custom(format!("formatting problem: {err:?}")))
}
}
impl<'de> serde::Deserialize<'de> for Time {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
deserializer.deserialize_str(DateTimeTypeVisitor::<Self>::new())
}
}
unsafe impl SqlTranslatable for Time {
const TYPE_IDENT: &'static str = crate::pgrx_resolved_type!(Time);
const TYPE_ORIGIN: pgrx_sql_entity_graph::metadata::TypeOrigin =
pgrx_sql_entity_graph::metadata::TypeOrigin::External;
const ARGUMENT_SQL: Result<SqlMappingRef, ArgumentError> = Ok(SqlMappingRef::literal("time"));
const RETURN_SQL: Result<ReturnsRef, ReturnsError> =
Ok(ReturnsRef::One(SqlMappingRef::literal("time")));
}