1use std::cmp;
2use std::convert::TryFrom;
3use std::fmt;
4use std::ops;
5use std::ops::AddAssign;
6use std::ops::SubAssign;
7use std::sync::OnceLock;
8use std::sync::RwLock;
9use std::time::SystemTime;
10
11use byteorder::{LittleEndian, ReadBytesExt};
12
13use fog_crypto::serde::{
14 CryptoEnum, FOG_TYPE_ENUM, FOG_TYPE_ENUM_TIME_INDEX, FOG_TYPE_ENUM_TIME_NAME,
15};
16
17use serde::Deserialize;
18use serde::Serialize;
19use serde::{
20 de::{Deserializer, EnumAccess, Error, Unexpected, VariantAccess},
21 ser::{SerializeStructVariant, Serializer},
22};
23use serde_bytes::ByteBuf;
24
25const NTP_EPOCH_OFFSET: i64 = -86400 * (70 * 365 + 17);
26const MAX_NANOSEC: u32 = 999_999_999;
27const NANOS_PER_SEC: u32 = 1_000_000_000;
28const MICROS_PER_SEC: i64 = 1_000_000;
29const MILLIS_PER_SEC: i64 = 1_000;
30static UTC_LEAP: OnceLock<RwLock<LeapSeconds>> = OnceLock::new();
31
32fn get_table() -> std::sync::RwLockReadGuard<'static, LeapSeconds> {
33 let table = UTC_LEAP.get_or_init(|| RwLock::new(LeapSeconds::default()));
34 match table.read() {
35 Ok(o) => o,
36 Err(e) => e.into_inner(),
37 }
38}
39
40fn utc_to_tai(t: Timestamp) -> Timestamp {
42 let table = get_table();
43 t - table.reverse_leap_seconds(t)
44}
45
46fn tai_to_utc(t: Timestamp) -> Timestamp {
48 let table = get_table();
49 t + table.leap_seconds(t)
50}
51
52pub fn set_utc_leap_seconds(table: LeapSeconds) {
56 let store = UTC_LEAP.get_or_init(|| RwLock::new(LeapSeconds::default()));
57 let mut store = match store.write() {
58 Ok(o) => o,
59 Err(e) => e.into_inner(),
60 };
61 *store = table;
62}
63
64#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Hash)]
66pub struct TimeDelta {
67 secs: i64,
68 nanos: u32,
69}
70
71impl TimeDelta {
72 pub fn new(secs: i64, nanos: u32) -> Option<Self> {
75 if nanos > MAX_NANOSEC {
76 return None;
77 }
78 Some(Self { secs, nanos })
79 }
80
81 pub fn from_secs(secs: i64) -> Self {
83 Self { secs, nanos: 0 }
84 }
85
86 pub fn from_millis(millis: i64) -> Self {
88 Self {
89 secs: millis.div_euclid(MILLIS_PER_SEC),
90 nanos: millis.rem_euclid(MILLIS_PER_SEC) as u32,
91 }
92 }
93
94 pub fn from_micros(micros: i64) -> Self {
96 Self {
97 secs: micros.div_euclid(MICROS_PER_SEC),
98 nanos: micros.rem_euclid(MICROS_PER_SEC) as u32,
99 }
100 }
101
102 pub fn from_nanos(nanos: i64) -> Self {
104 Self {
105 secs: nanos.div_euclid(NANOS_PER_SEC as i64),
106 nanos: nanos.rem_euclid(NANOS_PER_SEC as i64) as u32,
107 }
108 }
109
110 pub fn subsec_nanos(&self) -> u32 {
112 self.nanos
113 }
114
115 pub fn as_secs(&self) -> i64 {
119 self.secs
120 }
121}
122
123impl ops::AddAssign<TimeDelta> for TimeDelta {
124 fn add_assign(&mut self, rhs: TimeDelta) {
125 self.nanos += rhs.nanos;
126 self.secs += rhs.secs;
127 if self.nanos >= NANOS_PER_SEC {
128 self.nanos -= NANOS_PER_SEC;
129 self.secs += 1;
130 }
131 }
132}
133
134impl ops::Add<TimeDelta> for TimeDelta {
135 type Output = TimeDelta;
136 fn add(mut self, rhs: TimeDelta) -> Self::Output {
137 self.add_assign(rhs);
138 self
139 }
140}
141
142impl ops::SubAssign<TimeDelta> for TimeDelta {
143 fn sub_assign(&mut self, rhs: TimeDelta) {
144 if self.nanos < rhs.nanos {
145 self.nanos += NANOS_PER_SEC;
146 self.secs -= 1;
147 }
148 self.nanos -= rhs.nanos;
149 self.secs -= rhs.secs;
150 }
151}
152
153impl ops::Sub<TimeDelta> for TimeDelta {
154 type Output = TimeDelta;
155 fn sub(mut self, rhs: TimeDelta) -> Self::Output {
156 self.sub_assign(rhs);
157 self
158 }
159}
160
161impl ops::Neg for TimeDelta {
162 type Output = TimeDelta;
163 fn neg(mut self) -> Self::Output {
164 self.secs = -self.secs;
165 if self.nanos != 0 {
166 self.nanos = NANOS_PER_SEC - self.nanos;
167 self.secs -= 1;
168 }
169 self
170 }
171}
172
173#[derive(Clone, Debug)]
189pub struct LeapSeconds(pub Vec<(Timestamp, TimeDelta)>);
190
191impl Default for LeapSeconds {
192 fn default() -> Self {
193 let file = include_str!("leap-seconds.list");
194 Self::from_ntp_file(file).unwrap()
195 }
196}
197
198impl LeapSeconds {
199 pub fn new(table: Vec<(Timestamp, TimeDelta)>) -> Self {
204 Self(table)
205 }
206
207 pub fn reverse_leap_seconds(&self, t: Timestamp) -> TimeDelta {
212 for leap_second in self.0.iter().rev() {
213 if (t - leap_second.1) >= leap_second.0 {
214 return leap_second.1;
215 }
216 }
217 TimeDelta::default()
218 }
219
220 pub fn leap_seconds(&self, t: Timestamp) -> TimeDelta {
223 for leap_second in self.0.iter().rev() {
224 if t >= leap_second.0 {
225 return leap_second.1;
226 }
227 }
228 TimeDelta::default()
229 }
230
231 pub fn from_ntp_file(file: &str) -> Option<Self> {
245 let mut table = Vec::new();
246 for line in file.lines() {
247 if let Some(first_char) = line.chars().next() {
248 if first_char == '#' {
249 continue;
250 } else {
251 let mut data = line.split_whitespace();
252
253 let Some(secs_utc) = data.next() else { return None };
255 let Ok(secs_utc) = str::parse::<i64>(secs_utc) else { return None };
256
257 let Some(delta) = data.next() else { return None };
259 let Ok(delta) = str::parse::<i64>(delta) else { return None };
260
261 let time = Timestamp::from_tai_secs(secs_utc + delta + NTP_EPOCH_OFFSET);
263 let delta = TimeDelta::from_secs(-delta);
264 table.push((time, delta));
265 }
266 }
267 }
268 Some(LeapSeconds(table))
269 }
270}
271
272#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
292pub struct Timestamp {
293 secs: i64,
294 nanos: u32,
295}
296
297impl Timestamp {
298 pub fn from_tai(secs: i64, nanos: u32) -> Option<Timestamp> {
302 if nanos > MAX_NANOSEC {
303 return None;
304 }
305 Some(Timestamp { secs, nanos })
306 }
307
308 pub fn from_utc(secs: i64, nanos: u32) -> Option<Timestamp> {
318 if nanos > MAX_NANOSEC {
319 return None;
320 }
321 Some(utc_to_tai(Timestamp { secs, nanos }))
322 }
323
324 pub fn from_utc_secs(secs: i64) -> Timestamp {
327 utc_to_tai(Timestamp { secs, nanos: 0 })
328 }
329
330 pub fn from_tai_secs(secs: i64) -> Timestamp {
333 Timestamp { secs, nanos: 0 }
334 }
335
336 pub const fn zero() -> Timestamp {
338 Timestamp { secs: 0, nanos: 0 }
339 }
340
341 pub const fn min_value() -> Timestamp {
343 Timestamp {
344 secs: i64::MIN,
345 nanos: 0,
346 }
347 }
348
349 pub const fn max_value() -> Timestamp {
351 Timestamp {
352 secs: i64::MAX,
353 nanos: MAX_NANOSEC,
354 }
355 }
356
357 pub fn min(self, other: Timestamp) -> Timestamp {
359 if self < other {
360 self
361 } else {
362 other
363 }
364 }
365
366 pub fn max(self, other: Timestamp) -> Timestamp {
368 if self > other {
369 self
370 } else {
371 other
372 }
373 }
374
375 pub fn next(mut self) -> Timestamp {
377 if self.nanos < MAX_NANOSEC {
378 self.nanos += 1;
379 } else {
380 self.nanos = 0;
381 self.secs += 1;
382 }
383 self
384 }
385
386 pub fn prev(mut self) -> Timestamp {
388 if self.nanos > 0 {
389 self.nanos -= 1;
390 } else {
391 self.nanos = MAX_NANOSEC;
392 self.secs -= 1;
393 }
394 self
395 }
396
397 pub fn utc(&self) -> (i64, u32) {
405 let t = tai_to_utc(*self);
406 (t.secs, t.nanos)
407 }
408
409 pub fn tai_secs(&self) -> i64 {
411 self.secs
412 }
413
414 pub fn tai_subsec_nanos(&self) -> u32 {
416 self.nanos
417 }
418
419 pub fn time_since(&self, other: &Timestamp) -> TimeDelta {
422 let rhs = TimeDelta {
423 secs: other.secs,
424 nanos: other.nanos,
425 };
426 let new = *self - rhs;
427 TimeDelta {
428 secs: new.secs,
429 nanos: new.nanos,
430 }
431 }
432
433 pub fn as_vec(&self) -> Vec<u8> {
436 let mut v = Vec::new();
437 self.encode_vec(&mut v);
438 v
439 }
440
441 pub fn encode_vec(&self, vec: &mut Vec<u8>) {
451 if self.nanos != 0 {
452 vec.reserve(8 + 4);
453 vec.extend_from_slice(&self.secs.to_le_bytes());
454 vec.extend_from_slice(&self.nanos.to_le_bytes());
455 } else if (self.secs <= u32::MAX as i64) && (self.secs >= 0) {
456 vec.reserve(4);
457 vec.extend_from_slice(&(self.secs as u32).to_le_bytes());
458 } else {
459 vec.reserve(8);
460 vec.extend_from_slice(&self.secs.to_le_bytes());
461 }
462 }
463
464 pub fn size(&self) -> usize {
467 if self.nanos != 0 {
468 8 + 4
469 } else if (self.secs <= u32::MAX as i64) && (self.secs >= 0) {
470 4
471 } else {
472 8
473 }
474 }
475
476 pub fn now() -> Timestamp {
478 Timestamp::from(SystemTime::now())
479 }
480}
481
482impl From<SystemTime> for Timestamp {
483 fn from(value: SystemTime) -> Self {
484 let t = value.duration_since(SystemTime::UNIX_EPOCH).unwrap();
485 Timestamp::from_utc(t.as_secs() as i64, t.subsec_nanos()).unwrap()
486 }
487}
488
489impl ops::Add<i64> for Timestamp {
490 type Output = Timestamp;
491 fn add(mut self, rhs: i64) -> Self {
492 self.secs += rhs;
493 self
494 }
495}
496
497impl ops::AddAssign<i64> for Timestamp {
498 fn add_assign(&mut self, rhs: i64) {
499 self.secs += rhs;
500 }
501}
502
503impl ops::Sub<i64> for Timestamp {
504 type Output = Timestamp;
505 fn sub(mut self, rhs: i64) -> Self {
506 self.secs -= rhs;
507 self
508 }
509}
510
511impl ops::SubAssign<i64> for Timestamp {
512 fn sub_assign(&mut self, rhs: i64) {
513 self.secs -= rhs;
514 }
515}
516
517impl ops::Add<TimeDelta> for Timestamp {
518 type Output = Timestamp;
519 fn add(mut self, rhs: TimeDelta) -> Timestamp {
520 self += rhs;
521 self
522 }
523}
524
525impl ops::AddAssign<TimeDelta> for Timestamp {
526 fn add_assign(&mut self, rhs: TimeDelta) {
527 self.nanos += rhs.nanos;
528 if self.nanos >= NANOS_PER_SEC {
529 self.nanos -= NANOS_PER_SEC;
530 self.secs += 1;
531 }
532 self.secs += rhs.secs;
533 }
534}
535
536impl ops::Sub<TimeDelta> for Timestamp {
537 type Output = Timestamp;
538 fn sub(mut self, rhs: TimeDelta) -> Timestamp {
539 self -= rhs;
540 self
541 }
542}
543
544impl ops::SubAssign<TimeDelta> for Timestamp {
545 fn sub_assign(&mut self, rhs: TimeDelta) {
546 if self.nanos < rhs.nanos {
547 self.nanos += NANOS_PER_SEC;
548 self.secs -= 1;
549 }
550 self.nanos -= rhs.nanos;
551 self.secs -= rhs.secs;
552 }
553}
554
555impl ops::Sub<Timestamp> for Timestamp {
556 type Output = TimeDelta;
557 fn sub(self, rhs: Timestamp) -> TimeDelta {
558 self.time_since(&rhs)
559 }
560}
561
562impl cmp::Ord for Timestamp {
563 fn cmp(&self, other: &Timestamp) -> cmp::Ordering {
564 match self.secs.cmp(&other.secs) {
565 cmp::Ordering::Equal => self.nanos.cmp(&other.nanos),
566 other => other,
567 }
568 }
569}
570
571impl cmp::PartialOrd for Timestamp {
572 fn partial_cmp(&self, other: &Timestamp) -> Option<cmp::Ordering> {
573 Some(self.cmp(other))
574 }
575}
576
577impl fmt::Display for Timestamp {
578 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579 write!(f, "TAI: {} secs + {} ns", self.secs, self.nanos)
580 }
581}
582
583impl TryFrom<&[u8]> for Timestamp {
586 type Error = String;
587 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
588 let mut raw = value;
589 let (secs, nanos) = match value.len() {
590 12 => {
591 let secs = raw.read_i64::<LittleEndian>().unwrap();
592 let nanos = raw.read_u32::<LittleEndian>().unwrap();
593 (secs, nanos)
594 }
595 8 => {
596 let secs = raw.read_i64::<LittleEndian>().unwrap();
597 (secs, 0)
598 }
599 4 => {
600 let secs = raw.read_u32::<LittleEndian>().unwrap() as i64;
601 (secs, 0)
602 }
603 _ => {
604 return Err(format!(
605 "not a recognized Timestamp length ({} bytes)",
606 value.len()
607 ))
608 }
609 };
610 Ok(Timestamp { secs, nanos })
611 }
612}
613
614impl serde::ser::Serialize for Timestamp {
615 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
616 where
617 S: Serializer,
618 {
619 if serializer.is_human_readable() {
620 let mut sv = serializer.serialize_struct_variant(
622 FOG_TYPE_ENUM,
623 FOG_TYPE_ENUM_TIME_INDEX as u32,
624 FOG_TYPE_ENUM_TIME_NAME,
625 2,
626 )?;
627 sv.serialize_field("secs", &self.secs)?;
630 sv.serialize_field("nanos", &self.nanos)?;
631 sv.end()
632 } else {
633 let value = ByteBuf::from(self.as_vec());
635 serializer.serialize_newtype_variant(
636 FOG_TYPE_ENUM,
637 FOG_TYPE_ENUM_TIME_INDEX as u32,
638 FOG_TYPE_ENUM_TIME_NAME,
639 &value,
640 )
641 }
642 }
643}
644
645impl<'de> serde::de::Deserialize<'de> for Timestamp {
646 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
647 where
648 D: Deserializer<'de>,
649 {
650 struct TimeVisitor {
651 is_human_readable: bool,
652 }
653
654 impl<'de> serde::de::Visitor<'de> for TimeVisitor {
655 type Value = Timestamp;
656
657 fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
658 write!(
659 fmt,
660 "{} enum with variant {} (id {})",
661 FOG_TYPE_ENUM, FOG_TYPE_ENUM_TIME_NAME, FOG_TYPE_ENUM_TIME_INDEX
662 )
663 }
664
665 fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
666 where
667 A: EnumAccess<'de>,
668 {
669 let variant = match data.variant()? {
670 (CryptoEnum::Time, variant) => variant,
671 (e, _) => {
672 return Err(A::Error::invalid_type(
673 Unexpected::Other(e.as_str()),
674 &"Time",
675 ))
676 }
677 };
678 if self.is_human_readable {
679 use serde::de::MapAccess;
680 struct TimeStructVisitor;
681 impl<'de> serde::de::Visitor<'de> for TimeStructVisitor {
682 type Value = Timestamp;
683 fn expecting(
684 &self,
685 fmt: &mut fmt::Formatter<'_>,
686 ) -> Result<(), fmt::Error> {
687 write!(fmt, "timestamp struct")
688 }
689
690 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
691 where
692 A: MapAccess<'de>,
693 {
694 let mut secs: Option<i64> = None;
695 let mut nanos: u32 = 0;
696 while let Some(key) = map.next_key::<String>()? {
697 match key.as_ref() {
698 "std" => {
699 let v: u8 = map.next_value()?;
700 if v != 0 {
701 return Err(A::Error::invalid_value(
702 Unexpected::Unsigned(v as u64),
703 &"0",
704 ));
705 }
706 }
707 "secs" => {
708 secs = Some(map.next_value()?);
709 }
710 "nanos" => {
711 nanos = map.next_value()?;
712 }
713 _ => {
714 return Err(A::Error::unknown_field(
715 key.as_ref(),
716 &["std", "secs", "nanos"],
717 ))
718 }
719 }
720 }
721 let secs = secs.ok_or_else(|| A::Error::missing_field("secs"))?;
722 Timestamp::from_tai(secs, nanos)
723 .ok_or_else(|| A::Error::custom("Invalid timestamp"))
724 }
725 }
726 variant.struct_variant(&["std", "secs", "nanos"], TimeStructVisitor)
727 } else {
728 let bytes: ByteBuf = variant.newtype_variant()?;
729 Timestamp::try_from(bytes.as_ref()).map_err(A::Error::custom)
730 }
731 }
732 }
733
734 let is_human_readable = deserializer.is_human_readable();
735 deserializer.deserialize_enum(
736 FOG_TYPE_ENUM,
737 &[FOG_TYPE_ENUM_TIME_NAME],
738 TimeVisitor { is_human_readable },
739 )
740 }
741}
742
743#[cfg(test)]
744mod test {
745 use super::*;
746
747 fn edge_cases() -> Vec<(usize, Timestamp)> {
748 vec![
749 (4, Timestamp::from_tai(0, 0).unwrap()),
750 (4, Timestamp::from_tai(1, 0).unwrap()),
751 (12, Timestamp::from_tai(1, 1).unwrap()),
752 (4, Timestamp::from_tai(u32::MAX as i64 - 1, 0).unwrap()),
753 (4, Timestamp::from_tai(u32::MAX as i64, 0).unwrap()),
754 (8, Timestamp::from_tai(u32::MAX as i64 + 1, 0).unwrap()),
755 (8, Timestamp::from_tai(i64::MIN, 0).unwrap()),
756 (12, Timestamp::from_tai(i64::MIN, 1).unwrap()),
757 ]
758 }
759
760 #[test]
761 fn roundtrip() {
762 for (index, case) in edge_cases().iter().enumerate() {
763 println!(
764 "Test #{}: '{}' with expected length = {}",
765 index, case.1, case.0
766 );
767 let mut enc = Vec::new();
768 case.1.encode_vec(&mut enc);
769 assert_eq!(enc.len(), case.0);
770 let decoded = Timestamp::try_from(enc.as_ref()).unwrap();
771 assert_eq!(decoded, case.1);
772 }
773 }
774
775 #[test]
776 fn too_long() {
777 for case in edge_cases() {
778 println!("Test with Timestamp = {}", case.1);
779 let mut enc = Vec::new();
780 case.1.encode_vec(&mut enc);
781 enc.push(0u8);
782 assert!(Timestamp::try_from(enc.as_ref()).is_err());
783 }
784 }
785
786 #[test]
787 fn too_short() {
788 for case in edge_cases() {
789 println!("Test with Timestamp = {}", case.1);
790 let mut enc = Vec::new();
791 case.1.encode_vec(&mut enc);
792 enc.pop();
793 assert!(Timestamp::try_from(enc.as_ref()).is_err());
794 }
795 }
796
797 #[test]
798 fn leap_seconds() {
799 let table = LeapSeconds::default();
800 let (tai_time, diff) = table.0.last().unwrap();
801 assert_eq!(*diff, TimeDelta::from_secs(-37));
802 assert_eq!(tai_time.utc().0, 3692217600 + NTP_EPOCH_OFFSET);
803 assert_eq!(tai_time.utc().1, 0);
804 for i in -5..=5 {
805 let time = *tai_time + i;
808 let utc = time.utc();
809 let time2 = Timestamp::from_utc(utc.0, utc.1).unwrap();
810 if i == -1 {
811 assert_eq!(
812 time,
813 time2 - 1,
814 "Failed for offset of {}, expected a diff of 1",
815 i
816 );
817 } else {
818 assert_eq!(time, time2, "Failed for offset of {}, expected no diff", i);
819 }
820
821 let utc = tai_time.tai_secs() - diff.as_secs() + i;
823 let tai = Timestamp::from_utc_secs(utc);
824 let utc2 = tai.utc();
825 assert_eq!(utc2.0, utc, "Failed for offset of {}, expected no diff", i);
826 assert_eq!(
827 utc2.1, 0,
828 "Failed for offset of {}, expected 0 ns for UTC",
829 i
830 );
831 }
832 }
833
834 #[test]
835 fn check_diffs() {
836 let time = Timestamp::from_tai(5, 5).unwrap();
837 let time2 = Timestamp::from_tai(6, 1).unwrap();
838 let diff = time2.time_since(&time);
839 let diff2 = time.time_since(&time2);
840 let neg_diff2 = -diff2;
841 let neg_diff3 = -neg_diff2;
842 assert_eq!(diff, neg_diff2);
843 assert_eq!(diff2, neg_diff3);
844 }
845}