tiny-counter 0.1.0

Track event counts across time windows with fixed memory and fast queries
Documentation
use chrono::{DateTime, Datelike, Timelike, Utc};

/// Utilities for synchronized time alignment across counters.
///
/// Returns January 1st at 00:00:00 UTC of the current year.
///
/// Used to align all event counters to the same starting instant,
/// ensuring buckets rotate simultaneously across application restarts
/// and different events share the same time alignment.
pub fn synchronized_start(now: DateTime<Utc>) -> DateTime<Utc> {
    now.with_month(1)
        .unwrap()
        .with_day(1)
        .unwrap()
        .with_hour(0)
        .unwrap()
        .with_minute(0)
        .unwrap()
        .with_second(0)
        .unwrap()
        .with_nanosecond(0)
        .unwrap()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_synchronized_start_returns_jan_1() {
        let now = Utc::now();
        let start = synchronized_start(now);
        assert_eq!(start.month(), 1);
        assert_eq!(start.day(), 1);
    }

    #[test]
    fn test_synchronized_start_returns_midnight() {
        let now = Utc::now();
        let start = synchronized_start(now);
        assert_eq!(start.hour(), 0);
        assert_eq!(start.minute(), 0);
        assert_eq!(start.second(), 0);
        assert_eq!(start.nanosecond(), 0);
    }

    #[test]
    fn test_synchronized_start_returns_current_year() {
        let now = Utc::now();
        let start = synchronized_start(now);
        assert_eq!(start.year(), now.year());
    }

    #[test]
    fn test_synchronized_start_is_idempotent() {
        let now = Utc::now();
        let start1 = synchronized_start(now);
        let start2 = synchronized_start(now);

        // Both calls in the same year should return the same value
        assert_eq!(start1, start2);
    }

    #[test]
    fn test_synchronized_start_is_in_past_or_now() {
        let now = Utc::now();
        let start = synchronized_start(now);
        // Start should be before or equal to now
        assert!(start <= now);
    }
}