uptime: Add -p, --pretty argument (#10143)

* uptime: Add -p, --pretty argument

Rebase of PR https://github.com/uutils/coreutils/pull/7910
All credits go to GitHub User: https://github.com/irbeam256

* Add localization for "up"
This commit is contained in:
Ivan-Shaml
2026-01-11 10:20:46 +01:00
committed by GitHub
parent 4e0062d213
commit 91edf285b2
6 changed files with 123 additions and 11 deletions
+3 -1
View File
@@ -184,7 +184,9 @@ also provides a `-v`/`--verbose` flag.
## `uptime`
Similar to the proc-ps implementation and unlike GNU/Coreutils, `uptime` provides `-s`/`--since` to show since when the system is up.
Similar to the proc-ps implementation and unlike GNU/Coreutils, `uptime` provides:
* `-s`/`--since` to show since when the system is up
* `-p`/`--pretty` to display uptime in a pretty-printed format
## `base32/base64/basenc`
+14
View File
@@ -9,6 +9,7 @@ uptime-about-musl-warning = Warning: When built with musl libc, the `uptime` uti
# Help messages
uptime-help-since = system up since
uptime-help-path = file to search boot time from
uptime-help-pretty = show uptime in pretty format
# Error messages
uptime-error-io = couldn't get boot time: { $error }
@@ -18,6 +19,7 @@ uptime-error-couldnt-get-boot-time = couldn't get boot time
# Output messages
uptime-output-unknown-uptime = up ???? days ??:??,
uptime-output-up-text = up
uptime-user-count = { $count ->
[one] 1 user
@@ -36,6 +38,18 @@ uptime-format = { $days ->
[one] { $days } day, { $time }
*[other] { $days } days { $time }
}
uptime-format-pretty-min = { $min ->
[one] { $min } minute
*[other] { $min } minutes
}
uptime-format-pretty-hour = { $hour ->
[one] { $hour } hour
*[other] { $hour } hours
}
uptime-format-pretty-day = { $day ->
[one] { $day } day
*[other] { $day } days
}
# Load average formatting
uptime-lib-format-loadavg = load average: { $avg1 }, { $avg5 }, { $avg15 }
+14
View File
@@ -9,6 +9,7 @@ uptime-about-musl-warning = Avertissement : Lorsque compilé avec musl libc, l'u
# Messages d'aide
uptime-help-since = système actif depuis
uptime-help-path = fichier pour rechercher l'heure de démarrage
uptime-help-pretty = afficher le temps de disponibilité dans un format agréable
# Messages d'erreur
uptime-error-io = impossible d'obtenir l'heure de démarrage : { $error }
@@ -18,6 +19,7 @@ uptime-error-couldnt-get-boot-time = impossible d'obtenir l'heure de démarrage
# Messages de sortie
uptime-output-unknown-uptime = actif ???? jours ??:??,
uptime-output-up-text = actif
uptime-user-count = { $count ->
[one] 1 utilisateur
@@ -36,6 +38,18 @@ uptime-format = { $days ->
[one] { $days } jour, { $time }
*[other] { $days } jours { $time }
}
uptime-format-pretty-min = { $min ->
[one] { $min } minute
*[other] { $min } minutes
}
uptime-format-pretty-hour = { $hour ->
[one] { $hour } heure
*[other] { $hour } heures
}
uptime-format-pretty-day = { $day ->
[one] { $day } jour
*[other] { $day } jours
}
# Formatage de la charge moyenne
uptime-lib-format-loadavg = charge moyenne : { $avg1 }, { $avg5 }, { $avg15 }
+22 -1
View File
@@ -27,6 +27,7 @@ use uucore::utmpx::*;
pub mod options {
pub static SINCE: &str = "since";
pub static PATH: &str = "path";
pub static PRETTY: &str = "pretty";
}
#[derive(Debug, Error)]
@@ -57,6 +58,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if matches.get_flag(options::SINCE) {
uptime_since()
} else if matches.get_flag(options::PRETTY) {
pretty_print_uptime()
} else if let Some(path) = file_path {
uptime_with_file(path)
} else {
@@ -92,6 +95,13 @@ pub fn uu_app() -> Command {
.value_parser(ValueParser::os_string())
.value_hint(ValueHint::AnyPath),
)
.arg(
Arg::new(options::PRETTY)
.short('p')
.long(options::PRETTY)
.help(translate!("uptime-help-pretty"))
.action(ArgAction::SetTrue),
)
}
#[cfg(unix)]
@@ -266,6 +276,17 @@ fn print_time() {
}
fn print_uptime(boot_time: Option<time_t>) -> UResult<()> {
print!("up {}, ", get_formatted_uptime(boot_time)?);
let localized_text = translate!("uptime-output-up-text");
let uptime_message = get_formatted_uptime(boot_time, OutputFormat::HumanReadable)?;
print!("{localized_text} {uptime_message}, ");
Ok(())
}
fn pretty_print_uptime() -> UResult<()> {
let localized_text = translate!("uptime-output-up-text");
let uptime_message = get_formatted_uptime(None, OutputFormat::PrettyPrint)?;
println!("{localized_text} {uptime_message}");
Ok(())
}
+61 -9
View File
@@ -205,6 +205,56 @@ pub fn get_uptime(boot_time: Option<time_t>) -> UResult<i64> {
Err(UptimeError::SystemUptime)?
}
/// The format used to display a FormattedUptime.
pub enum OutputFormat {
/// Typical `uptime` output (e.g. 2 days, 3:04).
HumanReadable,
/// Pretty printed output (e.g. 2 days, 3 hours, 04 minutes).
PrettyPrint,
}
struct FormattedUptime {
up_days: i64,
up_hours: i64,
up_mins: i64,
}
impl FormattedUptime {
fn new(up_secs: i64) -> Self {
let up_days = up_secs / 86400;
let up_hours = (up_secs - (up_days * 86400)) / 3600;
let up_mins = (up_secs - (up_days * 86400) - (up_hours * 3600)) / 60;
Self {
up_days,
up_hours,
up_mins,
}
}
fn get_human_readable_uptime(&self) -> String {
translate!(
"uptime-format",
"days" => self.up_days,
"time" => format!("{:02}:{:02}", self.up_hours, self.up_mins))
}
fn get_pretty_print_uptime(&self) -> String {
let mut parts = Vec::new();
if self.up_days > 0 {
parts.push(translate!("uptime-format-pretty-day", "day" => self.up_days));
}
if self.up_hours > 0 {
parts.push(translate!("uptime-format-pretty-hour", "hour" => self.up_hours));
}
if self.up_mins > 0 || parts.is_empty() {
parts.push(translate!("uptime-format-pretty-min", "min" => self.up_mins));
}
parts.join(", ")
}
}
/// Get the system uptime
///
/// # Arguments
@@ -227,26 +277,28 @@ pub fn get_uptime(_boot_time: Option<time_t>) -> UResult<i64> {
/// # Arguments
///
/// boot_time: Option<time_t> - Manually specify the boot time, or None to try to get it from the system.
/// output_format: OutputFormat - Selects the format of the output string.
///
/// # Returns
///
/// Returns a UResult with the uptime in a human-readable format(e.g. "1 day, 3:45") if successful, otherwise an UptimeError.
#[inline]
pub fn get_formatted_uptime(boot_time: Option<time_t>) -> UResult<String> {
pub fn get_formatted_uptime(
boot_time: Option<time_t>,
output_format: OutputFormat,
) -> UResult<String> {
let up_secs = get_uptime(boot_time)?;
if up_secs < 0 {
Err(UptimeError::SystemUptime)?;
}
let up_days = up_secs / 86400;
let up_hours = (up_secs - (up_days * 86400)) / 3600;
let up_mins = (up_secs - (up_days * 86400) - (up_hours * 3600)) / 60;
Ok(translate!(
"uptime-format",
"days" => up_days,
"time" => format!("{up_hours:02}:{up_mins:02}")
))
let formatted_uptime = FormattedUptime::new(up_secs);
match output_format {
OutputFormat::HumanReadable => Ok(formatted_uptime.get_human_readable_uptime()),
OutputFormat::PrettyPrint => Ok(formatted_uptime.get_pretty_print_uptime()),
}
}
/// Get the number of users currently logged in
+9
View File
@@ -267,6 +267,15 @@ fn test_uptime_since() {
new_ucmd!().arg("--since").succeeds().stdout_matches(&re);
}
#[test]
fn test_uptime_pretty_print() {
new_ucmd!()
.arg("-p")
.succeeds()
.stdout_contains("up")
.stdout_contains("minute");
}
/// Test uptime reliability on macOS with sysctl kern.boottime fallback.
/// This addresses intermittent failures from issue #3621 by ensuring
/// the command consistently succeeds when utmpx data is unavailable.