use chrono::{DateTime, NaiveDate, Utc};
use chrono_tz::Tz;
pub fn resolve_timezone(
client_tz: Option<&str>,
user_setting: Option<&str>,
config_default: &str,
) -> Tz {
for candidate in [client_tz, user_setting, Some(config_default)] {
if let Some(tz) = candidate.and_then(parse_timezone) {
return tz;
}
}
Tz::UTC
}
pub fn parse_timezone(s: &str) -> Option<Tz> {
s.parse::<Tz>().ok()
}
pub fn today_in_tz(tz: Tz) -> NaiveDate {
Utc::now().with_timezone(&tz).date_naive()
}
pub fn now_in_tz(tz: Tz) -> DateTime<Tz> {
Utc::now().with_timezone(&tz)
}
pub fn detect_system_timezone() -> Tz {
iana_time_zone::get_timezone()
.ok()
.and_then(|s| parse_timezone(&s))
.unwrap_or(Tz::UTC)
}
#[cfg(test)]
mod tests {
use chrono::Datelike;
use super::*;
#[test]
fn test_resolve_client_wins() {
let tz = resolve_timezone(Some("America/New_York"), Some("Europe/London"), "UTC");
assert_eq!(tz, chrono_tz::America::New_York);
}
#[test]
fn test_resolve_user_setting_fallback() {
let tz = resolve_timezone(None, Some("Europe/London"), "UTC");
assert_eq!(tz, chrono_tz::Europe::London);
}
#[test]
fn test_resolve_config_fallback() {
let tz = resolve_timezone(None, None, "Asia/Tokyo");
assert_eq!(tz, chrono_tz::Asia::Tokyo);
}
#[test]
fn test_resolve_all_none_utc() {
let tz = resolve_timezone(None, None, "UTC");
assert_eq!(tz, Tz::UTC);
}
#[test]
fn test_resolve_invalid_client_skipped() {
let tz = resolve_timezone(Some("Fake/Zone"), Some("Europe/London"), "UTC");
assert_eq!(tz, chrono_tz::Europe::London);
}
#[test]
fn test_parse_valid() {
assert_eq!(
parse_timezone("America/Chicago"),
Some(chrono_tz::America::Chicago)
);
}
#[test]
fn test_parse_invalid() {
assert_eq!(parse_timezone("Fake/Zone"), None);
}
#[test]
fn test_detect_system_tz() {
let tz = detect_system_timezone();
let _ = now_in_tz(tz); }
#[test]
fn test_today_in_tz_returns_valid_date() {
let date = today_in_tz(Tz::UTC);
assert!(date.year() > 0);
assert!((1..=12).contains(&date.month()));
assert!((1..=31).contains(&date.day()));
}
}