20

I have to create a Shell Script wherein one of the parameters will be the date in the format dd/mm/yyyy. My question is, how can I check if the Date passed as parameter really follows this Date Format? I tried to use the grep command as below:

if echo "$1" | grep -q '^[0-3][0-9]/[0-1][0-9]/[0-9]\{4\}$' 

but it didn't give the correct format because the day for example can be 33, 34, (...), that is not really the correct format. Anyone know something that can really check if the date passed really follows the format dd/mm/yyyy ?

1

14 Answers 14

30

Use date

date "+%d/%m/%Y" -d "09/99/2013" > /dev/null 2>&1 is_valid=$? 

The date string must be in "MM/DD/YYYY" format.

If you do not get 0 then date is in invalid format.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your answer. This really works but just for format mm/dd/yyyy . If you check on terminal the option -d check just the format mm/dd/yyyy. I tried to change the format but it not affects the way as the option -d for date command is used.
WTF? The question was how can I check if the Date passed as parameter really follows this Date Format. This doesn't check anything. date accepts dates in different formats. For example tomorrow is a valid format, date -d 'tomorrow + 5 months 20:00:01 is valid as well.
Also date -d '05/20/1900' throws an error for me. It also depends on your architecture (the date range can be bigger for 64-bit systems). This is definitely not the way to go. Use konsolebox's answer instead.
As pointed by Aleks-Daniel Jakimenko-A., this actually does not check the date format. To verify if dd is in YYYY-MM-DD format and is a valid date, one can do: [[ $(date "+%Y-%m-%d" -d "$dd") == "$dd" ]] || echo 'invalid date format'
that didn't work for me, for example: date "+%d/%m/%Y" -d "20/09/2013" is invalid, but is a valid date. It's really boring those differences between date command in the bash...
|
20

The simplest solution, that still works perfectly, is the following :

if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null 2>&1 ... 

It consists in combining 2 checks :

  • the first part checks that $1 is of this format : NNNN-NN-NN
  • the second part checks that it is a valid date

You need the two checks because :

  • if you don't do the first check, date will exit with code 0 even if your variable is a valid date in another format
  • if you don't do the second check, then you can end up with a 0 even for variables such as 2016-13-45

1 Comment

the second check will succeed in case you don't use dashes. e.g. 20203112 will succeed in the date -d check.
6

This function expects 2 strings,a format string, a date string

The format string uses the codes from the date command but does not include the '+'

The function returns 0 if the provided date matches the given format, otherwise it returns 1

Code (my_script.sh)

#!/bin/bash datecheck() { local format="$1" d="$2" [[ "$(date "+$format" -d "$d" 2>/dev/null)" == "$d" ]] } date_test="$1" echo $date_test if datecheck "%d %b %Y" "$date_test"; then echo OK else echo KO fi 

Output

$ ./my_script.sh "05 Apr 2020" 05 Apr 2020 OK $ ./my_script.sh "foo bar" foo bar KO 

Comments

5

First, check the form of the input using the regex. Then use awk to switch to mm/dd/yyyy and use date to validate. You can use the following expression in your if statement:

echo "$1" | egrep -q '^[0-3][0-9]/[0-1][0-9]/[0-9]{4}$' && date -d "$(echo "$1" | awk 'BEGIN{FS=OFS="/"}{print $2"/"$1"/"$3}')" >/dev/null 2>&1 

1 Comment

Kudos for actually answering the OP's question and in a one-liner. The egrep regex can be simplified to '^[0-9]{2}/[0-9]{2}/[0-9]{4}$' as the subsequent check with date will disqualify dates like 40/01/2020.
3

Simplest way for dd/mm/yyyy exactly in Bash is:

if [[ $1 == [0-3][0-9]/[0-1][0-9]/[0-9][0-9][0-9][0-9] ]] 

Or

if [[ $1 =~ ^[0-3][0-9]/[0-1][0-9]/[0-9]{4}$ ]] 

2 Comments

That's exactly what the OP said didn't work, since it accepts invalid dates like 33/19/2000.
@augurar Yes I could have made a more accurate one. But then again even the accepted solution could accept other formats not just dd/mm/yyyy. So I believe it wouldn't really matter now.
2

How about using awk:

echo "31/12/1999" | awk -F '/' '{ print ($1 <= 31 && $2 <= 12 && match($3, /^[1-9][1-9][1-9][1-9]$/)) ? "good" : "bad" }' 

It prints "good" if its valid date else prints "bad"

3 Comments

This will validate dates like 31/02/2012 or 29/02/2001 or 31/04/2000 or... :(
I have the need to validate multiple date formats across multiple files, which I'll already be awking out to get the date. This solution fits the request better than KonsoleBox's or PxL's. That being said, the format of the date in the request is different then this solution. It should be echo "31/12/1999" | awk -F '/' '{ print ($1 <= 12 && $2 <= 31 && match($3, /^[1-9][1-9][1-9][1-9]$/)) ? "good" : "bad" }' The only caveat is that 02/30/2014 would pass even though that's not a date, but this is still perfect for my needs.
For the year parameter, it's better to use [0-9], instead of [1-9]
2
#! /bin/bash isDateInvalid() { DATE="${1}" # Autorized separator char ['space', '/', '.', '_', '-'] SEPAR="([ \/._-])?" # Date format day[01..31], month[01,03,05,07,08,10,12], year[1900..2099] DATE_1="((([123][0]|[012][1-9])|3[1])${SEPAR}(0[13578]|1[02])${SEPAR}(19|20)[0-9][0-9])" # Date format day[01..30], month[04,06,09,11], year[1900..2099] DATE_2="(([123][0]|[012][1-9])${SEPAR}(0[469]|11)${SEPAR}(19|20)[0-9][0-9])" # Date format day[01..28], month[02], year[1900..2099] DATE_3="(([12][0]|[01][1-9]|2[1-8])${SEPAR}02${SEPAR}(19|20)[0-9][0-9])" # Date format day[29], month[02], year[1904..2096] DATE_4="(29${SEPAR}02${SEPAR}(19|20(0[48]|[2468][048]|[13579][26])))" # Match the date in the Regex if ! [[ "${DATE}" =~ "^(${DATE_1}|${DATE_2}|${DATE_3}|${DATE_4})$" ]] then echo -e "ERROR - '${DATE}' invalid!" else echo "${DATE} is valid" fi } echo echo "Exp 1: "`isDateInvalid '12/13/3000'` echo "Exp 2: "`isDateInvalid '12/11/2014'` echo "Exp 3: "`isDateInvalid '12 01 2000'` echo "Exp 4: "`isDateInvalid '28-02-2014'` echo "Exp 5: "`isDateInvalid '12_02_2002'` echo "Exp 6: "`isDateInvalid '12.10.2099'` echo "Exp 7: "`isDateInvalid '31/11/2000'` 

Comments

1

Here's a function to do some data validation this:

# Script expecting a Date parameter in MM-DD-YYYY format as input verifyInputDate(){ echo ${date} | grep '^[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$' if [ $? -eq 0 ]; then echo "Date is valid" else echo "Date is not valid" fi } 

1 Comment

This is no better than the OPs example. Dates like 99/87/0000 would pass your grep.
1

`X="2016-04-21" then check for the below value being 1 or 0.

cal echo $x | cut -c 6-7 echo $x | cut -c 1-4 2>/dev/null | grep -c echo $x | cut -c 9-10

If the value is 1, then it's valid, else it's not valid.

1 Comment

Do you mean "backticks"? You should enter them as such in your answer then, otherwise it won't work because at the moment they are single quotes.
1

I would like to give an extended answer for a slightly different format, but this can easily be changed to the dd/mm/YY format with the answers already given; it's tested on busybox (posix shell)

This is one of the first hits for web searches similar to "busybox posix shell script date" and "test format" or "validate" etc, so here my solution for busybox (tested with 1.29.3, 1.23.1)

#!/bin/sh ########## # # check if date valid in busybox # tested in busybox 1.29.3, 1.23.1 # # call with: # $0 <yyyymmdd> # ########## mydate=$1 if echo $mydate | grep -qE '20[0-9][0-9](0[1-9]|1[0-2])([012][0-9]|3[01])'; then printf 'may be valid\n' date +%Y%m%d -d $mydate -D %Y%m%d > /dev/null 2>&1 is_valid=$? if [ $is_valid -ne 0 ]; then printf 'not valid\n' return 1 else mytestdate=$(date +%Y%m%d -d $mydate -D %Y%m%d) if [ $mydate -ne $mytestdate ]; then printf 'not valid, results in "%s"\n' "$mytestdate" return 1 else printf 'valid\n' fi fi else printf 'not valid (must be: <yyyymmdd>)\n' return 1 fi 

as in busybox (1.29.3 & 1.23.1) you have responds like:

lxsys:~# date +%Y%m%d -d 20110229 -D "%Y%m%d" 20110301 

I had the need to validate the date in some better way but i wanted to rely mostly on the system itself

so with

mytestdate=$(date +%Y%m%d -d $mydate -D %Y%m%d) if [ $mydate -ne $mytestdate ]; then ... fi 

there is a second test - do we have a difference between the wanted or given format (input, $mydate) and the system interpretation (output, $mytestdate) of it ... if it's not the same, discard the date

Comments

0

I wrote this bash script to validate date. I can accept mont as alphanumeric.

#!/bin/bash function isDateValid { DATE=$1 if [[ $DATE =~ ^[0-9]{1,2}-[0-9a-zA-Z]{1,3}-[0-9]{4}$ ]]; then echo "Date $DATE is a number!" day=`echo $DATE | cut -d'-' -f1` month=`echo $DATE | cut -d'-' -f2` year=`echo $DATE | cut -d'-' -f3` if [ "$month" == "01" ] || [ "$month" == "1" ]; then month="Jan" elif [ "$month" == "02" ] || [ "$month" == "2" ]; then month="Feb" elif [ "$month" == "03" ] || [ "$month" == "3" ]; then month="Mar" elif [ "$month" == "04" ] || [ "$month" == "4" ]; then month="Apr" elif [ "$month" == "05" ] || [ "$month" == "5" ]; then month="May" elif [ "$month" == "06" ] || [ "$month" == "6" ]; then month="Jun" elif [ "$month" == "07" ] || [ "$month" == "7" ]; then month="Jul" elif [ "$month" == "08" ] || [ "$month" == "8" ]; then month="Aug" elif [ "$month" == "09" ] || [ "$month" == "9" ]; then month="Sep" elif [ "$month" == "10" ]; then month="Oct" elif [ "$month" == "11" ]; then month="Nov" elif [ "$month" == "12" ]; then month="Dec" fi ymd=$year"-"$month"-"$day echo "ymd: "$ymd dmy=$(echo "$ymd" | awk -F- '{ OFS=FS; print $3,$2,$1 }') echo "dmy: "$dmy if date --date "$dmy" >/dev/null 2>&1; then echo "OK" return 0 else echo "NOK" return 1 fi else echo "Date $DATE is not a number" return 1 fi } if isDateValid "15-15-2014"; then echo "date is valid =)" else echo "bad format date" fi echo "===================" if isDateValid "15-12-2014"; then echo "date is valid =)" else echo "bad format date" fi echo "===================" if isDateValid "15-Dec-2014"; then echo "date is valid =)" else echo "bad format date" fi echo "===================" if isDateValid "1-May-2014"; then echo "date is valid =)" else echo "bad format date" fi echo "===================" if isDateValid "1-1-2014"; then echo "date is valid =)" else echo "bad format date" fi echo "===================" if isDateValid "12-12-2014"; then echo "date is valid =)" else echo "bad format date" fi 

Comments

0

Though the solution (if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null 2>&1) of @https://stackoverflow.com/users/2873507/vic-seedoubleyew is best one at least for linux, but it gives error as we can not directly compare/match regex in if statement. We should put the regex in a variable and then we should compare/match that variable in if statement. Moreover second part of if condition does not return a boolean value so this part will also cause error.

So I have done slight modification in the above formula and this modification can also be customized further for various other formats or combination of them.

DATEVALUE=2019-11-12 REGEX='^[0-9]{4}-[0-9]{2}-[0-9]{2}$' if [[ $DATEVALUE =~ $REGEX ]] ; then date -d $DATEVALUE if [ $? -eq 0 ] ; then echo "RIGHT DATE" else echo "WRONG DATE" fi else echo "WRONG FORMAT" fi 

Comments

0

Another regex to validate the date:

"$1" =~ [0-9]{4}/(0[1-9]|1[0-2])/(0[1-9]|[1-2][0-9]$|3[0-1]$) 

This regex has some shortcomings, it doesn't check if the day of month is valid for the specified month.

Comments

-2

Blockquote

DATE = "$*" [[ "${DATE}" != @(((([123][0]|[012][1-9])|3[1])?([ \/._-])(0[13578]|1[02])?([ \/._-])(19|20)[0-9][0-9])|(([123][0]|[012][1-9])?([ \/._-])\ (0[469]|11)?([ \/._-])(19|20)[0-9][0-9])|(([12][0]|[01][1-9]|2[1-8])?([ \/._-])02?([ \/._-])(19|20)[0-9][0-9])|(29?([ \/._-])02?([ \/._-])\ (19|20(0[48]|[2468][048]|[13579][26])))) ]] && echo error || echo good) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.