neofetch/
uptime.rs

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    // Format: 20250530024623.265456+480
70    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]; // "20250530024623.265456"
85    let offset_str = &input[21..]; // "+480"
86
87    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    // Get current time
144    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    // Calculate uptime in seconds
153    let uptime = current_time.tv_sec - boot_time.tv_sec;
154    Ok(Time(uptime as u64))
155}