Is there any easy way to calculate uptime excluding period system is suspended? I would like to count time I've spent in front of computer.
-
3var/log/pm-suspend.log, will contain the last awake time ,using this ,uptime and current time you can manually find real uptime with subtraction.Guess this is not the easy way!! – Stormvirux Jul 19 '13 at 12:38
-
@Stormvirux I can write script but it is the last resort – teodozjan Jul 19 '13 at 13:22
-
1@Stormvirux I had the same idea, but wish I had found your suggestion first instead of figuring that out six years later! – WinEunuuchs2Unix Nov 07 '19 at 02:01
-
The next time you sign on, let us know how you eventually ended up solving this question. Thanks. – WinEunuuchs2Unix May 03 '20 at 22:33
2 Answers
On one hand this answer is six years late, on the other hand it's a blink of the eye if the internet is eternal!
You can get the real uptime with this little bash script:
$ suspendtime
Apr 07 05:53:34 to Apr 07 17:07:17 suspended 11 hours, 13 minutes, 43 seconds
Apr 07 21:56:34 to Apr 08 04:20:57 suspended 6 hours, 24 minutes, 23 seconds
(... SNIP ...)
May 08 05:55:20 to May 08 16:32:37 suspended 10 hours, 37 minutes, 17 seconds
May 08 23:21:00 to May 09 07:02:05 suspended 7 hours, 41 minutes, 5 seconds
Linux uptime 2,813,939 seconds (4 weeks, 4 days, 13 hours, 38 minutes, 59 seconds)
64 Suspends 1,715,196 seconds (2 weeks, 5 days, 20 hours, 26 minutes, 36 seconds)
Real uptime 1,098,743 seconds (1 week, 5 days, 17 hours, 12 minutes, 23 seconds)
Linux will report uptime as 8 days and 40 minutes. The real uptime (after subtracting suspend time) is about 2 days and 18 hours.
suspendtime
bash script
Here's the code you can copy to your system:
#!/bin/bash
NAME: suspendtime
PATH: $HOME/askubuntu/
DESC: For: https://askubuntu.com/questions/321855/how-to-get-real-uptime
DATE: November 6, 2019.
NOTE: Calculate suspend time from systemd's journalctl
UPDT: 2019-11-07 Fine-tune removing 0 Units in DaysMinutesStr
2020-05-09 Add "weeks" unit measure to DaysMinutes() function
Duplicate DaysMinutes from ~/.bashrc for Ask Ubuntu
DaysMinutes () {
local w d h m s
(( w = ${1} / 604800 ))
(( d = ${1}%604800 / 86400 ))
(( h = (${1}%86400) / 3600 ))
(( m = (${1}%3600) / 60 ))
(( s = ${1}%60 ))
DaysMinutesStr="$w weeks, $d days, $h hours, $m minutes, $s seconds"
# Convert 1's to singular
[[ ${DaysMinutesStr:0:2} = "1 " ]] && \
DaysMinutesStr="${DaysMinutesStr/weeks/week}"
DaysMinutesStr="${DaysMinutesStr/ 1 days/ 1 day}"
DaysMinutesStr="${DaysMinutesStr/ 1 hours/ 1 hour}"
DaysMinutesStr="${DaysMinutesStr/ 1 minutes/ 1 minute}"
DaysMinutesStr="${DaysMinutesStr/ 1 seconds/ 1 second}"
# Suppress zero strings
[[ ${DaysMinutesStr:0:1} = "0" ]] &&
DaysMinutesStr="${DaysMinutesStr/0 weeks, / }"
DaysMinutesStr="${DaysMinutesStr/ 0 days, / }"
DaysMinutesStr="${DaysMinutesStr/ 0 hours, / }"
DaysMinutesStr="${DaysMinutesStr/ 0 minutes, / }"
DaysMinutesStr="${DaysMinutesStr/, 0 seconds/}"
} # DaysMinutes
Build array of suspend cycles from Systemd
IFS=$'\n' Arr=( $(journalctl -b-0 |
grep -E 'systemd[1]: Start.*Suspend' | cut -c1-15) )
[[ ${#Arr[@]} -gt 0 ]] && upper=$(( ${#Arr[@]} - 1 ))
[[ $upper -gt 0 ]] && for (( i=0; i<upper; i=i+2 )) ; do
(( SuspendCount++ ))
Time=$(( $(date +%s -d "${Arr[i+1]}") - $(date +%s -d "${Arr[i]}") ))
SuspendTime=$(( SuspendTime + Time ))
DaysMinutes "$Time"
printf "%s to %s suspended%s\n" "${Arr[i]}" "${Arr[i+1]}"
"$DaysMinutesStr"
done
echo
LinuxTime=$(( $(date +%s -d "Now") - $(date +%s -d "$(uptime -s)") ))
DaysMinutes "$LinuxTime"
printf "Linux uptime %'d seconds (%s)\n" "$LinuxTime" "$DaysMinutesStr"
DaysMinutes "$SuspendTime"
printf "%s Suspends %'d seconds (%s)\n"
"$SuspendCount" "$SuspendTime" "$DaysMinutesStr"
RealTime=$(( LinuxTime - SuspendTime ))
DaysMinutes "$RealTime"
printf "Real uptime %'d seconds (%s)\n" "$RealTime" "$DaysMinutesStr"
About half the program is converting seconds to human readable format in weeks, days, hours, minutes and seconds. The function DaysMinutes
does this and was copied from my ~/.bashrc
file where you may want to put that function yourself.
How it works
The key component is getting suspend start and end times from journalctl
:
$ journalctl -b-0 | grep -E 'systemd\[1]: Start.*Suspend'
Oct 31 05:55:19 alien systemd[1]: Starting Suspend...
Oct 31 16:54:26 alien systemd[1]: Started Suspend.
(... SNIP ...)
Nov 07 21:07:28 alien systemd[1]: Starting Suspend...
Nov 08 05:08:52 alien systemd[1]: Started Suspend.
- The
journalctl -b-0
command reads all the system messages for the current boot. You could enhance the function to look at the previous boot using-b-1
the boot before that with-b-2
, etc. - The
grep
command with regex does the heavy lifting returning 32 system messages pertaining to suspend from the 19,330 system messages recorded (on my system)

- 102,282
-
It seems to work not anymore in 20.04 ?!? Build array of suspend cycles from Systemd seems not to work. echo $Arr Mai 03 09:06:53 Mai 03 09:19:28 Mai 03 10:10:09 but next command - what this should be result with '\n' ahead? – Arno May 03 '20 at 08:32
-
@Arno Can we first confirm what systemd returns when you use the example above of:
journalctl -b-0 | grep -E 'systemd\[1]: Start.*Suspend'
? It should show the times going to sleep and waking up. – WinEunuuchs2Unix May 03 '20 at 14:37 -
Journal has been changed: Better is: "IFS=$'\n' Arr=( $(journalctl -b-0 | grep -E 'PM: suspend' | cut -c1-15) )", but date command gives me "wrong date"... – Arno May 03 '20 at 16:13
-
date +%s -d "Mai 03 18:00:00" date: ungültiges Datum »Mai 03 18:00:00“ (or translated invalid date) – Arno May 03 '20 at 16:37
-
@Arno I can only test my own code not your code I can't see. I will have to upgrade my test partition to 20.04 and test how systemd has changed in that version. Life is busy with SARS-CoV-2 research and I still haven't installed the 32GB Ram upgrade I bought just before the March 28, 2020 lock down. If I don't reply with an updated answer in 1 week please send me a reminder. Thanks. – WinEunuuchs2Unix May 03 '20 at 17:08
-
I need to convert the date "Mai 03 18:00:00" to "2020-05-03 18:00:00". Other date formats I can't convert to seconds. I thought I can do that with a format string like +"%b %d %H:%M:%S", but that doesn't work. -- like that -- {date +%s -d "Mai 03 18:00:00" +"%b %d %H:%M:%S"} – Arno May 03 '20 at 17:23
-
2022: around line 42 should be changed to
IFS=$'\n' Arr=( $(journalctl -b-0 | \ grep -E 'systemd\[1]: (Starting|Finished).*Suspend' | cut -c1-15) )
– Superbiji Jul 06 '22 at 10:00 -
1@Superbiji I'm still in 2016 and just tested the script and it works fine. I plan on installing 22.04.1 and hope to remember to test the script again. Thanks. – WinEunuuchs2Unix Jul 07 '22 at 00:23
After years the above script requires an update. systemd messages and date command are not compatible anymore - as explained in the comments to the upper script. So I take that one as a base and modified it. Journal returns date as "Mai 03 ..." which can't be handled by date. To work around I faked the year and month - so it will not count properly if month is changed during standby .... Maybe someone will change that.
#!/bin/bash
# NAME: suspendtime
# PATH: $HOME/askubuntu/
# DESC: For: https://askubuntu.com/questions/321855/how-to-get-real-uptime
# DATE: November 6, 2019.
# NOTE: Calculate suspend time from systemd's journalctl
# UPDT: 2019-11-07 Fine-tune removing 0 Units in DaysMinutesStr
# UPDT: 2020-05-05 works with Ubuntu 20.04 but has problem with changing monthes
# Duplicate DaysMinutes from ~/.bashrc for Ask Ubuntu
DaysMinutes () {
local d h m s
(( d = ${1} / 86400 ))
(( h = (${1}%86400) / 3600 ))
(( m = (${1}%3600) / 60 ))
(( s = ${1}%60 ))
DaysMinutesStr="$d days, $h hours, $m minutes, $s seconds"
# Convert 1's to singular
[[ ${DaysMinutesStr:0:2} = "1 " ]] && \
DaysMinutesStr="${DaysMinutesStr/days/day}"
DaysMinutesStr="${DaysMinutesStr/ 1 hours/ 1 hour}"
DaysMinutesStr="${DaysMinutesStr/ 1 minutes/ 1 minute}"
DaysMinutesStr="${DaysMinutesStr/ 1 seconds/ 1 second}"
# Suppress zero strings
[[ ${DaysMinutesStr:0:1} = "0" ]] &&
DaysMinutesStr="${DaysMinutesStr/0 days, / }"
DaysMinutesStr="${DaysMinutesStr/ 0 hours, / }"
DaysMinutesStr="${DaysMinutesStr/ 0 minutes, / }"
DaysMinutesStr="${DaysMinutesStr/, 0 seconds/}"
} # DaysMinutes
# Build array of suspend cycles from Systemd
IFS=$'\n' Arr=( $(journalctl -b-0 | grep -E 'PM: suspend' | cut -c5-15) )
[[ ${#Arr[@]} -gt 0 ]] && upper=$(( ${#Arr[@]} - 1 ))
SuspentCount=0
[[ $upper -gt 0 ]] && for (( i=0; i<upper; i=i+2 )) ; do
(( SuspendCount++ ))
Time=$(( $(date +%s -d "2020-01-${Arr[i+1]}") - $(date +%s -d "2020-01-${Arr[i]}") ))
SuspendTime=$(( SuspendTime + Time ))
printf "%s to %s lasting %'d seconds\n" "${Arr[i]}" "${Arr[i+1]}" "$Time"
done
echo
LinuxTime=$(( $(date +%s -d "Now") - $(date +%s -d "$(uptime -s)") ))
DaysMinutes "$LinuxTime"
printf "Linux uptime \t%'d seconds (%s)\n" "$LinuxTime" "$DaysMinutesStr"
if [ $SuspendCount > 0 ]; then
DaysMinutes "$SuspendTime"
printf "%sx Suspend \t%'d seconds (%s)\n" "$SuspendCount" "$SuspendTime" "$DaysMinutesStr"
fi
RealTime=$(( LinuxTime - SuspendTime ))
DaysMinutes "$RealTime"
printf "Real uptime \t%'d seconds (%s)\n" "$RealTime" "$DaysMinutesStr"

- 485
-
The problem appears to not be with
systemd
in Ubuntu 20.04 but rather with thedate
command in Ubuntu 20.04. I see you've changed this line:Time=$(( $(date +%s -d "${Arr[i+1]}") - $(date +%s -d "${Arr[i]}") ))
because thedate
command isn't working. – WinEunuuchs2Unix May 03 '20 at 22:30 -
True. 3 things I have changed: I go for "PM: suspend" in the journal. Without that change it will only find Starting of suspend, not start and stop. Then I wiped out the month in the log (cut -c4-15) and finally replaced the months by a fixed year and month in different format (Mai 03 -> 2020-05-03) to make the conversion to seconds happen. To improve it it needs some more effort in converting the date. – Arno May 04 '20 at 05:54
-
-
It basically means you read one line at a time. See: https://unix.stackexchange.com/questions/184863/what-is-the-meaning-of-ifs-n-in-bash-scripting – WinEunuuchs2Unix May 04 '20 at 11:24
-
Updated the script - as the previous version show error if no suspend has happened. If someone will take effort to improve month/year transition : welcome. I am atm to lazy to do that. – Arno May 05 '20 at 09:55
-
Because I haven't rebooted my system in 4 weeks, I changed my answer today to include weeks in the time span. – WinEunuuchs2Unix May 10 '20 at 00:11