use std::hash::Hash;
use arrow::datatypes::IntervalUnit as ArrowIntervalUnit;
use serde::{Deserialize, Serialize};
#[derive(
Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
pub enum IntervalUnit {
YearMonth,
DayTime,
#[default]
MonthDayNano,
}
impl From<&ArrowIntervalUnit> for IntervalUnit {
fn from(unit: &ArrowIntervalUnit) -> Self {
match unit {
ArrowIntervalUnit::YearMonth => IntervalUnit::YearMonth,
ArrowIntervalUnit::DayTime => IntervalUnit::DayTime,
ArrowIntervalUnit::MonthDayNano => IntervalUnit::MonthDayNano,
}
}
}
impl From<ArrowIntervalUnit> for IntervalUnit {
fn from(unit: ArrowIntervalUnit) -> Self {
(&unit).into()
}
}
#[derive(
Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
)]
#[repr(C)]
pub struct IntervalYearMonth {
pub months: i32,
}
impl IntervalYearMonth {
pub fn new(months: i32) -> Self {
Self { months }
}
pub fn to_i32(&self) -> i32 {
self.months
}
pub fn from_i32(months: i32) -> Self {
Self { months }
}
pub fn negative(&self) -> Self {
Self::new(-self.months)
}
pub fn to_iso8601_string(&self) -> String {
IntervalFormat::from(*self).to_iso8601_string()
}
}
impl From<IntervalYearMonth> for IntervalFormat {
fn from(interval: IntervalYearMonth) -> Self {
IntervalFormat {
years: interval.months / 12,
months: interval.months % 12,
..Default::default()
}
}
}
impl From<i32> for IntervalYearMonth {
fn from(v: i32) -> Self {
Self::from_i32(v)
}
}
impl From<IntervalYearMonth> for i32 {
fn from(v: IntervalYearMonth) -> Self {
v.to_i32()
}
}
impl From<IntervalYearMonth> for serde_json::Value {
fn from(v: IntervalYearMonth) -> Self {
serde_json::Value::from(v.to_i32())
}
}
#[derive(
Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
)]
#[repr(C)]
pub struct IntervalDayTime {
pub days: i32,
pub milliseconds: i32,
}
impl IntervalDayTime {
pub const ZERO: Self = Self::new(0, 0);
pub const MINUS_ONE: Self = Self::new(-1, -1);
pub const MAX: Self = Self::new(i32::MAX, i32::MAX);
pub const MIN: Self = Self::new(i32::MIN, i32::MIN);
pub const fn new(days: i32, milliseconds: i32) -> Self {
Self { days, milliseconds }
}
pub fn to_i64(&self) -> i64 {
let d = (self.days as u64 & u32::MAX as u64) << 32;
let m = self.milliseconds as u64 & u32::MAX as u64;
(d | m) as i64
}
pub fn from_i64(value: i64) -> Self {
let days = (value >> 32) as i32;
let milliseconds = value as i32;
Self { days, milliseconds }
}
pub fn negative(&self) -> Self {
Self::new(-self.days, -self.milliseconds)
}
pub fn to_iso8601_string(&self) -> String {
IntervalFormat::from(*self).to_iso8601_string()
}
pub fn as_millis(&self) -> i64 {
self.days as i64 * MS_PER_DAY + self.milliseconds as i64
}
}
impl From<i64> for IntervalDayTime {
fn from(v: i64) -> Self {
Self::from_i64(v)
}
}
impl From<IntervalDayTime> for i64 {
fn from(v: IntervalDayTime) -> Self {
v.to_i64()
}
}
impl From<IntervalDayTime> for serde_json::Value {
fn from(v: IntervalDayTime) -> Self {
serde_json::Value::from(v.to_i64())
}
}
pub const MS_PER_SEC: i64 = 1_000;
pub const MS_PER_MINUTE: i64 = 60 * MS_PER_SEC;
pub const MS_PER_HOUR: i64 = 60 * MS_PER_MINUTE;
pub const MS_PER_DAY: i64 = 24 * MS_PER_HOUR;
pub const NANOS_PER_MILLI: i64 = 1_000_000;
impl From<IntervalDayTime> for IntervalFormat {
fn from(interval: IntervalDayTime) -> Self {
IntervalFormat {
days: interval.days,
hours: interval.milliseconds as i64 / MS_PER_HOUR,
minutes: (interval.milliseconds as i64 % MS_PER_HOUR) / MS_PER_MINUTE,
seconds: (interval.milliseconds as i64 % MS_PER_MINUTE) / MS_PER_SEC,
microseconds: (interval.milliseconds as i64 % MS_PER_SEC) * MS_PER_SEC,
..Default::default()
}
}
}
#[derive(
Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
)]
#[repr(C)]
pub struct IntervalMonthDayNano {
pub months: i32,
pub days: i32,
pub nanoseconds: i64,
}
impl IntervalMonthDayNano {
pub const ZERO: Self = Self::new(0, 0, 0);
pub const MINUS_ONE: Self = Self::new(-1, -1, -1);
pub const MAX: Self = Self::new(i32::MAX, i32::MAX, i64::MAX);
pub const MIN: Self = Self::new(i32::MIN, i32::MIN, i64::MIN);
pub const fn new(months: i32, days: i32, nanoseconds: i64) -> Self {
Self {
months,
days,
nanoseconds,
}
}
pub fn to_i128(&self) -> i128 {
let m = (self.months as u128 & u32::MAX as u128) << 96;
let d = (self.days as u128 & u32::MAX as u128) << 64;
let n = self.nanoseconds as u128 & u64::MAX as u128;
(m | d | n) as i128
}
pub fn from_i128(value: i128) -> Self {
let months = (value >> 96) as i32;
let days = (value >> 64) as i32;
let nanoseconds = value as i64;
Self {
months,
days,
nanoseconds,
}
}
pub fn negative(&self) -> Self {
Self::new(-self.months, -self.days, -self.nanoseconds)
}
pub fn to_iso8601_string(&self) -> String {
IntervalFormat::from(*self).to_iso8601_string()
}
}
impl From<i128> for IntervalMonthDayNano {
fn from(v: i128) -> Self {
Self::from_i128(v)
}
}
impl From<IntervalMonthDayNano> for i128 {
fn from(v: IntervalMonthDayNano) -> Self {
v.to_i128()
}
}
impl From<IntervalMonthDayNano> for serde_json::Value {
fn from(v: IntervalMonthDayNano) -> Self {
serde_json::Value::from(v.to_i128().to_string())
}
}
pub const NS_PER_SEC: i64 = 1_000_000_000;
pub const NS_PER_MINUTE: i64 = 60 * NS_PER_SEC;
pub const NS_PER_HOUR: i64 = 60 * NS_PER_MINUTE;
pub const NS_PER_DAY: i64 = 24 * NS_PER_HOUR;
impl From<IntervalMonthDayNano> for IntervalFormat {
fn from(interval: IntervalMonthDayNano) -> Self {
IntervalFormat {
years: interval.months / 12,
months: interval.months % 12,
days: interval.days,
hours: interval.nanoseconds / NS_PER_HOUR,
minutes: (interval.nanoseconds % NS_PER_HOUR) / NS_PER_MINUTE,
seconds: (interval.nanoseconds % NS_PER_MINUTE) / NS_PER_SEC,
microseconds: (interval.nanoseconds % NS_PER_SEC) / 1_000,
}
}
}
pub fn interval_year_month_to_month_day_nano(interval: IntervalYearMonth) -> IntervalMonthDayNano {
IntervalMonthDayNano {
months: interval.months,
days: 0,
nanoseconds: 0,
}
}
pub fn interval_day_time_to_month_day_nano(interval: IntervalDayTime) -> IntervalMonthDayNano {
IntervalMonthDayNano {
months: 0,
days: interval.days,
nanoseconds: interval.milliseconds as i64 * NANOS_PER_MILLI,
}
}
#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
pub struct IntervalFormat {
pub years: i32,
pub months: i32,
pub days: i32,
pub hours: i64,
pub minutes: i64,
pub seconds: i64,
pub microseconds: i64,
}
impl IntervalFormat {
pub fn is_zero(&self) -> bool {
self.years == 0
&& self.months == 0
&& self.days == 0
&& self.hours == 0
&& self.minutes == 0
&& self.seconds == 0
&& self.microseconds == 0
}
pub fn has_year_month(&self) -> bool {
self.years != 0 || self.months != 0
}
pub fn has_day(&self) -> bool {
self.days != 0
}
pub fn has_time_part_positive(&self) -> bool {
self.hours > 0 || self.minutes > 0 || self.seconds > 0 || self.microseconds > 0
}
pub fn has_time_part(&self) -> bool {
self.hours != 0 || self.minutes != 0 || self.seconds != 0 || self.microseconds != 0
}
pub fn to_iso8601_string(&self) -> String {
if self.is_zero() {
return "PT0S".to_string();
}
let fract_str = match self.microseconds {
0 => String::default(),
_ => format!(".{:06}", self.microseconds)
.trim_end_matches('0')
.to_string(),
};
format!(
"P{}Y{}M{}DT{}H{}M{}{}S",
self.years, self.months, self.days, self.hours, self.minutes, self.seconds, fract_str
)
}
pub fn to_sql_standard_string(self) -> String {
if self.is_zero() {
"0".to_string()
} else if !self.has_time_part() && !self.has_day() {
get_year_month(self.months, self.years, true)
} else if !self.has_time_part() && !self.has_year_month() {
format!("{} 0:00:00", self.days)
} else if !self.has_year_month() && !self.has_day() {
get_time_part(
self.hours,
self.minutes,
self.seconds,
self.microseconds,
self.has_time_part_positive(),
true,
)
} else {
let year_month = get_year_month(self.months, self.years, false);
let time_interval = get_time_part(
self.hours,
self.minutes,
self.seconds,
self.microseconds,
self.has_time_part_positive(),
false,
);
format!("{} {:+} {}", year_month, self.days, time_interval)
}
}
pub fn to_postgres_string(&self) -> String {
if self.is_zero() {
return "00:00:00".to_string();
}
let mut result = String::default();
if self.has_year_month() {
if self.years != 0 {
result.push_str(&format!("{} year ", self.years));
}
if self.months != 0 {
result.push_str(&format!("{} mons ", self.months));
}
}
if self.has_day() {
result.push_str(&format!("{} days ", self.days));
}
result.push_str(&self.get_postgres_time_part());
result.trim().to_string()
}
fn get_postgres_time_part(&self) -> String {
let mut time_part = String::default();
if self.has_time_part() {
let sign = if !self.has_time_part_positive() {
"-"
} else {
""
};
let hours = Self::padding_i64(self.hours);
time_part.push_str(&format!(
"{}{}:{}:{}",
sign,
hours,
Self::padding_i64(self.minutes),
Self::padding_i64(self.seconds),
));
if self.microseconds != 0 {
time_part.push_str(&format!(".{:06}", self.microseconds.unsigned_abs()))
}
}
time_part
}
fn padding_i64(val: i64) -> String {
let num = if val < 0 {
val.unsigned_abs()
} else {
val as u64
};
format!("{:02}", num)
}
}
fn get_year_month(mons: i32, years: i32, is_only_year_month: bool) -> String {
let months = mons.unsigned_abs();
if years == 0 || is_only_year_month {
format!("{}-{}", years, months)
} else {
format!("{:+}-{}", years, months)
}
}
fn get_time_part(
hours: i64,
mins: i64,
secs: i64,
micros: i64,
is_time_part_positive: bool,
is_only_time: bool,
) -> String {
let mut interval = String::default();
if is_time_part_positive && is_only_time {
interval.push_str(&format!("{}:{:02}:{:02}", hours, mins, secs));
} else {
let minutes = mins.unsigned_abs();
let seconds = secs.unsigned_abs();
interval.push_str(&format!("{:+}:{:02}:{:02}", hours, minutes, seconds));
}
if micros != 0 {
let microseconds = format!(".{:06}", micros.unsigned_abs());
interval.push_str(µseconds);
}
interval
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_year_month() {
let interval = IntervalYearMonth::new(1);
assert_eq!(interval.months, 1);
}
#[test]
fn test_from_date_time() {
let interval = IntervalDayTime::new(1, 2);
assert_eq!(interval.days, 1);
assert_eq!(interval.milliseconds, 2);
}
#[test]
fn test_from_month_day_nano() {
let interval = IntervalMonthDayNano::new(1, 2, 3);
assert_eq!(interval.months, 1);
assert_eq!(interval.days, 2);
assert_eq!(interval.nanoseconds, 3);
}
#[test]
fn test_interval_i128_convert() {
let test_interval_eq = |month, day, nano| {
let interval = IntervalMonthDayNano::new(month, day, nano);
let interval_i128 = interval.to_i128();
let interval2 = IntervalMonthDayNano::from_i128(interval_i128);
assert_eq!(interval, interval2);
};
test_interval_eq(1, 2, 3);
test_interval_eq(1, -2, 3);
test_interval_eq(1, -2, -3);
test_interval_eq(-1, -2, -3);
test_interval_eq(i32::MAX, i32::MAX, i64::MAX);
test_interval_eq(i32::MIN, i32::MAX, i64::MAX);
test_interval_eq(i32::MAX, i32::MIN, i64::MAX);
test_interval_eq(i32::MAX, i32::MAX, i64::MIN);
test_interval_eq(i32::MIN, i32::MIN, i64::MAX);
test_interval_eq(i32::MAX, i32::MIN, i64::MIN);
test_interval_eq(i32::MIN, i32::MAX, i64::MIN);
test_interval_eq(i32::MIN, i32::MIN, i64::MIN);
let interval = IntervalMonthDayNano::from_i128(1);
assert_eq!(interval, IntervalMonthDayNano::new(0, 0, 1));
assert_eq!(1, IntervalMonthDayNano::new(0, 0, 1).to_i128());
}
#[test]
fn test_interval_i64_convert() {
let interval = IntervalDayTime::from_i64(1);
assert_eq!(interval, IntervalDayTime::new(0, 1));
assert_eq!(1, IntervalDayTime::new(0, 1).to_i64());
}
#[test]
fn test_convert_interval_format() {
let interval = IntervalMonthDayNano {
months: 14,
days: 160,
nanoseconds: 1000000,
};
let interval_format = IntervalFormat::from(interval);
assert_eq!(interval_format.years, 1);
assert_eq!(interval_format.months, 2);
assert_eq!(interval_format.days, 160);
assert_eq!(interval_format.hours, 0);
assert_eq!(interval_format.minutes, 0);
assert_eq!(interval_format.seconds, 0);
assert_eq!(interval_format.microseconds, 1000);
}
#[test]
fn test_to_iso8601_string() {
let interval = IntervalMonthDayNano::new(0, 0, 0);
assert_eq!(interval.to_iso8601_string(), "PT0S");
let interval = IntervalMonthDayNano::new(1, 1, 1);
assert_eq!(interval.to_iso8601_string(), "P0Y1M1DT0H0M0S");
let interval = IntervalMonthDayNano::new(14, 31, 10000000000);
assert_eq!(interval.to_iso8601_string(), "P1Y2M31DT0H0M10S");
let interval = IntervalMonthDayNano::new(14, 31, 23210200000000);
assert_eq!(interval.to_iso8601_string(), "P1Y2M31DT6H26M50.2S");
}
#[test]
fn test_to_postgres_string() {
let interval = IntervalMonthDayNano::new(0, 0, 0);
assert_eq!(
IntervalFormat::from(interval).to_postgres_string(),
"00:00:00"
);
let interval = IntervalMonthDayNano::new(23, 100, 23210200000000);
assert_eq!(
IntervalFormat::from(interval).to_postgres_string(),
"1 year 11 mons 100 days 06:26:50.200000"
);
}
#[test]
fn test_to_sql_standard_string() {
let interval = IntervalMonthDayNano::new(0, 0, 0);
assert_eq!(IntervalFormat::from(interval).to_sql_standard_string(), "0");
let interval = IntervalMonthDayNano::new(23, 100, 23210200000000);
assert_eq!(
IntervalFormat::from(interval).to_sql_standard_string(),
"+1-11 +100 +6:26:50.200000"
);
let interval = IntervalMonthDayNano::new(0, 0, 23210200000000);
assert_eq!(
IntervalFormat::from(interval).to_sql_standard_string(),
"6:26:50.200000"
);
}
#[test]
fn test_from_arrow_interval_unit() {
let unit = ArrowIntervalUnit::YearMonth;
assert_eq!(IntervalUnit::from(unit), IntervalUnit::YearMonth);
let unit = ArrowIntervalUnit::DayTime;
assert_eq!(IntervalUnit::from(unit), IntervalUnit::DayTime);
let unit = ArrowIntervalUnit::MonthDayNano;
assert_eq!(IntervalUnit::from(unit), IntervalUnit::MonthDayNano);
}
}