简体   繁体   中英

Bash - convert time interval string to nr. of seconds

I'm trying to convert strings, describing a time interval, to the corresponding number of seconds.

After some experimenting I figured out that I can use date like this:

soon=$(date -d '5 minutes 10 seconds' +%s); now=$(date +%s)
echo $(( $soon-$now ))

but I think there should be an easier way to convert strings like "5 minutes 10 seconds" to the corresponding number of seconds, in this example 310. Is there a way to do this in one command?

Note: although portability would be useful, it isn't my top priority.

You could start at epoch

date -d"1970-01-01 00:00:00 UTC 5 minutes 10 seconds" "+%s"
310

You could also easily sub in times

Time="1 day"
date -d"1970-01-01 00:00:00 UTC $Time" "+%s"
86400

There is one way to do it, without using date command in pure bash (for portability)

Assuming you just have an input string to convert "5 minutes 10 seconds" in a bash variable with a : de-limiter as below.

$ convertString="00:05:10"
$ IFS=: read -r hour minute second <<< "$convertString"
$ secondsValue=$(((hour * 60 + minute) * 60 + second))
$ printf "%s\n" "$secondsValue"
310

You can run the above commands directly on the command-line without the $ mark.

This will do (add the epoch 19700101):

$ date -ud '19700101 5 minutes 10 seconds' +%s
310

It is important to add a -u to avoid local time (and DST) effects.

$ TZ=America/Los_Angeles date -d '19700101 5 minutes 10 seconds' +%s
29110

Note that date could do some math:

$ date -ud '19700101 +5 minutes 10 seconds -47 seconds -1 min' +%s
203

The previous suggestions didn't work properly on alpine linux, so here's a small helper function that is POSIX compliant, is easy to use and also supports calculations (just as a side effect of the implementation).

The function always returns an integer based on the provided parameters.

$ durationToSeconds '<value>' '<fallback>'

$ durationToSeconds "1h 30m"
5400

$ durationToSeconds "$someemptyvar" 1h
3600

$ durationToSeconds "$someemptyvar" "1h 30m"
5400

# Calculations also work
$ durationToSeconds "1h * 3"
10800

$ durationToSeconds "1h - 1h"
0

# And also supports long forms for year, day, hour, minute, second
$ durationToSeconds "3 days 1 hour"
262800

# It's also case insensitive
$ durationToSeconds "3 Days"
259200
function durationToSeconds () {
  set -f
  normalize () { echo $1 | tr '[:upper:]' '[:lower:]' | tr -d "\"\\\'" | sed 's/years\{0,1\}/y/g; s/months\{0,1\}/m/g; s/days\{0,1\}/d/g; s/hours\{0,1\}/h/g; s/minutes\{0,1\}/m/g; s/min/m/g; s/seconds\{0,1\}/s/g; s/sec/s/g;  s/ //g;'; }
  local value=$(normalize "$1")
  local fallback=$(normalize "$2")

  echo $value | grep -v '^[-+*/0-9ydhms]\{0,30\}$' > /dev/null 2>&1
  if [ $? -eq 0 ]
  then
    >&2 echo Invalid duration pattern \"$value\"
  else
    if [ "$value" = "" ]; then
      [ "$fallback" != "" ] && durationToSeconds "$fallback"
    else
      sedtmpl () { echo "s/\([0-9]\+\)$1/(0\1 * $2)/g;"; }
      local template="$(sedtmpl '\( \|$\)' 1) $(sedtmpl y '365 * 86400') $(sedtmpl d 86400) $(sedtmpl h 3600) $(sedtmpl m 60) $(sedtmpl s 1) s/) *(/) + (/g;"
      echo $value | sed "$template" | bc
    fi
  fi
  set +f
}

Edit : Yes. I developed for OP after comment and checked on Mac OS X, CentOS and Ubuntu. One liner, POSIX compliant command for converting "X minutes Y seconds" format to seconds. That was the question .

echo $(($(echo "5 minutes 10 seconds" | cut -c1-2)*60  + $(echo "5 minutes 10 seconds" | cut -c1-12 | awk '{print substr($0,11)}')))

OP told me via comment that he wants for "X minutes Y seconds" format not for HH:MM:SS format. The command with date and "+%s" is throwing error on (my) Mac. OP wanted to grab the numerical values from "X minutes Y seconds" format and convert it to seconds. First I extracted the minute in digit (take it as equation A) :

echo "5 minutes 10 seconds" | cut -c1-2)

then I extracted the seconds part (take it as equation B) :

echo "5 minutes 10 seconds" | cut -c1-12 | awk '{print substr($0,11)}'

Now multiply minute by 60 then add with the other :

echo $((equation A)*60) + (equation B))

OP should ask the others to check my developmental version (but working) of command before using it for automatic repeated usage like we do with cron on a production server.

If we want to run this on a log file with values in "X minutes Y seconds" format, we have to change echo "5 minutes 10 seconds" to cat file | ... cat file | ... like command. I kept a gist of it too if I or others ever need we can use it with cat to run on server log files with x minutes y seconds like log format.

Although off-topic (what I understood, question has not much to do with current time), this is not working for POSIX-compliant OS to get current time in seconds :

date -d "1970-01-01 00:00:00 UTC 5 minutes 10 seconds" "+%s"

It will throw error on MacOS X but work on most GNU/Linux distro. That +%s part will throw error on POSIX-compliant OS upon complicated usage. These commands are mostly suitable to get current time in seconds on POSIX compliant to any kind of unix like OS :

awk 'BEGIN{srand(); print srand()}'
perl -le 'print time' 

If OP needs can extend it by generating current time in seconds and subtract. I hope it will help.

---- OLD Answer before EDIT ----

You can get the current time without that date -- echo | awk '{print systime();}' echo | awk '{print systime();}' or wget -qO- http://www.timeapi.org/utc/now?\\\\s . Other way to convert time to second is echo "00:20:40.25" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' echo "00:20:40.25" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' .

The example with printf shown in another answer is near perfect.

That thing you want is always needed by the basic utilities of GNU/Linux - gnu.org/../../../../../Setting-an-Alarm.html

Way to approach really depends how much foolproof way you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM