const MICROSECOND_THRESHOLD: u64 = 10_000_000_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimestampUnit {
Millisecond,
Microsecond,
}
impl TimestampUnit {
#[inline]
pub fn to_microseconds(self, raw: i64) -> i64 {
match self {
TimestampUnit::Millisecond => raw * 1_000,
TimestampUnit::Microsecond => raw,
}
}
}
pub fn normalize_timestamp(raw_timestamp: u64) -> i64 {
if raw_timestamp < MICROSECOND_THRESHOLD {
(raw_timestamp * 1_000) as i64
} else {
raw_timestamp as i64
}
}
pub fn validate_timestamp(timestamp: i64) -> bool {
const MIN_TIMESTAMP: i64 = 946_684_800_000_000; const MAX_TIMESTAMP: i64 = 2_051_222_400_000_000;
(MIN_TIMESTAMP..=MAX_TIMESTAMP).contains(×tamp)
}
pub fn create_aggtrade_with_normalized_timestamp(
ref_id: i64,
price: crate::FixedPoint,
volume: crate::FixedPoint,
first_sub_id: i64,
last_sub_id: i64,
raw_timestamp: u64,
is_buyer_maker: bool,
) -> crate::trade::Tick {
use crate::trade::Tick;
Tick {
ref_id,
price,
volume,
first_sub_id,
last_sub_id,
timestamp: normalize_timestamp(raw_timestamp),
is_buyer_maker,
is_best_match: None, best_bid: None,
best_ask: None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalize_13_digit_milliseconds() {
let millis = 1609459200000u64;
let expected = 1609459200000000i64;
assert_eq!(normalize_timestamp(millis), expected);
}
#[test]
fn test_normalize_16_digit_microseconds() {
let micros = 1609459200000000u64;
let expected = 1609459200000000i64;
assert_eq!(normalize_timestamp(micros), expected);
}
#[test]
fn test_threshold_boundary() {
let threshold_minus_one = MICROSECOND_THRESHOLD - 1;
let threshold = MICROSECOND_THRESHOLD;
assert_eq!(
normalize_timestamp(threshold_minus_one),
(threshold_minus_one * 1000) as i64
);
assert_eq!(normalize_timestamp(threshold), threshold as i64);
}
#[test]
fn test_validate_timestamp() {
assert!(validate_timestamp(1_704_067_200_000_000));
assert!(validate_timestamp(1_041_379_200_000_000));
assert!(validate_timestamp(946_684_800_000_000));
assert!(validate_timestamp(2_019_686_400_000_000));
assert!(!validate_timestamp(915_148_800_000_000));
assert!(!validate_timestamp(1_000_000_000_000));
assert!(!validate_timestamp(2_524_608_000_000_000)); }
#[test]
fn test_validate_timestamp_negative() {
assert!(!validate_timestamp(-1));
assert!(!validate_timestamp(i64::MIN));
}
#[test]
fn test_validate_timestamp_zero() {
assert!(!validate_timestamp(0));
}
#[test]
fn test_validate_timestamp_exact_boundaries() {
assert!(validate_timestamp(946_684_800_000_000));
assert!(!validate_timestamp(946_684_800_000_000 - 1));
assert!(validate_timestamp(2_051_222_400_000_000));
assert!(!validate_timestamp(2_051_222_400_000_000 + 1));
}
#[test]
fn test_normalize_timestamp_zero() {
assert_eq!(normalize_timestamp(0), 0);
}
#[test]
fn test_timestamp_unit_millisecond_conversion() {
assert_eq!(
TimestampUnit::Millisecond.to_microseconds(1609459200000),
1609459200000000
);
}
#[test]
fn test_timestamp_unit_microsecond_passthrough() {
assert_eq!(
TimestampUnit::Microsecond.to_microseconds(1609459200000000),
1609459200000000
);
}
#[test]
fn test_create_aggtrade_normalizes_timestamp() {
use crate::FixedPoint;
let trade = create_aggtrade_with_normalized_timestamp(
1,
FixedPoint::from_str("100.0").unwrap(),
FixedPoint::from_str("1.0").unwrap(),
10,
10,
1609459200000, false,
);
assert_eq!(trade.timestamp, 1609459200000000); }
}