1use crate::protocol::state_dir;
2use std::path::PathBuf;
3
4pub fn log_dir() -> PathBuf {
5 state_dir().join("logs")
6}
7
8pub fn service_log_dir(service: &str) -> PathBuf {
9 log_dir().join(service)
10}
11
12pub fn current_log_name(process: &str) -> String {
13 let now = now_ymd();
14 format!("{} {}.log", process, now)
15}
16
17pub fn rotated_log_name(process: &str) -> String {
18 let now = now_ymdhm();
19 let (date, hour, minute) = now;
20 let candidate = format!("{} {} {}.log", process, date, hour);
21 let candidate_path = log_dir().join(&candidate);
22 if candidate_path.exists() {
23 format!("{} {} {}.{}.log", process, date, hour, minute)
24 } else {
25 candidate
26 }
27}
28
29pub fn parse_log_date(filename: &str) -> Option<(u32, u32, u32)> {
30 let parts: Vec<&str> = filename.splitn(2, ' ').collect();
32 if parts.len() < 2 {
33 return None;
34 }
35 let rest = parts[1];
36 let date_str = rest
38 .split(' ')
39 .next()
40 .unwrap_or(rest)
41 .trim_end_matches(".log");
42
43 let parts: Vec<&str> = date_str.splitn(2, '-').collect();
45 if parts.len() != 2 {
46 return None;
47 }
48 let year: u32 = parts[0].parse().ok()?;
49 let mmdd = parts[1];
50 if mmdd.len() != 4 {
51 return None;
52 }
53 let month: u32 = mmdd[..2].parse().ok()?;
54 let day: u32 = mmdd[2..].parse().ok()?;
55 Some((year, month, day))
56}
57
58fn now_ymd() -> String {
59 use std::time::SystemTime;
61 let now = SystemTime::now()
62 .duration_since(SystemTime::UNIX_EPOCH)
63 .unwrap()
64 .as_secs();
65 let (year, month, day, _, _) = secs_to_datetime(now);
66 format!("{:02}-{:02}{:02}", year % 100, month, day)
67}
68
69fn now_ymdhm() -> (String, String, String) {
70 use std::time::SystemTime;
71 let now = SystemTime::now()
72 .duration_since(SystemTime::UNIX_EPOCH)
73 .unwrap()
74 .as_secs();
75 let (year, month, day, hour, minute) = secs_to_datetime(now);
76 (
77 format!("{:02}-{:02}{:02}", year % 100, month, day),
78 format!("{:02}", hour),
79 format!("{:02}", minute),
80 )
81}
82
83fn secs_to_datetime(secs: u64) -> (u32, u32, u32, u32, u32) {
84 let days = (secs / 86400) as i64;
86 let time_of_day = secs % 86400;
87 let hour = (time_of_day / 3600) as u32;
88 let minute = ((time_of_day % 3600) / 60) as u32;
89
90 let z = days + 719468;
92 let era = if z >= 0 { z } else { z - 146096 } / 146097;
93 let doe = (z - era * 146097) as u32;
94 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
95 let y = yoe as i64 + era * 400;
96 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
97 let mp = (5 * doy + 2) / 153;
98 let d = doy - (153 * mp + 2) / 5 + 1;
99 let m = if mp < 10 { mp + 3 } else { mp - 9 };
100 let y = if m <= 2 { y + 1 } else { y };
101
102 (y as u32, m, d, hour, minute)
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_parse_log_date() {
111 assert_eq!(parse_log_date("web 26-0214.log"), Some((26, 2, 14)));
112 assert_eq!(parse_log_date("web 26-0214 09.log"), Some((26, 2, 14)));
113 assert_eq!(parse_log_date("web 26-0214 09.47.log"), Some((26, 2, 14)));
114 assert_eq!(parse_log_date("invalid"), None);
115 }
116
117 #[test]
118 fn test_secs_to_datetime() {
119 let (y, m, d, h, min) = secs_to_datetime(1771027200);
121 assert_eq!((y, m, d, h, min), (2026, 2, 14, 0, 0));
122 }
123}