use super::civil::{days_in_month, ymd_to_days};
use super::components::{components_to_timestamp, extract_components};
use super::delta::TimeDelta;
use crate::time_units::TimeUnit;
pub fn add_delta(timestamp: i64, delta: &TimeDelta, time_unit: TimeUnit) -> i64 {
let delta_nanos = delta.total_nanoseconds();
match time_unit {
TimeUnit::Seconds => timestamp + (delta_nanos / 1_000_000_000) as i64,
TimeUnit::Milliseconds => timestamp + (delta_nanos / 1_000_000) as i64,
TimeUnit::Microseconds => timestamp + (delta_nanos / 1_000) as i64,
TimeUnit::Nanoseconds => timestamp + delta_nanos as i64,
TimeUnit::Days => timestamp + delta.total_seconds() / 86_400,
}
}
pub fn diff_timestamps(lhs: i64, rhs: i64, time_unit: TimeUnit) -> TimeDelta {
let diff = lhs - rhs;
match time_unit {
TimeUnit::Seconds => TimeDelta::seconds(diff),
TimeUnit::Milliseconds => TimeDelta::milliseconds(diff),
TimeUnit::Microseconds => TimeDelta::microseconds(diff),
TimeUnit::Nanoseconds => TimeDelta::nanoseconds(diff),
TimeUnit::Days => TimeDelta::days(diff),
}
}
pub fn add_months(timestamp: i64, months: i64, time_unit: TimeUnit) -> i64 {
let comp = extract_components(timestamp, time_unit);
let total_months = comp.month as i64 + months - 1; let year_offset = if total_months >= 0 {
total_months / 12
} else {
(total_months - 11) / 12
};
let target_year = comp.year + year_offset as i32;
let target_month = ((total_months % 12 + 12) % 12 + 1) as u32;
let max_day = days_in_month(target_year, target_month);
let target_day = comp.day.min(max_day);
let days = ymd_to_days(target_year, target_month, target_day);
let seconds_in_day = comp.hour * 3600 + comp.minute * 60 + comp.second;
let total_seconds = days * 86400 + seconds_in_day as i64;
match time_unit {
TimeUnit::Seconds => total_seconds,
TimeUnit::Milliseconds => {
total_seconds * 1_000 + (comp.nanosecond / 1_000_000) as i64
}
TimeUnit::Microseconds => total_seconds * 1_000_000 + (comp.nanosecond / 1_000) as i64,
TimeUnit::Nanoseconds => total_seconds * 1_000_000_000 + comp.nanosecond as i64,
TimeUnit::Days => days,
}
}
pub fn add_years(timestamp: i64, years: i64, time_unit: TimeUnit) -> i64 {
add_months(timestamp, years * 12, time_unit)
}
pub fn truncate_to_unit(timestamp: i64, from_unit: TimeUnit, to_unit: TimeUnit) -> i64 {
let comp = extract_components(timestamp, from_unit);
let (hour, minute, second, nanosecond) = match to_unit {
TimeUnit::Days => (0, 0, 0, 0),
TimeUnit::Seconds => (comp.hour, comp.minute, comp.second, 0),
TimeUnit::Milliseconds => (comp.hour, comp.minute, comp.second, comp.nanosecond),
TimeUnit::Microseconds => (comp.hour, comp.minute, comp.second, comp.nanosecond),
TimeUnit::Nanoseconds => (comp.hour, comp.minute, comp.second, comp.nanosecond),
};
let truncated_comp = super::components::DateTimeComponents {
year: comp.year,
month: comp.month,
day: comp.day,
hour,
minute,
second,
nanosecond,
};
components_to_timestamp(&truncated_comp, from_unit)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_delta() {
let delta = TimeDelta::days(1);
let result = add_delta(0, &delta, TimeUnit::Seconds);
assert_eq!(result, 86_400);
}
#[test]
fn test_diff_timestamps() {
let delta = diff_timestamps(86_400, 0, TimeUnit::Seconds);
assert_eq!(delta.total_seconds(), 86_400);
}
#[test]
fn test_add_months_simple() {
let ts = ymd_to_days(2000, 1, 15) * 86400;
let result = add_months(ts, 1, TimeUnit::Seconds);
let expected = ymd_to_days(2000, 2, 15) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_add_months_end_of_month() {
let ts = ymd_to_days(2000, 1, 31) * 86400;
let result = add_months(ts, 1, TimeUnit::Seconds);
let expected = ymd_to_days(2000, 2, 29) * 86400;
assert_eq!(result, expected);
let ts = ymd_to_days(2001, 1, 31) * 86400;
let result = add_months(ts, 1, TimeUnit::Seconds);
let expected = ymd_to_days(2001, 2, 28) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_add_months_year_boundary() {
let ts = ymd_to_days(2000, 12, 15) * 86400;
let result = add_months(ts, 1, TimeUnit::Seconds);
let expected = ymd_to_days(2001, 1, 15) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_add_months_negative() {
let ts = ymd_to_days(2000, 3, 15) * 86400;
let result = add_months(ts, -1, TimeUnit::Seconds);
let expected = ymd_to_days(2000, 2, 15) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_add_years() {
let ts = ymd_to_days(2000, 1, 15) * 86400;
let result = add_years(ts, 1, TimeUnit::Seconds);
let expected = ymd_to_days(2001, 1, 15) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_add_years_leap_day() {
let ts = ymd_to_days(2020, 2, 29) * 86400;
let result = add_years(ts, 1, TimeUnit::Seconds);
let expected = ymd_to_days(2021, 2, 28) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_truncate_to_day() {
let ts = ymd_to_days(2000, 1, 15) * 86400 + 12 * 3600 + 34 * 60 + 56;
let result = truncate_to_unit(ts, TimeUnit::Seconds, TimeUnit::Days);
let expected = ymd_to_days(2000, 1, 15) * 86400;
assert_eq!(result, expected);
}
#[test]
fn test_truncate_milliseconds() {
let ts = 1_700_000_123; let result = truncate_to_unit(ts, TimeUnit::Milliseconds, TimeUnit::Seconds);
assert_eq!(result, 1_700_000_000);
}
}