use std::fmt;
use jiff::civil::Date;
use jiff::tz::TimeZone;
use jiff::{Timestamp, ToSpan, Zoned};
pub const RETENTION_DAYS: i32 = 90;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Window {
Today,
Last24Hours,
Last7Days,
Last30Days,
Last90Days,
MonthToDate,
AllTime,
}
impl Window {
pub const ALL: [Self; 7] = [
Self::Today,
Self::Last24Hours,
Self::Last7Days,
Self::Last30Days,
Self::MonthToDate,
Self::Last90Days,
Self::AllTime,
];
#[must_use]
pub fn slug(self) -> &'static str {
match self {
Self::Today => "today",
Self::Last24Hours => "last_24h",
Self::Last7Days => "last_7d",
Self::Last30Days => "last_30d",
Self::Last90Days => "last_90d",
Self::MonthToDate => "month_to_date",
Self::AllTime => "all_time",
}
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Today => "Today",
Self::Last24Hours => "Last 24 hours",
Self::Last7Days => "Last 7 days",
Self::Last30Days => "Last 30 days",
Self::Last90Days => "Last 90 days",
Self::MonthToDate => "Month to date",
Self::AllTime => "All time",
}
}
#[must_use]
pub fn is_calendar(self) -> bool {
matches!(self, Self::Today | Self::MonthToDate)
}
#[must_use]
pub fn from_slug(slug: &str) -> Option<Self> {
Self::ALL.into_iter().find(|w| w.slug() == slug)
}
#[must_use]
pub fn resolve(self, now: &Zoned) -> Span {
let end = now.timestamp();
let zone = now.time_zone().clone();
let (start, reckoned_in) = match self {
Self::Today => (Some(start_of_day(now)), Some(zone)),
Self::MonthToDate => (Some(start_of_month(now)), Some(zone)),
Self::Last24Hours => (Some(rolling_back(end, 24)), None),
Self::Last7Days => (Some(rolling_back(end, 7 * 24)), None),
Self::Last30Days => (Some(rolling_back(end, 30 * 24)), None),
Self::Last90Days => (Some(rolling_back(end, 90 * 24)), None),
Self::AllTime => (None, None),
};
Span {
window: self,
start,
end,
reckoned_in,
horizon: rolling_back(end, RETENTION_DAYS * 24),
}
}
}
impl fmt::Display for Window {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.label())
}
}
fn rolling_back(from: Timestamp, hours: i32) -> Timestamp {
from.checked_sub(hours.hours()).unwrap_or(Timestamp::MIN)
}
fn start_of_day(now: &Zoned) -> Timestamp {
now.start_of_day()
.map_or_else(|_| now.timestamp(), |zoned| zoned.timestamp())
}
fn start_of_month(now: &Zoned) -> Timestamp {
let first = Date::new(now.year(), now.month(), 1).unwrap_or_else(|_| now.date());
first
.to_zoned(now.time_zone().clone())
.map_or_else(|_| start_of_day(now), |zoned| zoned.timestamp())
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span {
pub window: Window,
pub start: Option<Timestamp>,
pub end: Timestamp,
pub reckoned_in: Option<TimeZone>,
pub horizon: Timestamp,
}
impl Span {
#[must_use]
pub fn contains(&self, at: Timestamp) -> bool {
self.start.is_none_or(|start| at >= start) && at < self.end
}
#[must_use]
pub fn truncated_by_retention(&self) -> bool {
match self.window {
Window::AllTime => false,
_ => self.start.is_some_and(|start| start < self.horizon),
}
}
#[must_use]
pub fn zone_name(&self) -> Option<&str> {
self.reckoned_in.as_ref().and_then(TimeZone::iana_name)
}
#[must_use]
pub fn duration_secs(&self) -> Option<i64> {
Some(self.end.as_second() - self.start?.as_second())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn now() -> Zoned {
"2026-09-16T13:10:00+03:00[Europe/Tallinn]"
.parse()
.expect("valid zoned timestamp")
}
#[test]
fn rolling_windows_go_back_exactly_that_far() {
let span = Window::Last7Days.resolve(&now());
assert_eq!(span.duration_secs(), Some(7 * 24 * 3_600));
assert!(!span.window.is_calendar());
}
#[test]
fn a_calendar_month_starts_at_local_midnight_on_the_first() {
let span = Window::MonthToDate.resolve(&now());
let start = span.start.expect("month to date has a start");
assert_eq!(
start.to_string(),
"2026-08-31T21:00:00Z",
"local midnight on 1 September in UTC+3 is 21:00 the previous day in UTC"
);
assert_eq!(span.zone_name(), Some("Europe/Tallinn"));
}
#[test]
fn a_calendar_window_reckoned_in_two_zones_covers_different_instants() {
let tallinn = Window::Today.resolve(&now());
let honolulu: Zoned = "2026-09-16T00:10:00-10:00[Pacific/Honolulu]"
.parse()
.expect("valid zoned timestamp");
let pacific = Window::Today.resolve(&honolulu);
assert_ne!(tallinn.start, pacific.start);
assert_eq!(pacific.zone_name(), Some("Pacific/Honolulu"));
}
#[test]
fn a_rolling_window_records_that_no_zone_was_involved() {
let span = Window::Last7Days.resolve(&now());
assert!(span.reckoned_in.is_none());
assert!(span.zone_name().is_none());
}
#[test]
fn windows_are_half_open_so_consecutive_periods_do_not_double_count() {
let span = Window::Last7Days.resolve(&now());
let start = span.start.expect("rolling window has a start");
assert!(span.contains(start), "the start is inside");
assert!(!span.contains(span.end), "the end is not");
assert!(!span.contains(start - 1.second()));
}
#[test]
fn nothing_reaches_past_retention_except_the_window_that_says_so() {
let at = now();
assert!(!Window::Last30Days.resolve(&at).truncated_by_retention());
assert!(
!Window::Last90Days.resolve(&at).truncated_by_retention(),
"ninety days is exactly what is kept, so it is complete"
);
assert!(
!Window::AllTime.resolve(&at).truncated_by_retention(),
"all time means all that is kept, and claiming otherwise would warn on every page load"
);
}
#[test]
fn a_month_to_date_window_is_truncated_only_once_it_outruns_retention() {
let span = Window::MonthToDate.resolve(&now());
assert!(!span.truncated_by_retention());
let stretched = Span {
start: Some(span.horizon - 1.second()),
..span
};
assert!(stretched.truncated_by_retention());
}
#[test]
fn a_rolling_week_stays_168_hours_across_a_daylight_saving_change() {
let across_dst: Zoned = "2026-10-26T12:00:00+02:00[Europe/Tallinn]"
.parse()
.expect("valid zoned timestamp");
assert_eq!(
Window::Last7Days.resolve(&across_dst).duration_secs(),
Some(7 * 24 * 3_600)
);
}
#[test]
fn a_calendar_day_does_stretch_across_a_daylight_saving_change() {
let ordinary: Zoned = "2026-10-20T23:00:00+03:00[Europe/Tallinn]"
.parse()
.expect("valid zoned timestamp");
let transition: Zoned = "2026-10-25T23:00:00+02:00[Europe/Tallinn]"
.parse()
.expect("valid zoned timestamp");
assert_eq!(
Window::Today.resolve(&ordinary).duration_secs(),
Some(23 * 3_600)
);
assert_eq!(
Window::Today.resolve(&transition).duration_secs(),
Some(24 * 3_600),
"the clocks went back, so an extra hour has passed by the same reading"
);
}
#[test]
fn slugs_round_trip_and_are_unique() {
let mut seen = Vec::new();
for window in Window::ALL {
assert_eq!(Window::from_slug(window.slug()), Some(window));
assert!(
!seen.contains(&window.slug()),
"duplicate {}",
window.slug()
);
seen.push(window.slug());
}
assert_eq!(Window::from_slug("fortnight"), None);
}
#[test]
fn only_calendar_windows_need_a_zone() {
for window in Window::ALL {
let span = window.resolve(&now());
assert_eq!(
span.reckoned_in.is_some(),
window.is_calendar(),
"{window} disagrees about whether it used a zone"
);
}
}
#[test]
fn an_open_window_contains_everything_up_to_now() {
let span = Window::AllTime.resolve(&now());
assert!(span.start.is_none());
assert!(span.duration_secs().is_none());
assert!(span.contains(Timestamp::MIN));
}
}