aede_core/clock.rs
1//! Unix timestamps shared by catalog and conclusion stores.
2//!
3//! Small enough to be tempting to rewrite on the spot — which is exactly what
4//! had happened, in three places, each with its own answer to "and if the clock
5//! is before 1970?". A scan date, an integrity date and an import date have to
6//! be comparable, so they come from here.
7
8use std::time::{SystemTime, UNIX_EPOCH};
9
10/// Now, in seconds since the Unix epoch.
11///
12/// A clock set before 1970 yields `0` rather than an error: nothing in the
13/// catalog can act on that failure, and a date of zero reads as "unknown"
14/// everywhere a date is shown.
15pub fn now_seconds() -> u64 {
16 SystemTime::now()
17 .duration_since(UNIX_EPOCH)
18 .map(|d| d.as_secs())
19 .unwrap_or(0)
20}
21
22/// Now with subsecond precision, for ordering analysis results produced in
23/// quick succession. File identity stores seconds and the fractional part
24/// separately so both integers survive the JSON representation exactly.
25pub fn now_nanoseconds() -> u64 {
26 nanoseconds(SystemTime::now())
27}
28
29/// A report file's modification time, with the same precision as new results.
30pub fn mtime_nanoseconds(meta: &std::fs::Metadata) -> u64 {
31 meta.modified().map(nanoseconds).unwrap_or(0)
32}
33
34fn nanoseconds(time: SystemTime) -> u64 {
35 time.duration_since(UNIX_EPOCH)
36 .ok()
37 .and_then(|duration| u64::try_from(duration.as_nanos()).ok())
38 .unwrap_or(0)
39}
40
41/// Modification date of a file, in seconds since the Unix epoch.
42///
43/// The scanner also compares [`mtime_subseconds`]. Keeping the parts separate
44/// avoids rounding epoch nanoseconds through JSON's floating-point numbers.
45pub fn mtime_seconds(meta: &std::fs::Metadata) -> u64 {
46 meta.modified()
47 .ok()
48 .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
49 .map(|d| d.as_secs())
50 .unwrap_or(0)
51}
52
53/// Nanosecond fraction of a file's modification date, or `0` when unavailable.
54pub fn mtime_subseconds(meta: &std::fs::Metadata) -> u32 {
55 meta.modified()
56 .ok()
57 .and_then(|time| time.duration_since(UNIX_EPOCH).ok())
58 .map(|duration| duration.subsec_nanos())
59 .unwrap_or(0)
60}