1use std::time::{SystemTime, UNIX_EPOCH};
2
3const EPICS_EPOCH_OFFSET: u64 = 631_152_000;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub struct EpicsTimestamp {
9 pub sec: u32,
10 pub nsec: u32,
11}
12
13impl EpicsTimestamp {
14 pub fn now() -> Self {
15 Self::from(SystemTime::now())
16 }
17
18 pub fn as_f64(&self) -> f64 {
19 self.sec as f64 + self.nsec as f64 * 1e-9
20 }
21}
22
23impl From<SystemTime> for EpicsTimestamp {
24 fn from(st: SystemTime) -> Self {
25 match st.duration_since(UNIX_EPOCH) {
26 Ok(d) => {
27 let unix_secs = d.as_secs();
28 let epics_secs = unix_secs.saturating_sub(EPICS_EPOCH_OFFSET);
29 Self {
30 sec: epics_secs as u32,
31 nsec: d.subsec_nanos(),
32 }
33 }
34 Err(_) => Self::default(),
35 }
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn test_now_nonzero() {
45 let ts = EpicsTimestamp::now();
46 assert!(ts.sec > 0);
47 }
48
49 #[test]
50 fn test_as_f64() {
51 let ts = EpicsTimestamp {
52 sec: 100,
53 nsec: 500_000_000,
54 };
55 assert!((ts.as_f64() - 100.5).abs() < 1e-9);
56 }
57
58 #[test]
59 fn test_from_system_time() {
60 use std::time::Duration;
61 let st = UNIX_EPOCH + Duration::from_secs(EPICS_EPOCH_OFFSET + 1000);
62 let ts = EpicsTimestamp::from(st);
63 assert_eq!(ts.sec, 1000);
64 assert_eq!(ts.nsec, 0);
65 }
66
67 #[test]
68 fn test_default_is_zero() {
69 let ts = EpicsTimestamp::default();
70 assert_eq!(ts.sec, 0);
71 assert_eq!(ts.nsec, 0);
72 }
73}