use crate::time::{civil_to_epoch_days, eastern_offset_ms};
#[allow(clippy::cast_possible_wrap)]
#[must_use]
pub fn latency_ns(exchange_ms_of_day: i32, event_date: i32, received_at_ns: u64) -> i64 {
let exchange_epoch_ns = exchange_epoch_ns(exchange_ms_of_day, event_date);
received_at_ns as i64 - exchange_epoch_ns
}
#[allow(clippy::cast_sign_loss)]
fn exchange_epoch_ns(ms_of_day: i32, date_yyyymmdd: i32) -> i64 {
let year = date_yyyymmdd / 10_000;
let month = ((date_yyyymmdd % 10_000) / 100) as u32;
let day = (date_yyyymmdd % 100) as u32;
let days = civil_to_epoch_days(year, month, day);
let midnight_utc_ms = days * 86_400_000;
let approx_utc_ms = midnight_utc_ms + i64::from(ms_of_day) + 5 * 3_600 * 1_000; let offset_ms = eastern_offset_ms(approx_utc_ms as u64);
let exchange_epoch_ms = midnight_utc_ms + i64::from(ms_of_day) - offset_ms;
exchange_epoch_ms * 1_000_000
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn latency_basic_est() {
let days = civil_to_epoch_days(2024, 1, 15);
let midnight_utc_ns = days * 86_400_000 * 1_000_000;
let utc_offset_ns = (14 * 3600 + 30 * 60) as i64 * 1_000_000_000;
let received_at_ns = (midnight_utc_ns + utc_offset_ns) as u64;
let lat = latency_ns(34_200_000, 20240115, received_at_ns);
assert!(
lat.abs() < 1_000_000, "expected ~0 latency, got {lat} ns"
);
}
#[test]
fn latency_basic_edt() {
let days = civil_to_epoch_days(2024, 6, 15);
let midnight_utc_ns = days * 86_400_000 * 1_000_000;
let utc_offset_ns = (13 * 3600 + 30 * 60) as i64 * 1_000_000_000;
let received_at_ns = (midnight_utc_ns + utc_offset_ns) as u64;
let lat = latency_ns(34_200_000, 20240615, received_at_ns);
assert!(lat.abs() < 1_000_000, "expected ~0 latency, got {lat} ns");
}
#[test]
fn latency_positive_when_received_later() {
let days = civil_to_epoch_days(2024, 1, 15);
let midnight_utc_ns = days * 86_400_000 * 1_000_000;
let utc_offset_ns = (14 * 3600 + 30 * 60) as i64 * 1_000_000_000;
let received_at_ns = (midnight_utc_ns + utc_offset_ns) as u64 + 100_000_000;
let lat = latency_ns(34_200_000, 20240115, received_at_ns);
assert!(
(lat - 100_000_000).abs() < 1_000_000,
"expected ~100ms latency, got {lat} ns"
);
}
#[test]
fn latency_negative_when_clock_skewed() {
let days = civil_to_epoch_days(2024, 1, 15);
let midnight_utc_ns = days * 86_400_000 * 1_000_000;
let utc_offset_ns = (14 * 3600 + 30 * 60) as i64 * 1_000_000_000;
let received_at_ns = (midnight_utc_ns + utc_offset_ns) as u64 - 50_000_000;
let lat = latency_ns(34_200_000, 20240115, received_at_ns);
assert!(
(lat + 50_000_000).abs() < 1_000_000,
"expected ~-50ms latency, got {lat} ns"
);
}
}