1use std::fmt::Display;
2const ONE_MINUTE: u64 = 60;
3const ONE_HOUR: u64 = 60 * 60;
4const ONE_DAY: u64 = 60 * 60 * 24;
5
6#[derive(Debug, Clone, Copy)]
7pub struct Time(pub u64);
8
9impl Display for Time {
10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11 if self.0 < ONE_MINUTE {
12 return f.write_str(&with_unit(self.0, "sec"));
13 }
14
15 if self.0 < ONE_HOUR {
16 let min = self.0 / ONE_MINUTE;
17 let sec = self.0 - min * ONE_MINUTE;
18 return f.write_str(&format!(
19 "{} {}",
20 with_unit(min, "min"),
21 with_unit(sec, "sec")
22 ));
23 }
24
25 if self.0 < ONE_DAY {
26 let hour = self.0 / ONE_HOUR;
27 let min = (self.0 - hour * ONE_HOUR) / ONE_MINUTE;
28 let sec = self.0 - hour * ONE_HOUR - min * ONE_MINUTE;
29 return f.write_str(&format!(
30 "{}, {}, {}",
31 with_unit(hour, "hour"),
32 with_unit(min, "min"),
33 with_unit(sec, "sec")
34 ));
35 }
36 let day = self.0 / ONE_DAY;
37 let hour = (self.0 - day * ONE_DAY) / ONE_HOUR;
38 let min = (self.0 - day * ONE_DAY - hour * ONE_HOUR) / ONE_MINUTE;
39 f.write_str(&format!(
40 "{}, {}, {}",
41 with_unit(day, "day"),
42 with_unit(hour, "hour"),
43 with_unit(min, "min")
44 ))
45 }
46}
47
48fn with_unit(n: u64, unit: &str) -> String {
49 format!("{n} {unit}{}", if n > 1 { "s" } else { "" })
50}
51
52#[cfg(windows)]
53pub async fn get_uptime() -> crate::error::Result<Time> {
54 use chrono::TimeZone;
55 use chrono::Utc;
56 use chrono::{FixedOffset, NaiveDateTime};
57 use serde::Deserialize;
58
59 use crate::platform::wmi_query;
60
61 #[derive(Deserialize, Debug, Clone)]
62 #[serde(rename = "Win32_OperatingSystem")]
63 struct OperatingSystem {
64 #[serde(rename = "LastBootUpTime")]
65 last_boot_up_time: String,
66 }
67
68 let results: Vec<OperatingSystem> = wmi_query().await?;
69 let input = results
71 .first()
72 .map(|i| i.last_boot_up_time.clone())
73 .ok_or_else(|| crate::error::NeofetchError::data_unavailable("Boot time not found"))?;
74
75 let now = Utc::now();
76
77 if input.len() < 21 {
78 return Err(crate::error::NeofetchError::parse_error(
79 "boot_time",
80 "Invalid boot time format",
81 ));
82 }
83
84 let datetime_str = &input[..21]; let offset_str = &input[21..]; let naive_dt = NaiveDateTime::parse_from_str(datetime_str, "%Y%m%d%H%M%S%.f")
88 .map_err(|e| crate::error::NeofetchError::parse_error("boot_time", e.to_string()))?;
89
90 let offset_minutes: i32 = offset_str.parse().map_err(|e| {
91 crate::error::NeofetchError::parse_error("timezone_offset", format!("{}", e))
92 })?;
93
94 let offset = FixedOffset::east_opt(offset_minutes * 60).ok_or_else(|| {
95 crate::error::NeofetchError::parse_error("timezone_offset", "Invalid offset")
96 })?;
97
98 let datetime_with_tz = offset
99 .from_local_datetime(&naive_dt)
100 .unwrap()
101 .with_timezone(&Utc);
102
103 let uptime = now - datetime_with_tz;
104 let uptime_seconds = uptime.num_seconds() as u64;
105
106 Ok(Time(uptime_seconds))
107}
108
109#[cfg(any(target_os = "linux", target_os = "android"))]
110pub async fn get_uptime() -> crate::error::Result<Time> {
111 let mut info: libc::sysinfo = unsafe { std::mem::zeroed() };
112 let result = unsafe { libc::sysinfo(&mut info) };
113 if result != 0 {
114 return Err(crate::error::NeofetchError::system_call(
115 "Failed to get system uptime from sysinfo",
116 ));
117 }
118 Ok(Time(info.uptime as u64))
119}
120
121#[cfg(target_os = "macos")]
122pub async fn get_uptime() -> crate::error::Result<Time> {
123 let mut mib = [libc::CTL_KERN as i32, libc::KERN_BOOTTIME as i32];
124 let mut boot_time: libc::timeval = unsafe { std::mem::zeroed() };
125 let mut size = std::mem::size_of::<libc::timeval>();
126
127 let result = unsafe {
128 libc::sysctl(
129 mib.as_mut_ptr(),
130 2,
131 &mut boot_time as *mut _ as *mut _,
132 &mut size,
133 std::ptr::null_mut(),
134 0,
135 )
136 };
137 if result != 0 {
138 return Err(crate::error::NeofetchError::system_call(
139 "Failed to get boot time from sysctl",
140 ));
141 }
142
143 let mut current_time = unsafe { std::mem::zeroed() };
145 let time_result = unsafe { libc::gettimeofday(&mut current_time, std::ptr::null_mut()) };
146 if time_result != 0 {
147 return Err(crate::error::NeofetchError::system_call(
148 "Failed to get current time from gettimeofday",
149 ));
150 }
151
152 let uptime = current_time.tv_sec - boot_time.tv_sec;
154 Ok(Time(uptime as u64))
155}