use ralph::timeutil;
fn next_distinct_timestamp(after: &str) -> String {
for _ in 0..10_000 {
let candidate = timeutil::now_utc_rfc3339().unwrap();
if candidate != after {
return candidate;
}
std::hint::spin_loop();
}
panic!("failed to observe a distinct RFC3339 timestamp");
}
#[test]
fn test_now_utc_rfc3339_success() {
let result = timeutil::now_utc_rfc3339();
assert!(result.is_ok());
let timestamp = result.unwrap();
assert!(!timestamp.is_empty());
assert!(timestamp.contains('T'));
assert!(timestamp.contains('Z'));
}
#[test]
fn test_now_utc_rfc3339_format() {
let result = timeutil::now_utc_rfc3339();
assert!(result.is_ok());
let timestamp = result.unwrap();
assert_eq!(timestamp.len(), 30);
assert!(timestamp.contains('T'));
assert!(timestamp.ends_with('Z'));
assert!(timestamp.contains('.'));
let date_part = ×tamp[0..10];
assert_eq!(&date_part[4..5], "-");
assert_eq!(&date_part[7..8], "-");
let time_part = ×tamp[11..19];
assert_eq!(&time_part[2..3], ":");
assert_eq!(&time_part[5..6], ":");
}
#[test]
fn test_now_utc_rfc3339_monotonic() {
let timestamp1 = timeutil::now_utc_rfc3339().unwrap();
let timestamp2 = next_distinct_timestamp(×tamp1);
assert!(timestamp2 >= timestamp1);
}
#[test]
fn test_now_utc_rfc3339_year_range() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let year_str = ×tamp[0..4];
let year: i32 = year_str.parse().unwrap();
assert!(year >= 2020);
assert!(year <= 2030);
}
#[test]
fn test_now_utc_rfc3339_month_range() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let month_str = ×tamp[5..7];
let month: u32 = month_str.parse().unwrap();
assert!(month >= 1);
assert!(month <= 12);
}
#[test]
fn test_now_utc_rfc3339_day_range() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let day_str = ×tamp[8..10];
let day: u32 = day_str.parse().unwrap();
assert!(day >= 1);
assert!(day <= 31);
}
#[test]
fn test_now_utc_rfc3339_hour_range() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let hour_str = ×tamp[11..13];
let hour: u32 = hour_str.parse().unwrap();
assert!(hour <= 23);
}
#[test]
fn test_now_utc_rfc3339_minute_range() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let minute_str = ×tamp[14..16];
let minute: u32 = minute_str.parse().unwrap();
assert!(minute <= 59);
}
#[test]
fn test_now_utc_rfc3339_second_range() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let second_str = ×tamp[17..19];
let second: u32 = second_str.parse().unwrap();
assert!(second <= 59);
}
#[test]
fn test_now_utc_rfc3339_or_fallback_success() {
let timestamp = timeutil::now_utc_rfc3339_or_fallback();
assert!(!timestamp.is_empty());
assert!(timestamp.contains('T'));
assert!(timestamp.contains('Z'));
}
#[test]
fn test_fallback_constant() {
assert_eq!(timeutil::FALLBACK_RFC3339, "1970-01-01T00:00:00.000000000Z");
}
#[test]
fn test_fallback_constant_format() {
let fallback = timeutil::FALLBACK_RFC3339;
assert_eq!(fallback.len(), 30);
assert_eq!(&fallback[10..11], "T");
assert_eq!(&fallback[19..20], ".");
assert_eq!(&fallback[29..30], "Z");
}
#[test]
fn test_now_utc_rfc3339_idempotent() {
let timestamp1 = timeutil::now_utc_rfc3339().unwrap();
let timestamp2 = next_distinct_timestamp(×tamp1);
assert_eq!(timestamp1.len(), timestamp2.len());
}
#[test]
fn test_now_utc_rfc3339_only_zulu() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
assert!(timestamp.ends_with('Z'));
assert!(!timestamp.contains('+'));
assert!(!timestamp.contains("-00:00"));
assert!(!timestamp.contains("+00:00"));
}
#[test]
fn test_now_utc_rfc3339_has_fractional_seconds() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
assert!(timestamp.contains('.'));
}
#[test]
fn test_now_utc_rfc3339_ascii_only() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
assert!(timestamp.is_ascii());
}
#[test]
fn test_now_utc_rfc3339_parseable() {
let timestamp = timeutil::now_utc_rfc3339().unwrap();
let parsed = timeutil::parse_rfc3339(×tamp);
assert!(parsed.is_ok());
}
#[test]
fn test_now_utc_rfc3339_or_fallback_never_empty() {
let timestamp = timeutil::now_utc_rfc3339_or_fallback();
assert!(!timestamp.is_empty());
assert_eq!(timestamp.len(), 30);
}
#[test]
fn test_now_utc_rfc3339_consistent_length() {
let timestamps: Vec<_> = (0..5)
.map(|_| timeutil::now_utc_rfc3339().unwrap())
.collect();
let first_len = timestamps[0].len();
for ts in ×tamps {
assert_eq!(ts.len(), first_len);
}
}
#[test]
fn test_fallback_timestamp_components() {
let fallback = timeutil::FALLBACK_RFC3339;
assert_eq!(&fallback[0..4], "1970");
assert_eq!(&fallback[5..7], "01");
assert_eq!(&fallback[8..10], "01");
assert_eq!(&fallback[10..11], "T");
assert_eq!(&fallback[11..13], "00");
assert_eq!(&fallback[14..16], "00");
assert_eq!(&fallback[17..19], "00");
assert_eq!(&fallback[19..20], ".");
assert_eq!(&fallback[29..30], "Z");
}
#[test]
fn test_parse_rfc3339_valid() {
let ts = "2026-01-19T12:00:00Z";
let result = timeutil::parse_rfc3339(ts);
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.year(), 2026);
assert_eq!(dt.month() as u8, 1);
assert_eq!(dt.day(), 19);
}
#[test]
fn test_parse_rfc3339_invalid() {
let ts = "invalid-timestamp";
let result = timeutil::parse_rfc3339(ts);
assert!(result.is_err());
}
#[test]
fn test_format_rfc3339_normalizes_to_utc() {
let dt = timeutil::parse_rfc3339("2026-01-19T12:00:00-05:00").unwrap();
let formatted = timeutil::format_rfc3339(dt).unwrap();
assert_eq!(formatted, "2026-01-19T17:00:00.000000000Z");
}