Search code examples
bashwhile-looparithmetic-expressions

subtracting two lists of timestamps from each other in bash


I have a script that checks my logs for the timestamps of when the application has gone down and back up (availability of the app).
I want to find the difference between a list of timestamps then add up all of those difference so I know a total amount of time the app has been down. So the downtime.txt file has a list like this:
04:55:51
05:41:51
and the uptime.txt has a list like the same format:
04:56:59
05:42:21

If I didn't need to convert the timestamps into numbers for arithmetic I think I could
paste downtime.txt uptime.txt | awk '{print $1 - $2}'>timedown.txt
or something like that. How can I read the timestamps, convert it to a number, subtract the matching lines from the two files, then add up all the sums from those lines?


Solution

  • You can use the date command to convert timestamps. It's unfortunate your timestamps don't have dates on them, not sure what happens when you roll over past midnight, but assuming you don't have that problem, you can choose the fixed date "01-Jan-1970 UTC" for calculation purposes.

    Here is your code:

    paste downtime.txt uptime.txt | while read d u; do echo $(( $(date -d "01-Jan-1970 UTC $u" +%s) - $(date -d "01-Jan-1970 UTC $d" +%s) )); done
    

    Explanation: The date command converts the timestamps into seconds. The -d option means, act on the following date instead of "now". So we give it a date using your input files, assuming that the times specified are from midnight. Since date works on the basis of seconds since 01 Jan 1970 UTC 00:00:00, we add that date to simplify the result. The +%s parameter means, tell me how many seconds it is since 01-Jan-1970. This is where the conversion comes in. Since we specified -d, it uses the timestamp you specified instead of "now". So the value of $(date -d "01-Jan-1970 UTC $u" +%s) is the number of seconds since midnight for the uptime. Then we subtract the downtime seconds from the uptime seconds using $(( ... )) to get the number of seconds between the two timestamps. (If your bash doesn't have that function, you can use $(expr $(date -d "01-Jan-1970 UTC $u" +%s) - $(date -d "01-Jan-1970 UTC $d" +%s) ) instead).

    UPDATE: I should finish the job. To accumulate and count the total time, you can add | awk '{total=total+$1} END {print $total}'. To convert this back into hours and minutes, use date again; use the -u option to prevent conversion to local time, the -d option with @ to specify number of seconds (again we are using 01-Jan-1970 as a base, that's what @ means), and +%T to convert into hours and minutes, though if it's more than 24 hours you'll lose the extra days.

    date -u -d @$(paste downtime.txt uptime.txt | while read d u; do echo $(( $(date -d "01-Jan-1970 UTC $u" +%s) - $(date -d "01-Jan-1970 UTC $d" +%s) )); done | awk '{total=total+$1} END {print total}') +%T