rusty_bin/monitor/utils/
time.rs1use crate::monitor::storage::naming::SimpleDate;
7use quanta::Clock;
8use rusty_common::time::days_since_epoch_to_date;
9use std::time::{SystemTime, UNIX_EPOCH};
10
11pub struct TimingUtils {
13 clock: Clock,
15}
16
17impl TimingUtils {
18 #[must_use]
20 pub fn new() -> Self {
21 Self {
22 clock: Clock::new(),
23 }
24 }
25
26 pub fn now_nanos(&self) -> u64 {
29 rusty_common::time::get_epoch_timestamp_ns()
30 }
31
32 pub fn now_millis(&self) -> u64 {
34 self.now_nanos() / 1_000_000
35 }
36
37 pub fn now_seconds(&self) -> u64 {
39 self.now_nanos() / 1_000_000_000
40 }
41
42 pub fn monotonic_nanos(&self) -> u64 {
46 self.clock.raw() }
48
49 pub fn monotonic_millis(&self) -> u64 {
53 self.monotonic_nanos() / 1_000_000
54 }
55
56 pub fn system_time_to_nanos(time: SystemTime) -> u64 {
61 rusty_common::time::system_time_to_nanos(time)
62 }
63
64 pub fn current_date_string() -> String {
66 let now = SystemTime::now();
67 let duration = now.duration_since(UNIX_EPOCH).unwrap_or_else(|e| {
68 log::error!("CRITICAL: System clock error: {e}. Using fallback date.");
69 std::time::Duration::from_secs(0)
72 });
73 Self::duration_to_date_string(duration)
74 }
75
76 pub fn current_datetime_string() -> String {
78 let now = SystemTime::now();
79 let duration = now.duration_since(UNIX_EPOCH).unwrap_or_else(|e| {
80 log::error!("CRITICAL: System clock error: {e}. Using fallback datetime.");
81 std::time::Duration::from_secs(0)
84 });
85 Self::duration_to_datetime_string(duration)
86 }
87
88 pub fn is_new_day(last_timestamp_nanos: u64, current_timestamp_nanos: u64) -> bool {
90 let last_date = Self::nanos_to_date_string(last_timestamp_nanos);
91 let current_date = Self::nanos_to_date_string(current_timestamp_nanos);
92 last_date != current_date
93 }
94
95 pub fn nanos_to_date_string(timestamp_nanos: u64) -> String {
97 let duration = std::time::Duration::from_nanos(timestamp_nanos);
98 Self::duration_to_date_string(duration)
99 }
100
101 pub fn nanos_to_datetime_string(timestamp_nanos: u64) -> String {
103 let duration = std::time::Duration::from_nanos(timestamp_nanos);
104 Self::duration_to_datetime_string(duration)
105 }
106
107 fn duration_to_date_string(duration: std::time::Duration) -> String {
109 let days = duration.as_secs() / 86400; let (year, month, day) = days_since_epoch_to_date(days);
111 format!("{year:04}{month:02}{day:02}")
112 }
113
114 fn duration_to_datetime_string(duration: std::time::Duration) -> String {
116 let total_secs = duration.as_secs();
117 let days = total_secs / 86400;
118 let remaining_secs = total_secs % 86400;
119 let hours = remaining_secs / 3600;
120 let minutes = (remaining_secs % 3600) / 60;
121 let seconds = remaining_secs % 60;
122 let millis = duration.subsec_millis();
123
124 let (year, month, day) = days_since_epoch_to_date(days);
125 format!("{year:04}-{month:02}-{day:02}T{hours:02}:{minutes:02}:{seconds:02}.{millis:03}Z")
126 }
127
128 const fn is_leap_year(year: u32) -> bool {
130 year.is_multiple_of(4) && (!year.is_multiple_of(100) || year.is_multiple_of(400))
131 }
132
133 pub fn days_ago(days: u32) -> SimpleDate {
135 let now = SystemTime::now();
136 let duration = now.duration_since(UNIX_EPOCH).unwrap_or_else(|e| {
137 log::error!(
138 "CRITICAL: System clock error: {e}. Using current epoch time for date calculation."
139 );
140 std::time::Duration::from_secs(0)
142 });
143 let current_days = duration.as_secs() / 86400;
144 let target_days = current_days.saturating_sub(days as u64);
145 let (year, month, day) = days_since_epoch_to_date(target_days);
146 SimpleDate::new(year, month, day).unwrap_or_else(|| {
147 log::error!("CRITICAL: Failed to create valid date from calculated values: year={year}, month={month}, day={day}. Using epoch date.");
148 SimpleDate::new(1970, 1, 1).unwrap()
149 })
150 }
151}
152
153impl Default for TimingUtils {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159static TIMING: std::sync::OnceLock<TimingUtils> = std::sync::OnceLock::new();
161
162pub fn timing() -> &'static TimingUtils {
164 TIMING.get_or_init(TimingUtils::new)
165}
166
167pub fn now_nanos() -> u64 {
169 timing().now_nanos()
170}
171
172pub fn now_millis() -> u64 {
174 timing().now_millis()
175}
176
177pub fn now_seconds() -> u64 {
179 timing().now_seconds()
180}
181
182pub fn monotonic_nanos() -> u64 {
186 timing().monotonic_nanos()
187}
188
189pub fn monotonic_millis() -> u64 {
193 timing().monotonic_millis()
194}
195
196pub fn current_date() -> String {
198 TimingUtils::current_date_string()
199}
200
201pub fn days_ago(days: u32) -> SimpleDate {
203 TimingUtils::days_ago(days)
204}
205
206pub fn current_datetime() -> String {
208 TimingUtils::current_datetime_string()
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_timing_utils() {
217 let timing = TimingUtils::new();
218
219 let nanos = timing.now_nanos();
220 let millis = timing.now_millis();
221 let seconds = timing.now_seconds();
222
223 assert!(nanos > 0);
224 assert!(millis > 0);
225 assert!(seconds > 0);
226
227 assert!(nanos / 1_000_000 >= millis - 1); assert!(millis / 1_000 >= seconds - 1);
230 }
231
232 #[test]
233 fn test_date_formatting() {
234 let date = TimingUtils::current_date_string();
235 assert_eq!(date.len(), 8); let datetime = TimingUtils::current_datetime_string();
238 assert!(datetime.contains('T'));
239 assert!(datetime.ends_with('Z'));
240 }
241
242 #[test]
243 fn test_timestamp_conversion() {
244 let timestamp_nanos = 1_640_995_200_000_000_000; let date = TimingUtils::nanos_to_date_string(timestamp_nanos);
246 assert_eq!(date, "20220101");
247
248 let datetime = TimingUtils::nanos_to_datetime_string(timestamp_nanos);
249 assert!(datetime.starts_with("2022-01-01T00:00:00"));
250 }
251
252 #[test]
253 fn test_new_day_detection() {
254 let day1 = 1_640_995_200_000_000_000; let day2 = 1_641_081_600_000_000_000; assert!(!TimingUtils::is_new_day(day1, day1 + 3_600_000_000_000)); assert!(TimingUtils::is_new_day(day1, day2)); }
260}