use alloc::string::String;
use core::cmp::Ordering;
use core::fmt;
use core::str::FromStr;
use crate::error::{Error, Result};
use crate::format;
use crate::offset::Offset;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Zone {
Utc,
Fixed(Offset),
}
impl Zone {
pub const fn utc() -> Zone {
Zone::Utc
}
pub const fn fixed(offset: Offset) -> Zone {
if offset.is_utc() {
Zone::Utc
} else {
Zone::Fixed(offset)
}
}
pub fn from_name(name: &str) -> Option<Zone> {
match name {
"UTC" | "GMT" | "Z" | "Etc/UTC" | "Etc/GMT" => Some(Zone::Utc),
_ => None,
}
}
pub const fn offset(self) -> Offset {
match self {
Zone::Utc => Offset::UTC,
Zone::Fixed(offset) => offset,
}
}
pub fn to_iso(self) -> String {
match self {
Zone::Utc => alloc::string::String::from("UTC"),
Zone::Fixed(offset) => format::format_offset(offset),
}
}
pub fn from_iso(s: &str) -> Result<Zone> {
match s {
"UTC" | "Z" | "z" => Ok(Zone::Utc),
_ => Ok(Zone::fixed(Offset::from_iso(s)?)),
}
}
}
impl fmt::Display for Zone {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_iso())
}
}
impl FromStr for Zone {
type Err = Error;
fn from_str(s: &str) -> Result<Zone> {
Zone::from_iso(s)
}
}
impl PartialOrd for Zone {
fn partial_cmp(&self, other: &Zone) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Zone {
fn cmp(&self, other: &Zone) -> Ordering {
self.offset().cmp(&other.offset())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalization_and_equality() {
assert_eq!(Zone::fixed(Offset::UTC), Zone::Utc);
assert_eq!(Zone::from_name("UTC"), Some(Zone::Utc));
assert_eq!(Zone::from_name("CST"), None);
let z = Zone::fixed(Offset::from_hms(8, 0, 0).unwrap());
assert_eq!(z.offset().as_seconds(), 8 * 3600);
}
#[test]
fn iso_round_trips() {
for s in ["UTC", "+08:00", "-05:30"] {
let z = Zone::from_iso(s).unwrap_or_else(|e| panic!("{s}: {e}"));
assert_eq!(z.to_iso(), s, "{s}");
}
assert_eq!(Zone::from_iso("Z").unwrap(), Zone::Utc);
assert!(Zone::from_iso("+24:00").is_err());
}
#[test]
fn ordering() {
assert!(Zone::Utc < Zone::fixed(Offset::from_hms(1, 0, 0).unwrap()));
assert!(Zone::fixed(Offset::from_hms(-1, 0, 0).unwrap()) < Zone::Utc);
}
}