Linux разница между датами

How to compare two dates in a shell?

How can two dates be compared in a shell? Here is an example of how I would like to use this, though it does not work as-is:

todate=2013-07-18 cond=2013-07-15 if [ $todate -ge $cond ]; then break fi 

15 Answers 15

The right answer is still missing:

todate=$(date -d 2013-07-18 +%s) cond=$(date -d 2014-08-19 +%s) if [ $todate -ge $cond ]; then break fi 

Note that this requires GNU date. The equivalent date syntax for BSD date (like found in OSX by default) is date -j -f «%F» 2014-08-19 +»%s»

Donno why someone downvoted this, it’s pretty accurate and doesn’t deal with concatenating ugly strings. Convert your date to unix timestamp (in seconds), compare unix timestamps.

I think, me main advantage of this solution is, that you can do additions or subtractions easily. For example, I wanted to have a timeout of x seconds, but my first idea ( timeout=$(`date +%Y%m%d%H%M%S` + 500) ) is obviously wrong.

It’s probably better to use (( . > . )) than [ . -gt . ] or similar. [[ «$x» -lt «$y» ]] returns true when $x is empty, in fact [[ «» -eq 0 ]] is TRUE. If something goes wrong populating $x and it winds up empty, I’d want «» < 1 to return false. (( "$x" < "$y" )) properly errors when $x is empty without needing extra checks.

Can use a ksh-style string comparison to compare the chronological ordering [of strings in a year, month, day format].

date_a=2013-07-18 date_b=2013-07-15 if [[ "$date_a" > "$date_b" ]] ; then echo "break" fi 

Thankfully, when [strings that use the YYYY-MM-DD format] are sorted* in alphabetical order, they are also sorted* in chronological order.

Nothing fancy needed in this case. yay!

@TrippKinetics I just realized that you can also quote the > , so [ 2023-02-15 \> 2023-01-01 ] && echo ok works.

You are missing the date format for the comparison:

#!/bin/bash todate=$(date -d 2013-07-18 +"%Y%m%d") # = 20130718 cond=$(date -d 2013-07-15 +"%Y%m%d") # = 20130715 if [ $todate -ge $cond ]; #put the loop where you need it then echo 'yes'; fi 

You are missing looping structures too, how are you planning to get more dates?

date -d is non standard and does not work on a typical UNIX. On BSD it even attempts to set the kenel value DST .

This is not a problem of looping structures but of data types.

Those dates ( todate and cond ) are strings, not numbers, so you cannot use the «-ge» operator of test . (Remember that square bracket notation is equivalent to the command test .)

What you can do is use a different notation for your dates so that they are integers. For example:

will produce an integer like 20130715 for July 15th, 2013. Then you can compare your dates with «-ge» and equivalent operators.

Update: if your dates are given (e.g. you are reading them from a file in 2013-07-13 format) then you can preprocess them easily with tr.

$ echo "2013-07-15" | tr -d "-" 20130715 

The operator -ge only works with integers, which your dates aren’t.

In any shell, you can convert the strings to numbers while respecting the order of dates simply by removing the dashes.

if [ "$(echo "$todate" | tr -d -)" -ge "$(echo "$cond" | tr -d -)" ]; then break; fi 

Alternatively, you can go traditional and use the expr utility.

if expr "$todate" ">=" "$cond" > /dev/null; then break; fi 

As invoking subprocesses in a loop can be slow, you may prefer to do the transformation using shell string processing constructs.

todate_num=$$; todate_num=$$ cond_num=$$; cond_num=$$ if [ "$todate_num" -ge "$cond_num" ]; then break; fi 

Of course, if you can retrieve the dates without the hyphens in the first place, you’ll be able to compare them with -ge .

Читайте также:  Чем почистить linux mint

I convert the Strings into unix-timestamps (seconds since 1.1.1970 0:0:0). These can compared easily

unix_todate=$(date -d "$" "+%s") unix_cond=$(date -d "$" "+%s") if [ $ -ge $ ]; then echo "over condition" fi 

Seems to be the same as unix.stackexchange.com/a/170982/100397 which was posted some time before yours

There is also this method from the article titled: Simple date and time calulation in BASH from unix.com.

These functions are an excerpt from a script in that thread!

Usage

# calculate the number of days between 2 dates # -s in sec. | -m in min. | -h in hours | -d in days (default) dateDiff -s "2006-10-01" "2006-10-31" dateDiff -m "2006-10-01" "2006-10-31" dateDiff -h "2006-10-01" "2006-10-31" dateDiff -d "2006-10-01" "2006-10-31" dateDiff "2006-10-01" "2006-10-31" 

If you install gdate on MacOS via brew this can be easily modified to work by changing date to gdate .

bash specific answer

As I like to reduce forks and bash do permit a lot of tricks, there is my purpose:

todate=2013-07-18 cond=2013-07-15 

This will re-populate both variables $todate and $cond , using only one fork, with ouptut of date -f — wich take stdio for reading one date by line.

Finally, you could break your loop with

myfunc() < local todate cond < read todate read cond >< <( date -f - +%s <<<"$1"$'\n'"$2" ) ((todate>=cond))&&return printf "%(%a %d %b %Y)T older than %(%a %d %b %Y)T. \n" $todate $cond > 

Using bash’s builtin printf wich could render date time with seconds from epoch (see man bash 😉

This script only use one fork.

Alternative with limited forks and date reader function

This will create a dedicated subprocess (only one fork):

mkfifo /tmp/fifo exec 99> >(exec stdbuf -i 0 -o 0 date -f - +%s >/tmp/fifo 2>&1) exec 98 

As input and output are open, fifo entry could be deleted.

myDate() < local var="$" shift echo >&99 "$" read -t .01 -u 98 $var > 

Nota In order to prevent useless forks like todate=$(myDate 2013-07-18) , the variable is to be set by the function himself. And to permit free syntax (with or without quotes to datestring), the variable name must be the last argument.

myDate 2013-07-18 todate myDate Mon Jul 15 2013 cond (( todate >= cond )) && < printf "To: %(%c)T >Cond: %(%c)T\n" $todate $cond break > 
To: Thu Jul 18 00:00:00 2013 > Cond: Mon Jul 15 00:00:00 2013 bash: break: only meaningful in a `for', `while', or `until' loop 

Or use shell-connector bash function:

wget https://github.com/F-Hauri/Connector-bash/raw/master/shell_connector.bash 
wget https://f-hauri.ch/vrac/shell_connector.sh 

(Wich are not exactly same: .sh do contain full test script if not sourced)

source shell_connector.sh newConnector /bin/date '-f - +%s' @0 0 myDate 2013-07-18 todate myDate "Mon Jul 15 2013" cond (( todate >= cond )) && < printf "To: %(%c)T >Cond: %(%c)T\n" $todate $cond break > 

Profiling bash contain a good demonstration for how using date -f - could reduce ressources requirement.

dates are strings, not integers; you can't compare them with standard arithmetic operators.

one approach you can use, if the separator is guaranteed to be - :

IFS=- read -ra todate cond[idx] )) && break done unset IFS 

This works as far back as bash 2.05b.0(1)-release .

You can also use mysql's builtin function to compare the dates. It gives the result in 'days'.

from_date="2015-01-02" date_today="2015-03-10" diff=$(mysql -u$ -p$ -N -e"SELECT DATEDIFF('$','$');") 

Just insert the values of $DBUSER and $DBPASSWORD variables according to yours.

FWIW: on Mac, it seems feeding just a date like "2017-05-05" into date will append the current time to the date and you will get different value each time you convert it to epoch time. to get a more consistent epoch time for fixed dates, include dummy values for hours, minutes, and seconds:

date -j -f "%Y-%m-%d %H:%M:%S" "2017-05-05 00:00:00" +"%s" 

This may be a oddity limited to the version of date that shipped with macOS. tested on macOS 10.12.6.

Echoing @Gilles answer ("The operator -ge only works with integers"), here is an example.

curr_date=$(date +'%Y-%m-%d') echo "$curr_date" 2019-06-20 old_date="2019-06-19" echo "$old_date" 2019-06-19 
curr_date_int=$(date -d "$" +"%s") echo "$curr_date_int" 1561014000 old_date_int=$(date -d "$" +"%s") echo "$old_date_int" 1560927600 

"$curr_date" is greater than (more recent than) "$old_date" , but this expression incorrectly evaluates as False :

if [[ "$curr_date" -ge "$old_date" ]]; then echo 'foo'; fi 
if [[ "$curr_date" -ge "$old_date" ]]; then echo 'foo'; else echo 'bar'; fi bar 

Integer comparisons:

if [[ "$curr_date_int" -ge "$old_date_int" ]]; then echo 'foo'; fi foo if [[ "$curr_date_int" -gt "$old_date_int" ]]; then echo 'foo'; fi foo if [[ "$curr_date_int" -lt "$old_date_int" ]]; then echo 'foo'; fi if [[ "$curr_date_int" -lt "$old_date_int" ]]; then echo 'foo'; else echo 'bar'; fi bar 

The reason why this comparison doesn't work well with a hyphenated date string is the shell assumes a number with a leading 0 is an octal, in this case the month "07". There have been various solutions proposed but the quickest and easiest is to strip out the hyphens. Bash has a string substitution feature that makes this quick and easy then the comparison can be performed as an arithmetic expression:

todate=2013-07-18 cond=2013-07-15 if (( $ > $ )); then echo larger fi 

It seems that all or most of the answers so far either assume you get to specify date format (which the question does not explicitly specify one way or the other), or assume that you have a feature rich version of date command or something.

Sometimes though, you are not in control of the date's format (e.g., the expiration date of an SSL certificate where date is given by a month name abbreviation) and you don't have access to a fancier date command. So while this solution is not completely general, it does run on a vanilla bash shell on Linux, Solaris, and FreeBSD, and handles month names (borrows significantly from a few smart answers from https://stackoverflow.com/questions/15252383/unix-convert-month-name-to-number):

#!/usr/bin/bash function monthnumber < month=$(echo $| tr '[a-z]' '[A-Z]') MONTHS="JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC" tmp=$ month=$ monthnumber=$((month/3+1)) printf "%02d\n" $monthnumber > # Or at the expense of some flexibility and brevity, you get more readability: function monthnumber2 < case $(echo $| tr '[a-z]' '[A-Z]') in JAN) monthnumber="01" ;; FEB) monthnumber="02" ;; MAR) monthnumber="03" ;; APR) monthnumber="04" ;; MAY) monthnumber="05" ;; JUN) monthnumber="06" ;; JUL) monthnumber="07" ;; AUG) monthnumber="08" ;; SEP) monthnumber="09" ;; OCT) monthnumber="10" ;; NOV) monthnumber="11" ;; DEC) monthnumber="12" ;; esac printf "%02d\n" $monthnumber > TODAY=$( date "+%Y%m%d" ) echo "GET /" | openssl s_client -connect github.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > tmp.pem cert_expiry_date=$(openssl x509 -in tmp.pem -noout -enddate | cut -d'=' -f2) month_name=$(echo $cert_expiry_date | cut -d' ' -f1) month_number=$( monthnumber $month_name ) cert_expiration_datestamp=$( echo $cert_expiry_date | awk "\",\$2>" ) echo "compare: [ $cert_expiration_datestamp -gt $TODAY ]" if [ $cert_expiration_datestamp -gt $TODAY ] ; then echo "all ok, the cert expiration date is in the future." else echo "WARNING: cert expiration date is in the past." fi 

Источник

Date comparison in Bash [duplicate]

I need to compare two dates/times using Bash. Input format: 2014-12-01T21:34:03+02:00 I want to convert this format to int and then compare the int s of the two dates. Or does bash have another way to compare two dates?

2 Answers 2

You can compare lexicographically with the conditional construct [[ ]] in this way:

[[ expression ]]
Return a status of 0 or 1 depending on the evaluation of the conditional expression expression.

If you need to compare times with different time-zone, you can first convert those times in this way:

get_date() < date --utc --date="$1" +"%Y-%m-%d %H:%M:%S" >$ get_date "2014-12-01T14:00:00+00:00" 2014-12-01 14:00:00 $ get_date "2014-12-01T12:00:00-05:00" 2014-12-01 17:00:00 $ [[ $(get_date "2014-12-01T14:00:00+00:00") < $(get_date "2014-12-01T12:00:00-05:00") ]] && echo it works it works 

One option would be to convert the date to the number of seconds since the UNIX epoch:

date -d "2014-12-01T21:34:03+02:00" +%s 

You can then compare this integer to another date which has been processed in the same way:

(( $(date -d "2014-12-01T21:34:03+02:00" +%s) < $(date -d "2014-12-01T21:35:03+02:00" +%s) )) 

The (( )) syntax is used to create an arithmetic context as we are comparing two numbers. You could also use the more general [ $ -lt $ ] style syntax if portability is a concern.

One advantage of doing it this way is that date understands a variety of formats, for example
date -d "next year" +%s . Furthermore, date understands timezones, so it can correctly handle comparisons between pairs of dates where the timezone is different.

However, if neither of those issues concerns you, then I'd go for j.a.'s solution.

Источник

Calculate difference between two dates in Bash

In Bash, you can easily calculate the difference between two dates. This can be useful in various scenarios, such as calculating the number of days between two events or determining the age of a person. In this article, we’ll discuss different methods to calculate the difference between two dates in Bash.

Calculate difference between two dates

The ‘date’ command in Bash can be used to calculate the difference between two dates. The syntax for using the ‘date’ command is as follows:

In the above command, replace “YYYY-MM-DD” with the two dates you want to compare in the same format. The command will output the number of days between the two dates.

Let’s look at an example. If you want to calculate the number of days between January 1, 2023 and February 28, 2023, you can use the following command:

The output of this command will be:

This means that there are 58 days between January 1, 2023 and February 28, 2023.

Method 2: Using the ‘bc’ command

The ‘bc’ command in Bash can be used to perform mathematical calculations. To calculate the difference between two dates using the ‘bc’ command, use the following syntax:

In the above command, replace “YYYY-MM-DD” with the two dates you want to compare in the same format. The command will output the number of days between the two dates.

Источник

Оцените статью
Adblock
detector