use crate::time_units::TimeUnit;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConversionError {
Overflow,
PrecisionLoss { from: String, to: String },
}
impl std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConversionError::Overflow => write!(f, "Timestamp conversion overflow"),
ConversionError::PrecisionLoss { from, to } => {
write!(f, "Precision loss converting from {} to {}", from, to)
}
}
}
}
impl std::error::Error for ConversionError {}
pub fn convert_timestamp(
value: i64,
from_unit: TimeUnit,
to_unit: TimeUnit,
) -> Result<i64, ConversionError> {
if from_unit == to_unit {
return Ok(value);
}
let nanos = match from_unit {
TimeUnit::Seconds => value
.checked_mul(1_000_000_000)
.ok_or(ConversionError::Overflow)?,
TimeUnit::Milliseconds => value
.checked_mul(1_000_000)
.ok_or(ConversionError::Overflow)?,
TimeUnit::Microseconds => value.checked_mul(1_000).ok_or(ConversionError::Overflow)?,
TimeUnit::Nanoseconds => value,
TimeUnit::Days => value
.checked_mul(86_400_000_000_000)
.ok_or(ConversionError::Overflow)?,
};
let result = match to_unit {
TimeUnit::Seconds => nanos
.checked_div(1_000_000_000)
.ok_or(ConversionError::Overflow)?,
TimeUnit::Milliseconds => nanos
.checked_div(1_000_000)
.ok_or(ConversionError::Overflow)?,
TimeUnit::Microseconds => nanos.checked_div(1_000).ok_or(ConversionError::Overflow)?,
TimeUnit::Nanoseconds => nanos,
TimeUnit::Days => nanos
.checked_div(86_400_000_000_000)
.ok_or(ConversionError::Overflow)?,
};
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_convert_same_unit() {
let result = convert_timestamp(1000, TimeUnit::Seconds, TimeUnit::Seconds).unwrap();
assert_eq!(result, 1000);
}
#[test]
fn test_convert_seconds_to_millis() {
let result = convert_timestamp(1, TimeUnit::Seconds, TimeUnit::Milliseconds).unwrap();
assert_eq!(result, 1_000);
}
#[test]
fn test_convert_millis_to_seconds() {
let result = convert_timestamp(1_000, TimeUnit::Milliseconds, TimeUnit::Seconds).unwrap();
assert_eq!(result, 1);
}
#[test]
fn test_convert_seconds_to_nanos() {
let result = convert_timestamp(1, TimeUnit::Seconds, TimeUnit::Nanoseconds).unwrap();
assert_eq!(result, 1_000_000_000);
}
#[test]
fn test_convert_nanos_to_seconds() {
let result =
convert_timestamp(1_000_000_000, TimeUnit::Nanoseconds, TimeUnit::Seconds).unwrap();
assert_eq!(result, 1);
}
#[test]
fn test_convert_days_to_seconds() {
let result = convert_timestamp(1, TimeUnit::Days, TimeUnit::Seconds).unwrap();
assert_eq!(result, 86_400);
}
#[test]
fn test_convert_seconds_to_days() {
let result = convert_timestamp(86_400, TimeUnit::Seconds, TimeUnit::Days).unwrap();
assert_eq!(result, 1);
}
#[test]
fn test_convert_precision_loss() {
let result = convert_timestamp(1_500, TimeUnit::Milliseconds, TimeUnit::Seconds).unwrap();
assert_eq!(result, 1); }
#[test]
fn test_convert_all_pairs() {
let units = vec![
TimeUnit::Days,
TimeUnit::Seconds,
TimeUnit::Milliseconds,
TimeUnit::Microseconds,
TimeUnit::Nanoseconds,
];
for from_unit in &units {
for to_unit in &units {
let value = match from_unit {
TimeUnit::Days => 1,
TimeUnit::Seconds => 86_400,
TimeUnit::Milliseconds => 86_400_000,
TimeUnit::Microseconds => 86_400_000_000,
TimeUnit::Nanoseconds => 86_400_000_000_000,
};
let result = convert_timestamp(value, *from_unit, *to_unit);
assert!(result.is_ok(), "Failed to convert from {:?} to {:?}", from_unit, to_unit);
}
}
}
}