triangular_earth_calender_lib/
lib.rs1use 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 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(); 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}