use std::time::Duration;
use tokio::time::Instant;
pub fn next_anchor_phase_deadline(anchor: Instant, interval: Duration, now: Instant) -> Instant {
let elapsed_intervals = now.duration_since(anchor).as_nanos() / interval.as_nanos();
let offset_nanos = interval.as_nanos().saturating_mul(elapsed_intervals + 1);
let offset = Duration::new(
u64::try_from(offset_nanos / 1_000_000_000).unwrap_or(u64::MAX),
u32::try_from(offset_nanos % 1_000_000_000).expect("sub-second nanos fit u32"),
);
anchor + offset
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn next_anchor_phase_deadline_stays_exact_past_u64_nanoseconds() {
let anchor = Instant::now();
let interval = Duration::from_millis(1);
let now = anchor + Duration::from_secs(600 * 365 * 24 * 60 * 60);
let next = next_anchor_phase_deadline(anchor, interval, now);
assert!(next > now, "the deadline must stay strictly after now");
assert!(
next - now <= interval,
"the deadline is the first boundary after now, at most one interval away"
);
assert_eq!(
(next - anchor).as_nanos() % interval.as_nanos(),
0,
"the deadline stays on the anchor's phase"
);
}
}