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