use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
use crate::{
error::{err_column_null, err_type_conv},
ibase, Column, ColumnToVal, FbError, IntoParam, SqlType,
};
const FRACTION_TO_NANOS: u32 = 1e9 as u32 / ibase::ISC_TIME_SECONDS_PRECISION;
pub fn decode_date(date: ibase::ISC_DATE) -> NaiveDate {
let mut nday = date;
nday += 2400001 - 1721119;
let century = (4 * nday - 1) / 146097;
nday = 4 * nday - 1 - 146097 * century;
let mut day = nday / 4;
nday = (4 * day + 3) / 1461;
day = 4 * day + 3 - 1461 * nday;
day = (day + 4) / 4;
let mut month = (5 * day - 3) / 153;
day = 5 * day - 3 - 153 * month;
day = (day + 5) / 5;
let mut year = 100 * century + nday;
if month < 10 {
month += 3;
} else {
month -= 9;
year += 1;
};
chrono::NaiveDate::from_ymd(year, month as u32, day as u32)
}
pub fn encode_date(date: NaiveDate) -> ibase::ISC_DATE {
let day = date.day() as i64;
let mut month = date.month() as i64;
let mut year = date.year() as i64;
if month > 2 {
month -= 3;
} else {
month += 9;
year -= 1;
}
let c = year / 100;
let ya = year - 100 * c;
((146097 * c) as i64 / 4 + (1461 * ya) / 4 + (153 * month + 2) / 5 + day + 1721119 - 2400001)
as ibase::ISC_DATE
}
pub fn decode_time(time: ibase::ISC_TIME) -> NaiveTime {
let mut ntime = time;
let hours = ntime / (3600 * ibase::ISC_TIME_SECONDS_PRECISION);
ntime %= 3600 * ibase::ISC_TIME_SECONDS_PRECISION;
let minutes = ntime / (60 * ibase::ISC_TIME_SECONDS_PRECISION);
ntime %= 60 * ibase::ISC_TIME_SECONDS_PRECISION;
let seconds = ntime / ibase::ISC_TIME_SECONDS_PRECISION;
let fraction = ntime % ibase::ISC_TIME_SECONDS_PRECISION;
chrono::NaiveTime::from_hms_nano(hours, minutes, seconds, fraction * FRACTION_TO_NANOS)
}
pub fn encode_time(time: chrono::NaiveTime) -> ibase::ISC_TIME {
let hours = time.hour();
let minutes = time.minute();
let seconds = time.second();
let fraction = time.nanosecond() / FRACTION_TO_NANOS;
((hours * 60 + minutes) * 60 + seconds) * ibase::ISC_TIME_SECONDS_PRECISION + fraction
}
pub fn decode_timestamp(ts: ibase::ISC_TIMESTAMP) -> NaiveDateTime {
decode_date(ts.timestamp_date).and_time(decode_time(ts.timestamp_time))
}
pub fn encode_timestamp(dt: NaiveDateTime) -> ibase::ISC_TIMESTAMP {
ibase::ISC_TIMESTAMP {
timestamp_date: encode_date(dt.date()),
timestamp_time: encode_time(dt.time()),
}
}
impl IntoParam for NaiveDateTime {
fn into_param(self) -> SqlType {
SqlType::Timestamp(self)
}
}
impl IntoParam for NaiveDate {
fn into_param(self) -> SqlType {
self.and_time(NaiveTime::from_hms(0, 0, 0)).into_param()
}
}
impl IntoParam for NaiveTime {
fn into_param(self) -> SqlType {
chrono::Utc::today().naive_utc().and_time(self).into_param()
}
}
impl ColumnToVal<chrono::NaiveDate> for Column {
fn to_val(self) -> Result<chrono::NaiveDate, FbError> {
match self.value {
SqlType::Timestamp(ts) => Ok(ts.date()),
SqlType::Null => Err(err_column_null("NaiveDate")),
col => err_type_conv(col, "NaiveDate"),
}
}
}
impl ColumnToVal<chrono::NaiveTime> for Column {
fn to_val(self) -> Result<chrono::NaiveTime, FbError> {
match self.value {
SqlType::Timestamp(ts) => Ok(ts.time()),
SqlType::Null => Err(err_column_null("NaiveTime")),
col => err_type_conv(col, "NaiveTime"),
}
}
}
impl ColumnToVal<chrono::NaiveDateTime> for Column {
fn to_val(self) -> Result<chrono::NaiveDateTime, FbError> {
match self.value {
SqlType::Timestamp(ts) => Ok(ts),
SqlType::Null => Err(err_column_null("NaiveDateTime")),
col => err_type_conv(col, "NaiveDateTime"),
}
}
}