1#![forbid(unsafe_code)]
30#![cfg_attr(not(feature = "std"), no_std)]
31
32extern crate alloc;
33use crate::datetime::UTCDateTime;
34use crate::epoch::{Epoch, Timestamp, UnixTimestamp, UNIX_EPOCH};
35use crate::format::iso8601::ISO8601_DATE_TIME;
36use crate::format::{Format, FormatError, FormatParser};
37use alloc::string::String;
38use core::cmp::Ordering;
39use core::fmt::{Display, Formatter};
40use core::hash::{Hash, Hasher};
41use irox_fixedmath::{FixedU128, FixedU32, FixedU64};
42pub use irox_units::bounds::{GreaterThanEqualToValueError, LessThanValue, Range};
43pub use irox_units::units::duration::{Duration, DurationUnit};
44use irox_units::units::duration::{NANOS_TO_SEC, SEC_TO_NANOS};
45
46pub mod datetime;
47pub mod epoch;
48pub mod format;
49pub mod gregorian;
50pub mod julian;
51
52#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
57pub struct Time {
58 second_of_day: u32,
59 nanoseconds: u32,
60}
61
62impl Time {
63 pub fn new(
69 second_of_day: u32,
70 nanoseconds: u32,
71 ) -> Result<Time, GreaterThanEqualToValueError<u32>> {
72 LessThanValue::new(86400).check_value_is_valid(&second_of_day)?;
73 LessThanValue::new(1_000_000_000).check_value_is_valid(&nanoseconds)?;
74 Ok(Time {
75 second_of_day,
76 nanoseconds,
77 })
78 }
79
80 pub fn from_seconds_f64(seconds: f64) -> Result<Time, GreaterThanEqualToValueError<f64>> {
83 LessThanValue::new(86400_f64).check_value_is_valid(&seconds)?;
84
85 let second_of_day = seconds as u32;
86 let frac_nanos = seconds - second_of_day as f64;
87 let nanoseconds = (frac_nanos * SEC_TO_NANOS) as u32;
88 Ok(Time {
89 second_of_day,
90 nanoseconds,
91 })
92 }
93
94 pub fn from_hms(
95 hours: u8,
96 minutes: u8,
97 seconds: u8,
98 ) -> Result<Time, GreaterThanEqualToValueError<u8>> {
99 LessThanValue::new(24u8).check_value_is_valid(&hours)?;
100 LessThanValue::new(60u8).check_value_is_valid(&minutes)?;
101 LessThanValue::new(60u8).check_value_is_valid(&seconds)?;
102
103 let second_of_day = hours as u32 * 3600 + minutes as u32 * 60 + seconds as u32;
104 Ok(Time {
105 second_of_day,
106 nanoseconds: 0,
107 })
108 }
109
110 pub fn from_hms_f64(
111 hours: u8,
112 minutes: u8,
113 seconds: f64,
114 ) -> Result<Time, GreaterThanEqualToValueError<f64>> {
115 LessThanValue::new(24u8).check_value_is_valid(&hours)?;
116 LessThanValue::new(60u8).check_value_is_valid(&minutes)?;
117 LessThanValue::new(60f64).check_value_is_valid(&seconds)?;
118 let nanoseconds = (irox_tools::f64::FloatExt::fract(seconds) * SEC_TO_NANOS) as u32;
119 let second_of_day = hours as u32 * 3600 + minutes as u32 * 60 + seconds as u32;
120 Ok(Time {
121 second_of_day,
122 nanoseconds,
123 })
124 }
125
126 pub fn from_hms_millis(
127 hours: u8,
128 minutes: u8,
129 seconds: u8,
130 millis: u32,
131 ) -> Result<Time, GreaterThanEqualToValueError<u32>> {
132 LessThanValue::new(1_000_000).check_value_is_valid(&millis)?;
133 let time = Self::from_hms(hours, minutes, seconds)?;
134 Ok(Time {
135 second_of_day: time.second_of_day,
136 nanoseconds: millis * 1000,
137 })
138 }
139
140 #[must_use]
143 pub fn get_seconds(&self) -> u32 {
144 self.second_of_day
145 }
146
147 #[must_use]
150 pub fn get_nanoseconds(&self) -> u32 {
151 self.nanoseconds
152 }
153
154 #[must_use]
157 pub fn as_duration(&self) -> Duration {
158 let time = self.second_of_day as f64 + self.nanoseconds as f64 * NANOS_TO_SEC;
159 Duration::new(time, DurationUnit::Second)
160 }
161
162 #[must_use]
165 pub fn as_hours(&self) -> u32 {
166 (self.second_of_day as f64 / SECONDS_IN_HOUR as f64) as u32
167 }
168
169 #[must_use]
172 pub fn as_minutes(&self) -> u32 {
173 (self.second_of_day as f64 / SECONDS_IN_MINUTE as f64) as u32
174 }
175
176 #[must_use]
179 pub fn as_hms(&self) -> (u32, u32, u32) {
180 let hours = self.as_hours();
181 let minutes = self.as_minutes() - hours * MINUTES_IN_HOUR;
182 let seconds = self.get_seconds() - hours * SECONDS_IN_HOUR - minutes * SECONDS_IN_MINUTE;
183 (hours, minutes, seconds)
184 }
185
186 #[must_use]
189 pub fn as_hms_f64(&self) -> (u32, u32, f64) {
190 let hours = self.as_hours();
191 let minutes = self.as_minutes() - hours * MINUTES_IN_HOUR;
192 let seconds = self.get_seconds() - hours * SECONDS_IN_HOUR - minutes * SECONDS_IN_MINUTE;
193 let seconds = seconds as f64 + self.get_secondsfrac();
194 (hours, minutes, seconds)
195 }
196
197 #[must_use]
200 pub fn get_secondsfrac(&self) -> f64 {
201 self.nanoseconds as f64 * NANOS_TO_SEC
202 }
203
204 #[must_use]
207 pub fn format<F: Format<Self>>(&self, format: &F) -> String {
208 format.format(self)
209 }
210
211 pub fn parse_from<F: FormatParser<Self>>(
214 format: &F,
215 string: &str,
216 ) -> Result<Self, FormatError> {
217 format.try_from(string)
218 }
219
220 #[must_use]
241 pub fn wrapping_add(&self, duration: Duration) -> (Time, Duration) {
242 let add_seconds = duration.as_seconds();
243 let add_nanos = (duration - Duration::from_seconds(add_seconds)).as_nanos();
244 let mut new_seconds = self.second_of_day as u64 + add_seconds;
245 let mut new_nanos = add_nanos + self.nanoseconds as u64;
246 if new_nanos >= NANOS_IN_SECOND as u64 {
247 new_nanos -= NANOS_IN_SECOND as u64;
248 new_seconds += 1;
249 }
250 let mut rollover = Duration::default();
251 if new_seconds >= SECONDS_IN_DAY as u64 {
252 let days = new_seconds / SECONDS_IN_DAY as u64;
253 new_seconds -= days * SECONDS_IN_DAY as u64;
254 rollover += Duration::from_days(days);
255 }
256 (
257 Time {
258 second_of_day: new_seconds as u32,
259 nanoseconds: new_nanos as u32,
260 },
261 rollover,
262 )
263 }
264}
265
266impl Display for Time {
267 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
268 let (h, m, s) = self.as_hms();
269 if f.alternate() {
270 let s = s as f64 + self.get_secondsfrac();
271 f.write_fmt(format_args!("{h:02}:{m:02}:{s:09.6}"))
272 } else {
273 f.write_fmt(format_args!("{h:02}:{m:02}:{s:02}"))
274 }
275 }
276}
277
278impl From<Time> for Duration {
279 fn from(value: Time) -> Self {
280 value.as_duration()
281 }
282}
283
284pub const HOURS_IN_DAY: u32 = 24;
286
287pub const MINUTES_IN_HOUR: u32 = 60;
289
290pub const SECONDS_IN_MINUTE: u32 = 60;
292
293pub const MINUTES_IN_DAY: u32 = 1440;
295
296pub const SECONDS_IN_HOUR: u32 = 3600;
298
299pub const SECONDS_IN_DAY: u32 = 86400;
302
303pub const NANOS_IN_MICRO: u32 = 1000;
306pub const NANOS_IN_MILLI: u32 = 1_000_000;
309pub const NANOS_IN_SECOND: u32 = 1_000_000_000;
312pub const NANOS_IN_DAY: u64 = 86_400_000_000_000_u64;
315
316#[derive(Debug, Copy, Clone)]
324pub struct Time32 {
325 epoch: Epoch,
327
328 inner: FixedU32,
329}
330
331impl Time32 {
332 #[must_use]
335 pub const fn as_u32(&self) -> u32 {
336 self.inner.raw_value()
337 }
338}
339
340#[derive(Debug, Copy, Clone)]
352pub struct Time64 {
353 epoch: Epoch,
355 inner: FixedU64,
356}
357impl Default for Time64 {
358 fn default() -> Self {
359 Time64 {
360 epoch: UNIX_EPOCH,
361 inner: FixedU64::default(),
362 }
363 }
364}
365impl Time64 {
366 #[must_use]
369 pub const fn as_u64(&self) -> u64 {
370 self.inner.raw_value()
371 }
372
373 pub fn from_unix_u64(value: u64) -> Self {
376 Self {
377 epoch: UNIX_EPOCH,
378 inner: value.into(),
379 }
380 }
381
382 pub fn from_unix_raw(value: u64) -> Self {
383 Self {
384 epoch: UNIX_EPOCH,
385 inner: FixedU64::from_raw_value(value),
386 }
387 }
388}
389
390#[derive(Debug, Copy, Clone)]
405pub struct Time128 {
406 epoch: Epoch,
409
410 inner: FixedU128,
411}
412
413impl Time128 {
414 #[must_use]
417 pub const fn as_u128(&self) -> u128 {
418 self.inner.raw_value()
419 }
420}
421
422macro_rules! impls {
423 ($strukt:ty, $prim:ty, $inner:ty) => {
424 impl $strukt {
425 #[must_use]
426 pub const fn new(epoch: Epoch, seconds: $prim, fractional_seconds: $prim) -> Self {
427 let inner = <$inner>::from_parts(seconds, fractional_seconds);
428 Self { epoch, inner }
429 }
430 #[must_use]
431 pub fn new_f64(epoch: Epoch, seconds: f64) -> Self {
432 let inner = <$inner>::from(seconds);
433 Self { epoch, inner }
434 }
435 #[must_use]
438 pub const fn get_epoch(&self) -> Epoch {
439 self.epoch
440 }
441 #[must_use]
442 pub const fn get_seconds(&self) -> $prim {
443 self.inner.whole()
444 }
445 #[must_use]
446 pub const fn get_fractional_seconds(&self) -> $prim {
447 self.inner.fract()
448 }
449 #[must_use]
450 pub fn as_f64(&self) -> f64 {
451 self.into()
452 }
453 pub fn try_from_iso8601(val: &str) -> Result<Self, FormatError> {
454 let v = ISO8601_DATE_TIME.try_from(val)?;
455 Ok(v.into())
456 }
457
458 #[cfg(feature = "std")]
459 pub fn now() -> Self {
460 UnixTimestamp::now().into()
461 }
462 #[must_use]
463 pub fn as_only_seconds(&self) -> Self {
464 Self::new(self.epoch, self.inner.whole(), 0)
465 }
466 #[must_use]
467 pub fn as_only_fractional(&self) -> Self {
468 Self::new(self.epoch, 0, self.inner.fract())
469 }
470 #[must_use]
471 pub fn as_epoch(&self, other: Epoch) -> Self {
472 let offset = other.0 - self.epoch.0;
473 *self + offset
474 }
475 }
476 impl From<$strukt> for f64 {
477 fn from(value: $strukt) -> Self {
478 value.inner.as_f64()
479 }
480 }
481 impl From<&$strukt> for f64 {
482 fn from(value: &$strukt) -> Self {
483 value.inner.as_f64()
484 }
485 }
486 impl From<&mut $strukt> for f64 {
487 fn from(value: &mut $strukt) -> Self {
488 value.inner.as_f64()
489 }
490 }
491 impl<T> From<Timestamp<T>> for $strukt {
492 fn from(value: Timestamp<T>) -> Self {
493 let val = value.get_offset().as_seconds_f64();
494
495 <$strukt>::new_f64(value.get_epoch(), val)
496 }
497 }
498 impl<T> From<&Timestamp<T>> for $strukt {
499 fn from(value: &Timestamp<T>) -> Self {
500 let val = value.get_offset().as_seconds_f64();
501
502 <$strukt>::new_f64(value.get_epoch(), val)
503 }
504 }
505 impl<T> From<&mut Timestamp<T>> for $strukt {
506 fn from(value: &mut Timestamp<T>) -> Self {
507 let val = value.get_offset().as_seconds_f64();
508
509 <$strukt>::new_f64(value.get_epoch(), val)
510 }
511 }
512 impl<T> From<$strukt> for Timestamp<T> {
513 fn from(value: $strukt) -> Self {
514 let dur = Duration::new(value.as_f64(), DurationUnit::Second);
515 Timestamp::<T>::new(value.get_epoch(), dur)
516 }
517 }
518 impl<T> From<&$strukt> for Timestamp<T> {
519 fn from(value: &$strukt) -> Self {
520 let dur = Duration::new(value.as_f64(), DurationUnit::Second);
521 Timestamp::<T>::new(value.get_epoch(), dur)
522 }
523 }
524 impl<T> From<&mut $strukt> for Timestamp<T> {
525 fn from(value: &mut $strukt) -> Self {
526 let dur = Duration::new(value.as_f64(), DurationUnit::Second);
527 Timestamp::<T>::new(value.get_epoch(), dur)
528 }
529 }
530 impl From<UTCDateTime> for $strukt {
531 fn from(value: UTCDateTime) -> Self {
532 let ts = UnixTimestamp::from(value);
533 <$strukt>::from(ts)
534 }
535 }
536 impl From<&UTCDateTime> for $strukt {
537 fn from(value: &UTCDateTime) -> Self {
538 let ts = UnixTimestamp::from(value);
539 <$strukt>::from(ts)
540 }
541 }
542 impl From<&mut UTCDateTime> for $strukt {
543 fn from(value: &mut UTCDateTime) -> Self {
544 let ts = UnixTimestamp::from(*value);
545 <$strukt>::from(ts)
546 }
547 }
548 impl core::ops::Deref for $strukt {
549 type Target = $inner;
550
551 fn deref(&self) -> &Self::Target {
552 &self.inner
553 }
554 }
555 impl core::ops::Sub for $strukt {
556 type Output = Self;
557
558 fn sub(self, rhs: Self) -> Self::Output {
559 let v = self.inner - rhs.inner;
561 Self {
562 epoch: self.epoch,
563 inner: v,
564 }
565 }
566 }
567 impl core::ops::Sub for &$strukt {
568 type Output = $strukt;
569
570 fn sub(self, rhs: Self) -> Self::Output {
571 let v = self.inner - rhs.inner;
573 Self::Output {
574 epoch: self.epoch,
575 inner: v,
576 }
577 }
578 }
579 impl core::ops::Add<Duration> for $strukt {
580 type Output = Self;
581
582 fn add(self, rhs: Duration) -> Self::Output {
583 let v = self.inner + rhs.as_seconds_f64();
584 Self {
585 epoch: self.epoch,
586 inner: v,
587 }
588 }
589 }
590 impl core::ops::AddAssign<Duration> for $strukt {
591 fn add_assign(&mut self, rhs: Duration) {
592 self.inner += rhs.as_seconds_f64();
593 }
594 }
595 impl core::ops::AddAssign<Duration> for &mut $strukt {
596 fn add_assign(&mut self, rhs: Duration) {
597 self.inner += rhs.as_seconds_f64();
598 }
599 }
600 impl PartialEq for $strukt {
601 fn eq(&self, other: &Self) -> bool {
602 other.as_epoch(self.epoch).inner == self.inner
603 }
604 }
605 impl Eq for $strukt {}
606 impl PartialOrd for $strukt {
607 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
608 Some(self.cmp(&other))
609 }
610 }
611 impl Ord for $strukt {
612 fn cmp(&self, other: &Self) -> Ordering {
613 other.as_epoch(self.epoch).inner.cmp(&self.inner)
614 }
615 }
616 impl Hash for $strukt {
617 fn hash<H: Hasher>(&self, state: &mut H) {
618 self.epoch.hash(state);
619 self.inner.hash(state);
620 }
621 }
622 };
623}
624impls!(Time32, u16, FixedU32);
625impls!(Time64, u32, FixedU64);
626impls!(Time128, u64, FixedU128);