Skip to main content

ad_core/
timestamp.rs

1use std::time::{SystemTime, UNIX_EPOCH};
2
3/// EPICS epoch starts at 1990-01-01 00:00:00 UTC.
4const EPICS_EPOCH_OFFSET: u64 = 631_152_000;
5
6/// Lightweight EPICS timestamp.
7#[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}