kron_lib/
lib.rs

1#![doc = include_str!("../README.md")]
2
3//--------------------------------------------------------------------------------------------------
4
5use anyhow::{anyhow, Result};
6use lazy_static::lazy_static;
7use time::OffsetDateTime;
8
9//--------------------------------------------------------------------------------------------------
10
11pub const FMT_COMPACT: &str = "[year][month][day]-[hour][minute][second]Z";
12pub const FMT_ISO8601: &str = "[year]-[month]-[day]T[hour]:[minute]:[second]Z";
13pub const FMT_ISO8601NS: &str = "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:9]Z";
14
15//--------------------------------------------------------------------------------------------------
16
17lazy_static! {
18    pub static ref COMPACT: KronFormat<'static> = KronFormat::new(FMT_COMPACT).unwrap();
19    pub static ref ISO8601: KronFormat<'static> = KronFormat::new(FMT_ISO8601).unwrap();
20    pub static ref ISO8601NS: KronFormat<'static> = KronFormat::new(FMT_ISO8601NS).unwrap();
21}
22
23//--------------------------------------------------------------------------------------------------
24
25#[cfg(test)]
26mod tests;
27
28//--------------------------------------------------------------------------------------------------
29
30#[derive(Clone)]
31pub struct KronFormat<'a> {
32    fmt: Vec<time::format_description::FormatItem<'a>>,
33}
34
35impl KronFormat<'_> {
36    pub fn new(s: &str) -> Result<KronFormat> {
37        Ok(KronFormat {
38            fmt: time::format_description::parse(s)?,
39        })
40    }
41
42    pub fn from(s: &str) -> Result<KronFormat> {
43        match s {
44            "COMPACT" => Ok(COMPACT.clone()),
45            "ISO8601" => Ok(ISO8601.clone()),
46            "ISO8601NS" => Ok(ISO8601NS.clone()),
47            _ => KronFormat::new(s),
48            //_ => Err(anyhow!("Invalid format: {s:?}")),
49        }
50    }
51}
52
53//--------------------------------------------------------------------------------------------------
54
55pub struct Kron {
56    pub dt: OffsetDateTime,
57}
58
59impl Kron {
60    pub fn now() -> Kron {
61        Kron {
62            dt: OffsetDateTime::now_utc(),
63        }
64    }
65
66    pub fn from(s: &str) -> Result<Kron> {
67        if s.contains('.') && s.parse::<f64>().is_ok() {
68            let mut p = s.split('.');
69            let mut ts = Kron::timestamp(p.next().unwrap().parse::<i64>()?)?;
70            let mut nanos = p.next().unwrap().to_string();
71            while nanos.len() < 9 {
72                nanos.push('0');
73            }
74            ts.dt = ts.dt.replace_nanosecond(nanos.parse::<u32>()?)?;
75            Ok(ts)
76        } else if let Ok(n) = s.parse::<i64>() {
77            Kron::timestamp(n)
78        } else {
79            Err(anyhow!("Invalid timestamp: {s:?}"))
80        }
81    }
82
83    pub fn timestamp(n: i64) -> Result<Kron> {
84        Ok(Kron {
85            dt: OffsetDateTime::from_unix_timestamp(n)?,
86        })
87    }
88
89    pub fn epoch() -> Kron {
90        Kron::timestamp(0).unwrap()
91    }
92
93    pub fn format(&self, f: &KronFormat) -> Result<String> {
94        Ok(self.dt.format(&f.fmt)?)
95    }
96
97    pub fn format_str(&self, s: &'static str) -> Result<String> {
98        self.format(&KronFormat::new(s)?)
99    }
100}
101
102impl std::fmt::Display for Kron {
103    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
104        write!(f, "{}", self.dt.format(&ISO8601.fmt).unwrap())
105    }
106}