Differences between revisions 15 and 16
Revision 15 as of 2018-04-01 23:56:23
Size: 4663
Editor: pool-71-179-2-155
Comment: add printf's '%(fmt)T'
Revision 16 as of 2018-04-02 12:49:27
Size: 4758
Editor: GreyCat
Comment: portability note; also, while POSIX says %F exists, some systems don't have it yet; reduce silly indentation
Deletions are marked like this. Additions are marked like this.
Line 10: Line 10:
   # GNU/BSD date
   date +%s # Prints the current time in Unix format, e.g. 1164128484
   date -u +%s # Seconds from Epoch AT UTC. This clears local Daytime savings or local time corrections.
# GNU/BSD date
date +%s # Prints the current time in Unix format, e.g. 1164128484
date -u +%s # Seconds from Epoch AT UTC. This clears local Daytime savings or local time corrections.
Line 18: Line 18:
   # POSIX shell, with GNU/BSD date
   start=$(date -u +%s)
   ...
   end=$(date -u +%s)
   echo "Operation took $(($end - $start)) seconds."
# POSIX shell, with GNU/BSD date
start=$(date -u +%s)
...
end=$(date -u +%s)
echo "Operation took $(($end - $start)) seconds."
Line 27: Line 27:
   # GNU date
   date -u -d "1970-01-01" +"%s seconds" # Prints "0 seconds"
   date -u -d "1970-01-01" +"%D %T" # Prints "01/01/70 00:00:00"
# GNU date
date -u -d "1970-01-01" +"%s seconds" # Prints "0 seconds"
date -u -d "1970-01-01" +"%D %T" # Prints "01/01/70 00:00:00"
Line 31: Line 31:
   date -u -d "1970-01-01 14415 sec" +"%D %T"
   date -u -d @14415 +"%D %T" # Alternative notation
               # Prints "01/01/70 04:00:15", or 4 hours and 15 seconds ahead.
   date -u -d "1970-01-01 14415 sec - 3605 sec" +"%D %T"
               # Prints "01/01/70 03:00:10", that is, 4 hours 15 seconds ahead and 2 hours 5 seconds back.
date -u -d "1970-01-01 14415 sec" +"%D %T"
date -u -d @14415 +"%D %T" # Alternative notation
            # Prints "01/01/70 04:00:15", or 4 hours and 15 seconds ahead.
date -u -d "1970-01-01 14415 sec - 3605 sec" +"%D %T"
            # Prints "01/01/70 03:00:10", that is, 4 hours 15 seconds ahead
            #
and 2 hours 5 seconds back.
Line 40: Line 41:
   # Assuming start is "1418347200" and end is "1418350815" (for example):
   date -u -d "1970-01-01 $end sec - $start sec" +"%T"
   # Prints the time difference in the usual (human readable) time format:
   01:00:15
   # the output format could be easily adjusted as needed.
# Assuming start is "1418347200" and end is "1418350815" (for example):
date -u -d "1970-01-01 $end sec - $start sec" +"%T"

# Prints the time difference in the usual (human readable) time format:
01:00:15
# the output format could be easily adjusted as needed.
Line 49: Line 51:
With a relatively modern version of bash (4.2 and later), printf has the %(fmt)T option. It can be used to both get the current time in unix format, and to convert from unix format back to a human readable time. "fmt" will take anything that is valid for strftime(3): With a relatively modern version of bash (4.2 and later), `printf` has the `%(fmt)T` option. It can be used to both get the current time in unix format, and to convert from unix format back to a human readable time. "fmt" will take anything that is valid for `strftime(3)` (which means this basically only works on BSD and GNU systems):
Line 52: Line 54:
  printf -v start '%(%s)T' -1 # store the current time in "$start" # store the current epoch time in "$start" (BSD/GNU only)
printf -v start '%(%s)T' -1
Line 54: Line 57:
  printf '%(%F %T)T\n' "$start" # print the date back in a human readable format. # print the saved epoch time in a human readable format (portable)
printf '%(%Y-%m-%d %H:%M:%S)T\n' "$start"
Line 60: Line 64:
   perl -le "print scalar localtime 1164128484"
   # Prints "Tue Nov 21 12:01:24 2006"
perl -le "print scalar localtime 1164128484"
# Prints "Tue Nov 21 12:01:24 2006"
Line 70: Line 74:
   echo 'puts [clock format [clock scan "today"]]' | tclsh
   # Prints today's date (the format can be adjusted with parameters to "clock format").
echo 'puts [clock format [clock scan "today"]]' | tclsh
# Prints today's date (the format can be adjusted with parameters to "clock format").
Line 73: Line 77:
   echo 'puts [clock format [clock scan "fortnight"]]' | tclsh
   # Prints the date two weeks from now.
echo 'puts [clock format [clock scan "fortnight"]]' | tclsh
# Prints the date two weeks from now.
Line 76: Line 80:
   echo 'puts [clock format [clock scan "5 years + 6 months ago"]]' | tclsh
   # Five and a half years ago, compensating for leap days and daylight savings time.
echo 'puts [clock format [clock scan "5 years + 6 months ago"]]' | tclsh
# Five and a half years ago, compensating for leap days and daylight savings time.
Line 80: Line 84:
A convenient way of calculating seconds elapsed since 'YYYY MM DD HH MM SS' is to use awk. A convenient way of calculating seconds elapsed since 'YYYY MM DD HH MM SS' is to use GNU `awk`:
Line 83: Line 87:
   echo "2008 02 27 18 50 23" | awk '{print systime() - mktime($0)}'
   # This uses systime() to return the current time in epoch format
   # It then performs mktime() on the input string to return the epoch time of the input string
echo "2008 02 27 18 50 23" | awk '{print systime() - mktime($0)}'
# Use systime() to return the current time in epoch format
# Use mktime() on the input string to return the epoch time of the input string
# These are both GNU awk extensions; mawk may also work.
Line 88: Line 93:
To make this more human readable, GNU awk (gawk) can be used.
The [[http://www.gnu.org/software/gawk/manual/gawk.html#Time-Functions|format string]] is similar to GNU date's.
To make this more human readable, GNU `awk`'s `strftime()` may be used.
The [[http://www.gnu.org/software/gawk/manual/gawk.html#Time-Functions|format string]] is similar to that of GNU `date`.
Line 92: Line 97:
   echo "YYYY MM DD HH MM SS" | gawk '{print strftime("%M Minutes, %S Seconds",systime() - mktime($0))}'
   # The gawk-specific strftime() function converts the difference into a human readable format
echo "YYYY MM DD HH MM SS" | gawk '{print strftime("%M Minutes, %S Seconds",systime() - mktime($0))}'
# The gawk-specific strftime() function converts the difference into a human readable format

How do I convert Unix (epoch) times to human-readable values?

The only sane way to handle time values within a program is to convert them into a linear scale. You can't store "January 17, 2005 at 5:37 PM" in a variable and expect to do anything with it....

Therefore, any competent program is going to use time stamps with semantics such as "the number of seconds since point X". These are called epoch timestamps. If the epoch is January 1, 1970 at midnight UTC, then it's also called a "Unix timestamp", because this is how Unix stores all times (such as file modification times).

The closest tool to deal with Unix timestamps in Standard Unix, is only the command date. (Ironic, eh?) GNU date, and later BSD date, has a %s extension to generate output in Unix timestamp format:

# GNU/BSD date
date +%s       # Prints the current time in Unix format, e.g. 1164128484
date -u +%s    # Seconds from Epoch AT UTC. This clears local Daytime savings or local time corrections.

This is commonly used in scripts when one requires the interval between two events:

# POSIX shell, with GNU/BSD date
start=$(date -u +%s)
...
end=$(date -u +%s)
echo "Operation took $(($end - $start)) seconds."

Now, to convert those Unix timestamps back into human-readable values, we need to use date in a special way. The GNU date could perform simple adition and substraction :

# GNU date
date -u -d "1970-01-01" +"%s seconds"      # Prints "0 seconds"
date -u -d "1970-01-01" +"%D %T"           # Prints "01/01/70 00:00:00"

date -u -d "1970-01-01 14415 sec" +"%D %T"
date -u -d @14415 +"%D %T"   # Alternative notation
            # Prints "01/01/70 04:00:15", or 4 hours and 15 seconds ahead.
date -u -d "1970-01-01 14415 sec - 3605 sec" +"%D %T"
            # Prints "01/01/70 03:00:10", that is, 4 hours 15 seconds ahead
            # and 2 hours 5 seconds back.

So, we can do in just one command (provided start and end vars are values in seconds):

# Assuming start is "1418347200" and end is "1418350815" (for example):
date -u -d "1970-01-01 $end sec - $start sec" +"%T"

# Prints the time difference in the usual (human readable) time format:
01:00:15
# the output format could be easily adjusted as needed.

Note that this works only for less than 24 hours. Bigger time spans will require some external math.

With a relatively modern version of bash (4.2 and later), printf has the %(fmt)T option. It can be used to both get the current time in unix format, and to convert from unix format back to a human readable time. "fmt" will take anything that is valid for strftime(3) (which means this basically only works on BSD and GNU systems):

# store the current epoch time in "$start" (BSD/GNU only)
printf -v start '%(%s)T' -1

# print the saved epoch time in a human readable format (portable)
printf '%(%Y-%m-%d %H:%M:%S)T\n' "$start"

If you don't have GNU date or a modern version of bash available, you can use Perl:

perl -le "print scalar localtime 1164128484"
# Prints "Tue Nov 21 12:01:24 2006"

I used double quotes in these examples so that the time constant could be replaced with a variable reference. See the documentation for date(1) and Perl for details on changing the output format.

Newer versions of Tcl (8.5 and higher) have very good support of date and clock functions. For example:

echo 'puts [clock format [clock scan "today"]]' | tclsh
# Prints today's date (the format can be adjusted with parameters to "clock format").
   
echo 'puts [clock format [clock scan "fortnight"]]' | tclsh
# Prints the date two weeks from now.
   
echo 'puts [clock format [clock scan "5 years + 6 months ago"]]' | tclsh
# Five and a half years ago, compensating for leap days and daylight savings time.

A convenient way of calculating seconds elapsed since 'YYYY MM DD HH MM SS' is to use GNU awk:

echo "2008 02 27 18 50 23" | awk '{print systime() - mktime($0)}'
# Use systime() to return the current time in epoch format
# Use mktime() on the input string to return the epoch time of the input string
# These are both GNU awk extensions; mawk may also work.

To make this more human readable, GNU awk's strftime() may be used. The format string is similar to that of GNU date.

echo "YYYY MM DD HH MM SS" | gawk '{print strftime("%M Minutes, %S Seconds",systime() - mktime($0))}'
# The gawk-specific strftime() function converts the difference into a human readable format

BashFAQ/070 (last edited 2018-04-02 12:49:27 by GreyCat)