use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimestampUnit {
Second,
Millisecond,
Microsecond,
Nanosecond,
}
pub fn scale_timestamp_to_ms(value: i64, unit: TimestampUnit) -> Result<i64> {
match unit {
TimestampUnit::Second => value.checked_mul(1_000).ok_or_else(|| {
Error::Other(format!(
"second-precision timestamp {value} overflows the millisecond range"
))
}),
TimestampUnit::Millisecond => Ok(value),
TimestampUnit::Microsecond => Ok(value / 1_000),
TimestampUnit::Nanosecond => Ok(value / 1_000_000),
}
}
pub fn reject_negative_timestamp(row: usize, value: i64) -> Result<()> {
if value < 0 {
return Err(Error::Other(format!(
"row {row}: negative timestamp {value} (pre-1970). The STT temporal \
index stores unsigned ms-since-epoch and cannot represent pre-1970 \
times; filter or re-epoch these rows before building."
)));
}
Ok(())
}
pub fn normalize_timestamp_to_ms(row: usize, value: i64, unit: TimestampUnit) -> Result<u64> {
reject_negative_timestamp(row, value)?;
let ms = scale_timestamp_to_ms(value, unit).map_err(|_| {
Error::Other(format!(
"row {row}: second-precision timestamp {value} overflows the millisecond range"
))
})?;
Ok(ms as u64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scale_matches_units() {
assert_eq!(scale_timestamp_to_ms(5, TimestampUnit::Second).unwrap(), 5000);
assert_eq!(scale_timestamp_to_ms(5, TimestampUnit::Millisecond).unwrap(), 5);
assert_eq!(scale_timestamp_to_ms(5_000, TimestampUnit::Microsecond).unwrap(), 5);
assert_eq!(scale_timestamp_to_ms(5_000_000, TimestampUnit::Nanosecond).unwrap(), 5);
}
#[test]
fn scale_second_overflow_errors() {
assert!(scale_timestamp_to_ms(i64::MAX, TimestampUnit::Second).is_err());
assert!(scale_timestamp_to_ms(i64::MIN, TimestampUnit::Second).is_err());
}
#[test]
fn normalize_rejects_negative() {
assert!(normalize_timestamp_to_ms(0, -1, TimestampUnit::Millisecond).is_err());
assert_eq!(
normalize_timestamp_to_ms(0, 1_000, TimestampUnit::Second).unwrap(),
1_000_000
);
}
#[test]
fn normalize_second_overflow_errors() {
assert!(normalize_timestamp_to_ms(0, i64::MAX, TimestampUnit::Second).is_err());
}
}