Reputation: 523
I had two datetime strings like below...
2014-09-03T02:23:09Z
and 2014-09-03T03:24:57Z
Now, I have to assign the two datetimes to some variables and compare them, like this:
d1=2014-09-03T02:23:09Z
d2=2014-09-03T03:24:57Z
if (d1 < d2 )
I browsed many websites but couldn't find a solution. I tried the approach below but it doesn't work.
#set -vx
date1=`date -d "2014-09-03T04:27:23Z" +"%Y-%m-%dT%TZ"`
date2=`date -d "2014-09-03T02:23:09Z" +"%Y-%m-%dT%TZ"`
date3=`date -d "2014-09-03T05:23:09Z" +"%Y-%m-%dT%TZ"`
#date1=`date -d "2014-09-03T04:27:23Z"`
#date2=`date -d "2014-09-03T02:23:09Z"`
#date3=`date -d "2014-09-03T05:23:09Z"`
if [ ${date3} -lt ${date1} ] ; then
echo "working"
else
echo "gone"
fi
if [ ${date2} -gt ${date1} ] ; then
echo "working"
else
echo "gone"
fi
I am facing the below error:
sh compare.sh
date: invalid date `2014-09-03T04:27:23Z'
date: invalid date `2014-09-03T02:23:09Z'
date: invalid date `2014-09-03T05:23:09Z'
working
working
Can someone please guide me on how to do datetime comparison in Unix?
Upvotes: 0
Views: 2182
Reputation: 753990
Values in the ISO 8601 date format can be compared as strings when they're all in the same time zone (Z for Zulu or UTC in the example); that's one of the primary merits of the format.
The problem with if [ "${date3}" < "${date1}" ]
notation is that you're doing input redirection in the middle of your test — which is not what you intend.
If you have Bash, you can use [[
in place of [
:
if [[ "${date3}" < "${date1}" ]]
Alternatively, your test
(or [
) command may accept:
if [ "${date3}" '<' "${date1}" ]
The quotes around the comparison prevent it being interpreted as I/O redirection.
(This works on Mac OS X 10.9.4, but that's using bash
again. However, /bin/[
accepts it too, provided I omit the trailing ]
— it must be because it is not invoked as [
that it requires the absence of ]
.) POSIX test
is not required to support >
or <
as operators — only =
and !=
are required for string comparison.
The classic command for the task is expr
:
if [ $(expr "${date3}" '<' "${date1}") == 1 ]
This is probably the most portable mechanism (7th Edition Unix had expr
for this task). It is also the clumsiest notation.
I'm not clear that there is anything wrong with your date expressions per se, but the fact that they give you date: invalid date '2014-09-03T04:27:23Z'
errors suggests that the version of date
available to you does not support the notation. It works OK on Ubuntu 14.04 with the version from date --version
starting date (GNU coreutils) 8.21
. Here is a script that works for me when run by bash
(but not when run by sh
— because on Ubuntu that is a symlink to dash
rather than bash
). I used the -u
(UTC) option so that the input and output times agreed — beware the dreaded time zone.
Note that if you are using GNU date
, you can specify +%s
to get the time value in seconds since The Epoch (which is 1970-01-01 00:00:00 +00:00) as a pure number, and those can be compared numerically.
date1=`date -u -d "2014-09-03T04:27:23Z" +"%Y-%m-%dT%TZ"`
date2=`date -u -d "2014-09-03T02:23:09Z" +"%Y-%m-%dT%TZ"`
date3=`date -u -d "2014-09-03T05:23:09Z" +"%Y-%m-%dT%TZ"`
if [ ${date3} -lt ${date1} ]
then echo "true (claim: [ $date3 -lt $date1 ])"
else echo "false (claim: [ $date3 -lt $date1 ])"
fi
if [ ${date2} -gt ${date1} ]
then echo "true (claim: [ $date2 -gt $date1 ])"
else echo "false (claim: [ $date2 -gt $date1 ])"
fi
if [ ${date3} '<' ${date1} ]
then echo "true (claim: [ $date3 '<' $date1 ])"
else echo "false (claim: [ $date3 '<' $date1 ])"
fi
if [ ${date2} '>' ${date1} ]
then echo "true (claim: [ $date2 '>' $date1 ])"
else echo "false (claim: [ $date2 '>' $date1 ])"
fi
if [[ ${date3} < ${date1} ]]
then echo "true (claim: [[ $date3 < $date1 ]])"
else echo "false (claim: [[ $date3 < $date1 ]])"
fi
if [[ ${date2} > ${date1} ]]
then echo "true (claim: [[ $date3 > $date1 ]])"
else echo "false (claim: [[ $date3 > $date1 ]])"
fi
if [ $(expr ${date3} '>' ${date1}) = 1 ]
then echo "true (claim: [ \$(expr $date3 '>' $date1) = 1 ])"
else echo "false (claim: [ \$(expr $date3 '>' $date1) = 1 ])"
fi
if [ $(expr ${date2} '>' ${date1}) = 1 ]
then echo "true (claim: [ \$(expr $date2 '>'$date1) = 1 ])"
else echo "false (claim: [ \$(expr $date2 '>' $date1) = 1 ])"
fi
$ bash dtcmp.sh
dtcmp.sh: line 6: [: 2014-09-03T05:23:09Z: integer expression expected
false (claim: [ 2014-09-03T05:23:09Z -lt 2014-09-03T04:27:23Z ])
dtcmp.sh: line 11: [: 2014-09-03T02:23:09Z: integer expression expected
false (claim: [ 2014-09-03T02:23:09Z -gt 2014-09-03T04:27:23Z ])
false (claim: [ 2014-09-03T05:23:09Z '<' 2014-09-03T04:27:23Z ])
false (claim: [ 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z ])
false (claim: [[ 2014-09-03T05:23:09Z < 2014-09-03T04:27:23Z ]])
false (claim: [[ 2014-09-03T05:23:09Z > 2014-09-03T04:27:23Z ]])
true (claim: [ $(expr 2014-09-03T05:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
false (claim: [ $(expr 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
$
$ dash dtcmp.sh
dtcmp.sh: 6: [: Illegal number: 2014-09-03T05:23:09Z
false (claim: [ 2014-09-03T05:23:09Z -lt 2014-09-03T04:27:23Z ])
dtcmp.sh: 11: [: Illegal number: 2014-09-03T02:23:09Z
false (claim: [ 2014-09-03T02:23:09Z -gt 2014-09-03T04:27:23Z ])
false (claim: [ 2014-09-03T05:23:09Z '<' 2014-09-03T04:27:23Z ])
false (claim: [ 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z ])
dtcmp.sh: 26: dtcmp.sh: [[: not found
false (claim: [[ 2014-09-03T05:23:09Z < 2014-09-03T04:27:23Z ]])
dtcmp.sh: 31: dtcmp.sh: [[: not found
false (claim: [[ 2014-09-03T05:23:09Z > 2014-09-03T04:27:23Z ]])
true (claim: [ $(expr 2014-09-03T05:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
false (claim: [ $(expr 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
$
$ ksh dtcmp.sh
dtcmp.sh[6]: [: 2014-09-03T05:23:09Z: arithmetic syntax error
false (claim: [ 2014-09-03T05:23:09Z -lt 2014-09-03T04:27:23Z ])
dtcmp.sh[11]: [: 2014-09-03T02:23:09Z: arithmetic syntax error
false (claim: [ 2014-09-03T02:23:09Z -gt 2014-09-03T04:27:23Z ])
dtcmp.sh[16]: [: <: unknown operator
false (claim: [ 2014-09-03T05:23:09Z '<' 2014-09-03T04:27:23Z ])
false (claim: [ 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z ])
false (claim: [[ 2014-09-03T05:23:09Z < 2014-09-03T04:27:23Z ]])
false (claim: [[ 2014-09-03T05:23:09Z > 2014-09-03T04:27:23Z ]])
true (claim: [ $(expr 2014-09-03T05:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
false (claim: [ $(expr 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
$
$ zsh dtcmp.sh
dtcmp.sh:[:6: integer expression expected: 2014-09-03T05:23:09Z
false (claim: [ 2014-09-03T05:23:09Z -lt 2014-09-03T04:27:23Z ])
dtcmp.sh:[:11: integer expression expected: 2014-09-03T02:23:09Z
false (claim: [ 2014-09-03T02:23:09Z -gt 2014-09-03T04:27:23Z ])
dtcmp.sh:16: condition expected: <
false (claim: [ 2014-09-03T05:23:09Z '<' 2014-09-03T04:27:23Z ])
dtcmp.sh:21: condition expected: >
false (claim: [ 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z ])
false (claim: [[ 2014-09-03T05:23:09Z < 2014-09-03T04:27:23Z ]])
false (claim: [[ 2014-09-03T05:23:09Z > 2014-09-03T04:27:23Z ]])
true (claim: [ $(expr 2014-09-03T05:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
false (claim: [ $(expr 2014-09-03T02:23:09Z '>' 2014-09-03T04:27:23Z) = 1 ])
$
bash
accepts and works correctly with the '<'
and '>'
operators in plain test
aka [
— the other shells either do not accept it or accept it but produce the wrong answer (which is worse!).[[
, the results agree and are correct.expr
notation.For maximum portability, use expr
. For maximum sanity, use [[
.
Upvotes: 6