use vortex_array::extension::datetime::TimeUnit;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_error::vortex_panic;
pub const SECONDS_PER_DAY: i64 = 86_400;
pub struct TimestampParts {
pub days: i64,
pub seconds: i64,
pub subseconds: i64,
}
pub fn split(timestamp: i64, time_unit: TimeUnit) -> VortexResult<TimestampParts> {
let divisor = match time_unit {
TimeUnit::Nanoseconds => 1_000_000_000,
TimeUnit::Microseconds => 1_000_000,
TimeUnit::Milliseconds => 1_000,
TimeUnit::Seconds => 1,
TimeUnit::Days => vortex_bail!("Cannot handle day-level data"),
};
let ticks_per_day = SECONDS_PER_DAY * divisor;
Ok(TimestampParts {
days: timestamp / ticks_per_day,
seconds: (timestamp % ticks_per_day) / divisor,
subseconds: (timestamp % ticks_per_day) % divisor,
})
}
pub fn combine(ts_parts: TimestampParts, time_unit: TimeUnit) -> i64 {
let divisor = match time_unit {
TimeUnit::Nanoseconds => 1_000_000_000,
TimeUnit::Microseconds => 1_000_000,
TimeUnit::Milliseconds => 1_000,
TimeUnit::Seconds => 1,
TimeUnit::Days => vortex_panic!("Cannot handle day-level data"),
};
ts_parts.days * SECONDS_PER_DAY * divisor + ts_parts.seconds * divisor + ts_parts.subseconds
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_seconds() {
let ts = SECONDS_PER_DAY + 3723; let parts = split(ts, TimeUnit::Seconds).unwrap();
assert_eq!(parts.days, 1);
assert_eq!(parts.seconds, 3723);
assert_eq!(parts.subseconds, 0);
}
#[test]
fn test_split_milliseconds() {
let ts = (SECONDS_PER_DAY + 3723) * 1000 + 456;
let parts = split(ts, TimeUnit::Milliseconds).unwrap();
assert_eq!(parts.days, 1);
assert_eq!(parts.seconds, 3723);
assert_eq!(parts.subseconds, 456);
}
#[test]
fn test_split_microseconds() {
let ts = (SECONDS_PER_DAY + 3723) * 1_000_000 + 456789;
let parts = split(ts, TimeUnit::Microseconds).unwrap();
assert_eq!(parts.days, 1);
assert_eq!(parts.seconds, 3723);
assert_eq!(parts.subseconds, 456789);
}
#[test]
fn test_split_nanoseconds() {
let ts = (SECONDS_PER_DAY + 3723) * 1_000_000_000 + 456789123;
let parts = split(ts, TimeUnit::Nanoseconds).unwrap();
assert_eq!(parts.days, 1);
assert_eq!(parts.seconds, 3723);
assert_eq!(parts.subseconds, 456789123);
}
#[test]
fn test_split_epoch() {
let ts = 0;
for unit in [
TimeUnit::Seconds,
TimeUnit::Milliseconds,
TimeUnit::Microseconds,
TimeUnit::Nanoseconds,
] {
let parts = split(ts, unit).unwrap();
assert_eq!(parts.days, 0);
assert_eq!(parts.seconds, 0);
assert_eq!(parts.subseconds, 0);
}
}
#[test]
fn test_split_days_error() {
assert!(split(1, TimeUnit::Days).is_err());
}
#[test]
fn test_combine_seconds() {
let parts = TimestampParts {
days: 1,
seconds: 3723, subseconds: 0,
};
let ts = combine(parts, TimeUnit::Seconds);
assert_eq!(ts, SECONDS_PER_DAY + 3723);
}
#[test]
fn test_combine_milliseconds() {
let parts = TimestampParts {
days: 1,
seconds: 3723,
subseconds: 456,
};
let ts = combine(parts, TimeUnit::Milliseconds);
assert_eq!(ts, (SECONDS_PER_DAY + 3723) * 1000 + 456);
}
#[test]
fn test_combine_microseconds() {
let parts = TimestampParts {
days: 1,
seconds: 3723,
subseconds: 456789,
};
let ts = combine(parts, TimeUnit::Microseconds);
assert_eq!(ts, (SECONDS_PER_DAY + 3723) * 1_000_000 + 456789);
}
#[test]
fn test_combine_nanoseconds() {
let parts = TimestampParts {
days: 1,
seconds: 3723,
subseconds: 456789123,
};
let ts = combine(parts, TimeUnit::Nanoseconds);
assert_eq!(ts, (SECONDS_PER_DAY + 3723) * 1_000_000_000 + 456789123);
}
#[test]
#[should_panic(expected = "Cannot handle day-level data")]
fn test_combine_days_error() {
let parts = TimestampParts {
days: 1,
seconds: 0,
subseconds: 0,
};
combine(parts, TimeUnit::Days);
}
}