use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TimeScale {
UTC,
TAI,
TT,
TDB,
ET,
GPST,
GST,
BDT,
QZSST,
}
impl TimeScale {
pub const ALL_NAMES: &[&str] = &[
"UTC", "TAI", "TT", "TDB", "ET", "GPST", "GST", "BDT", "QZSST",
];
#[must_use]
pub const fn is_utc(self) -> bool {
matches!(self, Self::UTC)
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::UTC => "UTC",
Self::TAI => "TAI",
Self::TT => "TT",
Self::TDB => "TDB",
Self::ET => "ET",
Self::GPST => "GPST",
Self::GST => "GST",
Self::BDT => "BDT",
Self::QZSST => "QZSST",
}
}
#[must_use]
pub const fn to_hifitime(self) -> hifitime::TimeScale {
match self {
Self::UTC => hifitime::TimeScale::UTC,
Self::TAI => hifitime::TimeScale::TAI,
Self::TT => hifitime::TimeScale::TT,
Self::TDB => hifitime::TimeScale::TDB,
Self::ET => hifitime::TimeScale::ET,
Self::GPST => hifitime::TimeScale::GPST,
Self::GST => hifitime::TimeScale::GST,
Self::BDT => hifitime::TimeScale::BDT,
Self::QZSST => hifitime::TimeScale::QZSST,
}
}
#[must_use]
pub const fn from_hifitime(ts: hifitime::TimeScale) -> Self {
match ts {
hifitime::TimeScale::TAI => Self::TAI,
hifitime::TimeScale::TT => Self::TT,
hifitime::TimeScale::TDB => Self::TDB,
hifitime::TimeScale::ET => Self::ET,
hifitime::TimeScale::GPST => Self::GPST,
hifitime::TimeScale::GST => Self::GST,
hifitime::TimeScale::BDT => Self::BDT,
hifitime::TimeScale::QZSST => Self::QZSST,
_ => Self::UTC,
}
}
}
#[must_use]
pub fn time_scale_from_conversion_fn(name: &str) -> Option<TimeScale> {
match name {
"to_utc" => Some(TimeScale::UTC),
"to_tai" => Some(TimeScale::TAI),
"to_tt" => Some(TimeScale::TT),
"to_tdb" => Some(TimeScale::TDB),
"to_et" => Some(TimeScale::ET),
"to_gpst" => Some(TimeScale::GPST),
"to_gst" => Some(TimeScale::GST),
"to_bdt" => Some(TimeScale::BDT),
"to_qzsst" => Some(TimeScale::QZSST),
_ => None,
}
}
impl fmt::Display for TimeScale {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseTimeScaleError {
pub input: String,
}
impl fmt::Display for ParseTimeScaleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unknown time scale `{}`; expected one of: {}",
self.input,
TimeScale::ALL_NAMES.join(", ")
)
}
}
impl std::error::Error for ParseTimeScaleError {}
impl FromStr for TimeScale {
type Err = ParseTimeScaleError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let variants = [
Self::UTC,
Self::TAI,
Self::TT,
Self::TDB,
Self::ET,
Self::GPST,
Self::GST,
Self::BDT,
Self::QZSST,
];
for variant in variants {
if variant.name() == s {
return Ok(variant);
}
}
Err(ParseTimeScaleError {
input: s.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_roundtrip() {
let scales = [
TimeScale::UTC,
TimeScale::TAI,
TimeScale::TT,
TimeScale::TDB,
TimeScale::ET,
TimeScale::GPST,
TimeScale::GST,
TimeScale::BDT,
TimeScale::QZSST,
];
for scale in &scales {
let s = scale.to_string();
let parsed: TimeScale = s.parse().unwrap();
assert_eq!(*scale, parsed);
}
}
#[test]
fn from_str_unknown() {
let err = "INVALID".parse::<TimeScale>().unwrap_err();
assert_eq!(err.input, "INVALID");
assert!(err.to_string().contains("unknown time scale"));
}
#[test]
fn hifitime_roundtrip() {
let scales = [
TimeScale::UTC,
TimeScale::TAI,
TimeScale::TT,
TimeScale::TDB,
TimeScale::ET,
TimeScale::GPST,
TimeScale::GST,
TimeScale::BDT,
TimeScale::QZSST,
];
for scale in &scales {
let hf = scale.to_hifitime();
let back = TimeScale::from_hifitime(hf);
assert_eq!(*scale, back);
}
}
#[test]
fn utc_is_default() {
assert!(TimeScale::UTC.is_utc());
assert!(!TimeScale::TT.is_utc());
}
}