triangular_earth_calender_lib/
lib.rs

1use chrono::{Datelike, FixedOffset, Local, Offset, Timelike, Utc};
2use std::{fmt, time};
3use std::{
4    num::ParseIntError,
5    ops::{Add, Sub},
6    str::FromStr,
7};
8use thiserror::Error;
9
10#[derive(Debug, Error)]
11pub enum Errors {
12    #[error("Tracing Error")]
13    Tracing(#[from] tracing::dispatcher::SetGlobalDefaultError),
14    #[error("Parse chrono datetime error")]
15    ParseDateTime(#[from] chrono::ParseError),
16    #[error("Error when attempting to parse string to int")]
17    ParseInt(#[from] ParseIntError),
18    #[error("Error when perform std IO")]
19    Io(#[from] std::io::Error),
20    #[error("Generic Error:\n{0}")]
21    Generic(String),
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
25pub struct Year(i32);
26impl Year {
27    pub fn new(year: i32) -> Self {
28        Year(year)
29    }
30    pub fn from_datetime(dt: chrono::DateTime<FixedOffset>) -> Self {
31        let greg_year = dt.year();
32        Year(greg_year - 2001)
33    }
34    pub fn is_leap_year(&self) -> bool {
35        let y = self.0 + 2001;
36        (y % 4) == 0 && (y % 100) != 0 || (y % 400) == 0
37    }
38}
39
40impl fmt::Display for Year {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        write!(f, "{}", self.0)
43    }
44}
45
46impl Add for Year {
47    type Output = Self;
48
49    fn add(self, other: Self) -> Self {
50        Year(self.0 + other.0)
51    }
52}
53
54impl Sub for Year {
55    type Output = Self;
56
57    fn sub(self, other: Self) -> Self {
58        Year(self.0 - other.0)
59    }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
63pub struct Month(u32);
64impl Month {
65    pub fn new(month: u32) -> Self {
66        Month(month)
67    }
68    pub fn from_datetime(dt: chrono::DateTime<FixedOffset>) -> Self {
69        let year_days = dt
70            .format("%j")
71            .to_string()
72            .parse::<u32>()
73            .expect("Failed to parse year day");
74        Month(year_days / 36)
75    }
76}
77impl fmt::Display for Month {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        if self.0 == 10 {
80            write!(f, "A")
81        } else {
82            write!(f, "{}", self.0)
83        }
84    }
85}
86
87impl Add for Month {
88    type Output = Self;
89
90    fn add(self, other: Self) -> Self {
91        Month(self.0 + other.0)
92    }
93}
94
95impl Sub for Month {
96    type Output = Self;
97
98    fn sub(self, other: Self) -> Self {
99        Month(self.0 - other.0)
100    }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
104pub struct Day(u32);
105impl Day {
106    pub fn new(day: u32) -> Self {
107        Day(day)
108    }
109    pub fn from_datetime(dt: chrono::DateTime<FixedOffset>) -> Self {
110        let year_days = dt
111            .format("%j")
112            .to_string()
113            .parse::<u32>()
114            .expect("Failed to parse year day");
115        if year_days == 1 {
116            Day(0)
117        } else {
118            let res = (year_days / 36) * 36;
119            Day(year_days - res)
120        }
121    }
122}
123
124impl fmt::Display for Day {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        write!(f, "{}", self.0)
127    }
128}
129
130impl Add for Day {
131    type Output = Self;
132
133    fn add(self, other: Self) -> Self {
134        Day(self.0 + other.0)
135    }
136}
137
138impl Sub for Day {
139    type Output = Self;
140
141    fn sub(self, other: Self) -> Self {
142        Day(self.0 - other.0)
143    }
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
147pub struct Time(u32);
148impl Time {
149    pub fn new(time: u32) -> Self {
150        Time(time)
151    }
152    pub fn from_datetime(dt: chrono::DateTime<FixedOffset>) -> Self {
153        let hour = dt.hour();
154        let minute = dt.minute();
155        let second = dt.second();
156        Time::from_hms(hour, minute, second)
157    }
158
159    pub fn from_hms(hour: u32, minute: u32, second: u32) -> Self {
160        let res = ((hour * 3600) + (minute * 60) + second) as f32 * 1.15741;
161        // let res = ((hour as f64 / 24.0)
162        //     + (minute as f64 / 1440.0)
163        //     + (second as f64 / 86400.0) * 100000.0);
164        Time(res as u32)
165    }
166
167    pub fn from_hour(hour: u32) -> Self {
168        Time(((hour * 3600) as f32 * 1.15741) as u32)
169    }
170
171    pub fn now() -> Self {
172        let now: chrono::DateTime<FixedOffset> = Local::now().fixed_offset();
173        Time::from_datetime(now)
174    }
175}
176impl fmt::Display for Time {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        let strng = format!("{:0>5}", self.0);
179        let remove_trailing = strng.strip_suffix('0');
180        if let Some(rt) = remove_trailing {
181            write!(f, "{}", rt)
182        } else {
183            write!(f, "{}", strng)
184        }
185    }
186}
187
188impl FromStr for Time {
189    type Err = Errors;
190    fn from_str(s: &str) -> Result<Self, Self::Err> {
191        let strng = s.strip_prefix(':').unwrap_or(s);
192        Ok(Time::new(strng.parse::<u32>()?))
193    }
194}
195
196impl Add for Time {
197    type Output = Self;
198
199    fn add(self, other: Self) -> Self {
200        Time(self.0 + other.0)
201    }
202}
203
204impl Sub for Time {
205    type Output = Self;
206
207    fn sub(self, other: Self) -> Self {
208        Time(self.0 - other.0)
209    }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq)]
213pub struct Date {
214    year: Year,
215    month: Month,
216    day: Day,
217}
218
219impl Date {
220    pub fn new(year: Year, month: Month, day: Day) -> Self {
221        Date { year, month, day }
222    }
223    pub fn tec_epoch() -> Self {
224        Date::new(Year::new(0), Month::new(0), Day::new(0))
225    }
226    pub fn from_datetime(dt: chrono::DateTime<FixedOffset>) -> Self {
227        let year = Year::from_datetime(dt);
228        let month = Month::from_datetime(dt);
229        let day = Day::from_datetime(dt);
230        Date::new(year, month, day)
231    }
232}
233
234impl FromStr for Date {
235    type Err = Errors;
236    fn from_str(s: &str) -> Result<Self, Self::Err> {
237        let l: Vec<&str> = s.split('.').collect();
238        let year = Year::new(l[0].parse::<i32>()?);
239        let month_num = match l[1].parse::<u32>() {
240            Ok(i) => i,
241            Err(_) => {
242                if l[1] == "A" {
243                    10
244                } else {
245                    return Err(Errors::Generic(format!("{} month is not valid", l[1])));
246                }
247            }
248        };
249        let month = Month::new(month_num);
250        let day = Day::new(l[2].parse::<u32>()?);
251        Ok(Date::new(year, month, day))
252    }
253}
254impl fmt::Display for Date {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        write!(f, "{}.{}.{}", self.year, self.month, self.day)
257    }
258}
259
260#[derive(Debug, Clone, Copy, PartialEq)]
261pub struct DateTime {
262    date: Date,
263    time: Time,
264    offset: FixedOffset,
265}
266
267impl DateTime {
268    pub fn new(date: Date, time: Time, offset_opt: Option<FixedOffset>) -> Self {
269        let offset = if let Some(off) = offset_opt {
270            off
271        } else {
272            *Local::now().fixed_offset().offset()
273        };
274        DateTime { date, time, offset }
275    }
276
277    pub fn from_datetime(dt: chrono::DateTime<FixedOffset>) -> Self {
278        let date = Date::from_datetime(dt);
279        let time = Time::from_datetime(dt);
280        let offset = dt.offset();
281        DateTime::new(date, time, Some(*offset))
282    }
283
284    pub fn now() -> Self {
285        let now: chrono::DateTime<FixedOffset> = Local::now().fixed_offset();
286        DateTime::from_datetime(now)
287    }
288
289    pub fn tec_epoch() -> Self {
290        let date = Date::new(Year(0), Month(0), Day(0));
291        DateTime::new(date, Time(0), Some(Utc.fix()))
292    }
293}
294
295impl fmt::Display for DateTime {
296    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297        let local_offset = *Local::now().fixed_offset().offset();
298        if local_offset == self.offset {
299            write!(f, "{}:{}", self.date, self.time)
300        } else {
301            write!(f, "{}:{}@{}", self.date, self.time, self.offset)
302        }
303    }
304}
305
306fn str2offset(s: &str) -> Result<FixedOffset, Errors> {
307    if s.contains(':') {
308        let strng = s.replace(':', "");
309        let num = strng.parse::<i32>()?;
310        let secs = num * 60;
311        let fo = FixedOffset::west_opt(secs).unwrap();
312        Ok(fo)
313    } else {
314        let num = s.parse::<i32>()?;
315        let secs = num * 60;
316        let fo = FixedOffset::west_opt(secs).unwrap();
317        Ok(fo)
318    }
319}
320
321impl FromStr for DateTime {
322    type Err = Errors;
323    fn from_str(s: &str) -> Result<Self, Self::Err> {
324        let offset = if s.contains('@') {
325            let strng: Vec<&str> = s.split('@').collect();
326            str2offset(strng[1])?
327        } else {
328            *Local::now().fixed_offset().offset()
329        };
330
331        if s.contains(':') && !s.contains('-') {
332            let splt: Vec<&str> = s.split(':').collect();
333            let date = Date::from_str(splt[0])?;
334            let time = Time::from_str(splt[1])?;
335            Ok(DateTime::new(date, time, Some(offset)))
336        } else if s.contains('.') {
337            let date = Date::from_str(s)?;
338            let time = Time::new(50000);
339            Ok(DateTime::new(date, time, Some(offset)))
340        } else {
341            match dateparser::parse(s) {
342                Ok(dt) => Ok(DateTime::from_datetime(dt.fixed_offset())),
343                Err(_) => Err(Errors::Generic(format!("{s} is not a gregorian datetime"))),
344            }
345        }
346    }
347}
348
349#[derive(Debug, Clone, Copy, PartialEq, Ord, PartialOrd, Eq)]
350pub struct Duration(u64);
351
352impl Duration {
353    pub fn new(fracs: u64) -> Self {
354        Duration(fracs)
355    }
356    pub fn from_secs(secs: u64) -> Self {
357        Duration((secs as f64 * 1.1574) as u64)
358    }
359    pub fn to_secs(&self) -> u64 {
360        (self.0 as f64 / 1.1574) as u64
361    }
362    pub fn from_std_dur(dur: time::Duration) -> Self {
363        Duration::from_secs(dur.as_secs())
364    }
365
366    pub fn to_std_dur(&self) -> time::Duration {
367        time::Duration::from_secs(self.to_secs())
368    }
369}
370
371impl FromStr for Duration {
372    type Err = Errors;
373    fn from_str(s: &str) -> Result<Self, Self::Err> {
374        let lower = s.to_lowercase();
375        if lower.ends_with('s') {
376            let strp = lower.strip_suffix('s').unwrap(); // Can't fail
377            let secs = strp.parse::<u64>()?;
378            Ok(Duration::from_secs(secs))
379        } else if lower.ends_with('f') {
380            let strp = lower.strip_suffix('f').unwrap();
381            let fracs = strp.parse::<u64>()?;
382            Ok(Duration::new(fracs))
383        } else {
384            let fracs = s.parse::<u64>()?;
385            Ok(Duration::new(fracs))
386        }
387    }
388}
389
390impl fmt::Display for Duration {
391    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392        write!(f, "{}", self.0)
393    }
394}