use crate::ffi;
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct HighPrecisionDatetime(pub(crate) ffi::blpapi_HighPrecisionDatetime_t);
impl HighPrecisionDatetime {
#[doc(hidden)]
#[inline(always)]
pub const fn from_raw(raw: ffi::blpapi_HighPrecisionDatetime_t) -> Self {
Self(raw)
}
#[inline(always)]
pub fn raw(&self) -> &ffi::blpapi_HighPrecisionDatetime_t {
&self.0
}
#[inline(always)]
pub fn to_micros(&self) -> i64 {
let days = days_from_ymd(self.0.year as i32, self.0.month as u32, self.0.day as u32);
let us = self.to_time_micros();
days * 86_400_000_000 + us - self.offset_micros()
}
#[inline(always)]
pub fn to_time_micros(&self) -> i64 {
(self.0.hours as i64) * 3_600_000_000
+ (self.0.minutes as i64) * 60_000_000
+ (self.0.seconds as i64) * 1_000_000
+ (self.0.milliseconds as i64) * 1_000
+ (self.0.picoseconds as i64) / 1_000_000
}
#[inline(always)]
pub fn has_date_parts(&self) -> bool {
(self.0.parts & ffi::BLPAPI_DATETIME_DATE_PART) == ffi::BLPAPI_DATETIME_DATE_PART
}
#[inline(always)]
pub fn has_offset_part(&self) -> bool {
(self.0.parts & ffi::BLPAPI_DATETIME_OFFSET_PART) == ffi::BLPAPI_DATETIME_OFFSET_PART
}
#[inline(always)]
fn offset_micros(&self) -> i64 {
if self.has_offset_part() {
(self.0.offset as i64) * 60_000_000
} else {
0
}
}
#[inline(always)]
fn offset_nanos(&self) -> i64 {
if self.has_offset_part() {
(self.0.offset as i64) * 60_000_000_000
} else {
0
}
}
#[inline(always)]
pub fn to_nanos(&self) -> i64 {
let days = days_from_ymd(self.0.year as i32, self.0.month as u32, self.0.day as u32);
let ns = (self.0.hours as i64) * 3_600_000_000_000
+ (self.0.minutes as i64) * 60_000_000_000
+ (self.0.seconds as i64) * 1_000_000_000
+ (self.0.milliseconds as i64) * 1_000_000
+ (self.0.picoseconds as i64) / 1_000;
days * 86_400_000_000_000 + ns - self.offset_nanos()
}
}
#[inline(always)]
fn days_from_ymd(y: i32, m: u32, d: u32) -> i64 {
let y = y as i64 - (m <= 2) as i64;
let era = y.div_euclid(400);
let yoe = y.rem_euclid(400) as u32;
let doy = (153 * (if m > 2 { m - 3 } else { m + 9 }) + 2) / 5 + d - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
era * 146097 + doe as i64 - 719468
}
#[cfg(test)]
mod tests {
use super::*;
fn make_datetime(
year: u16,
month: u8,
day: u8,
hours: u8,
minutes: u8,
seconds: u8,
milliseconds: u16,
) -> HighPrecisionDatetime {
HighPrecisionDatetime(ffi::blpapi_HighPrecisionDatetime_t {
parts: 0xF7,
hours,
minutes,
seconds,
milliseconds,
month,
day,
year,
offset: 0,
picoseconds: 0,
})
}
#[test]
fn test_datetime_size() {
assert_eq!(
std::mem::size_of::<ffi::blpapi_HighPrecisionDatetime_t>(),
16
);
assert_eq!(std::mem::size_of::<HighPrecisionDatetime>(), 16);
}
#[test]
fn test_unix_epoch() {
let dt = make_datetime(1970, 1, 1, 0, 0, 0, 0);
assert_eq!(dt.to_micros(), 0);
}
#[test]
fn test_datetime_conversion() {
let dt = make_datetime(2024, 6, 15, 14, 30, 45, 123);
assert_eq!(dt.to_micros(), 1718461845123000);
}
#[test]
fn test_y2k() {
let dt = make_datetime(2000, 1, 1, 0, 0, 0, 0);
assert_eq!(dt.to_micros(), 946684800000000);
}
#[test]
fn test_to_nanos() {
let dt = make_datetime(1970, 1, 1, 0, 0, 1, 0);
assert_eq!(dt.to_nanos(), 1_000_000_000); }
#[test]
fn test_to_micros_applies_positive_offset() {
let mut dt = make_datetime(1970, 1, 1, 1, 0, 0, 0);
dt.0.parts |= ffi::BLPAPI_DATETIME_OFFSET_PART;
dt.0.offset = 60;
assert_eq!(dt.to_micros(), 0);
}
#[test]
fn test_to_micros_applies_negative_offset() {
let mut dt = make_datetime(1970, 1, 1, 0, 0, 0, 0);
dt.0.parts |= ffi::BLPAPI_DATETIME_OFFSET_PART;
dt.0.offset = -60;
assert_eq!(dt.to_micros(), 3_600_000_000);
}
#[test]
fn test_to_nanos_applies_offset() {
let mut dt = make_datetime(1970, 1, 1, 1, 0, 0, 0);
dt.0.parts |= ffi::BLPAPI_DATETIME_OFFSET_PART;
dt.0.offset = 60;
assert_eq!(dt.to_nanos(), 0);
}
}