bento-kit 0.1.1

A bento box of common Rust utilities: id generation, timing, masking
Documentation
//! Unix timestamps and `chrono`-style formatting.

use chrono::{DateTime, Local, TimeZone, Utc};

/// Current Unix timestamp in milliseconds.
pub fn now_millis() -> i64 {
    Utc::now().timestamp_millis()
}

/// Current Unix timestamp in seconds.
pub fn now_seconds() -> i64 {
    Utc::now().timestamp()
}

/// Current Unix timestamp in microseconds.
pub fn now_micros() -> i64 {
    Utc::now().timestamp_micros()
}

/// Format the current UTC time using a `chrono` format string.
///
/// See [chrono's strftime reference](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
///
/// ```
/// let s = bento_kit::time::format_now_utc("%Y-%m-%d");
/// assert_eq!(s.len(), 10);
/// ```
pub fn format_now_utc(fmt: &str) -> String {
    Utc::now().format(fmt).to_string()
}

/// Format the current local time using a `chrono` format string.
pub fn format_now_local(fmt: &str) -> String {
    Local::now().format(fmt).to_string()
}

/// Format a Unix timestamp (in seconds) as UTC.
///
/// Returns `None` if the timestamp is out of `chrono`'s representable range.
pub fn format_timestamp_utc(ts_seconds: i64, fmt: &str) -> Option<String> {
    Utc.timestamp_opt(ts_seconds, 0)
        .single()
        .map(|dt: DateTime<Utc>| dt.format(fmt).to_string())
}

/// Format a Unix timestamp (in seconds) as local time.
pub fn format_timestamp_local(ts_seconds: i64, fmt: &str) -> Option<String> {
    Local
        .timestamp_opt(ts_seconds, 0)
        .single()
        .map(|dt| dt.format(fmt).to_string())
}

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

    #[test]
    fn now_millis_is_close_to_chrono() {
        let mine = now_millis();
        let chrono_now = Utc::now().timestamp_millis();
        assert!((chrono_now - mine).abs() < 1000);
    }

    #[test]
    fn now_seconds_is_close_to_chrono() {
        let mine = now_seconds();
        let chrono_now = Utc::now().timestamp();
        assert!((chrono_now - mine).abs() <= 1);
    }

    #[test]
    fn format_timestamp_utc_known_value() {
        // 2024-01-01 00:00:00 UTC == 1704067200
        let s = format_timestamp_utc(1_704_067_200, "%Y-%m-%d %H:%M:%S").unwrap();
        assert_eq!(s, "2024-01-01 00:00:00");
    }

    #[test]
    fn format_timestamp_utc_rejects_out_of_range() {
        assert!(format_timestamp_utc(i64::MAX, "%Y").is_none());
    }

    #[test]
    fn format_now_utc_produces_expected_length() {
        let s = format_now_utc("%Y-%m-%dT%H:%M:%SZ");
        assert_eq!(s.len(), 20);
        assert!(s.ends_with('Z'));
    }
}