use crate::{Clock, DateTime, Utc};
pub struct FrozenClock {
datetime: DateTime<Utc>,
}
impl FrozenClock {
const DEFAULT_DATETIME: &'static str = "2025-05-23T10:13:00-07:00";
pub fn default_datetime() -> DateTime<Utc> {
DateTime::parse_from_rfc3339(Self::DEFAULT_DATETIME.as_ref())
.expect(&format!("invalid default date: {}", Self::DEFAULT_DATETIME))
.with_timezone(&Utc)
}
pub fn new(datetime: DateTime<Utc>) -> Self {
FrozenClock { datetime }
}
pub fn with_string(datetime: impl AsRef<str>) -> Self {
let datetime = DateTime::parse_from_rfc3339(datetime.as_ref())
.expect("invalid date supplied")
.with_timezone(&Utc);
Self::new(datetime)
}
}
impl Clock for FrozenClock {
fn now(&self) -> DateTime<Utc> {
self.datetime
}
}
impl Default for FrozenClock {
fn default() -> Self {
Self::new(Self::default_datetime())
}
}
#[cfg(test)]
mod tests {
use crate::{Clock, SystemClock};
use super::FrozenClock;
use chrono::{Datelike, Timelike};
#[test]
fn it_parses_a_datetime_string() {
let s = "2025-12-25T12:01:02-08:00";
let dt = FrozenClock::with_string(s).now();
assert_eq!(dt.month(), 12);
assert_eq!(dt.day(), 25);
assert_eq!(dt.year(), 2025);
assert_eq!(dt.hour(), 20);
assert_eq!(dt.minute(), 1);
assert_eq!(dt.second(), 2);
}
#[test]
#[should_panic]
fn it_panics_if_there_is_an_invalid_string() {
let s = "2025-12-32T12:01:02-08:00";
let _ = FrozenClock::with_string(s).now();
}
#[test]
fn it_returns_a_default_date_in_the_past() {
let now = SystemClock::default().now();
let dt = FrozenClock::default().now();
assert!(dt < now);
}
}