Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/src/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
13 changes: 13 additions & 0 deletions src/uu/uptime/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -36,6 +37,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 }
13 changes: 13 additions & 0 deletions src/uu/uptime/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -36,6 +37,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 }
23 changes: 22 additions & 1 deletion src/uu/uptime/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -266,6 +276,17 @@ fn print_time() {
}

fn print_uptime(boot_time: Option<time_t>) -> UResult<()> {
print!("up {}, ", get_formatted_uptime(boot_time)?);
print!(
"up {}, ",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this needs to be localized (translate!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

get_formatted_uptime(boot_time, OutputFormat::HumanReadable)?
);
Ok(())
}

fn pretty_print_uptime() -> UResult<()> {
println!(
"up {}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

get_formatted_uptime(None, OutputFormat::PrettyPrint)?
);
Ok(())
}
70 changes: 61 additions & 9 deletions src/uucore/src/lib/features/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
9 changes: 9 additions & 0 deletions tests/by-util/test_uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading