Validate date format in a shell script
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 '^36/14/9\$'
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 ?
14 Answers 14
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.
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.
The simplest solution, that still works perfectly, is the following :
if [[ $1 =~ ^5-8-7$ ]] && 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
the second check will succeed in case you don’t use dashes. e.g. 20203112 will succeed in the date -d check.
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
$ ./my_script.sh "05 Apr 2020" 05 Apr 2020 OK $ ./my_script.sh "foo bar" foo bar KO
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 '^13/18/8$' && date -d "$(echo "$1" | awk 'BEGIN')" >/dev/null 2>&1
Kudos for actually answering the OP’s question and in a one-liner. The egrep regex can be simplified to ‘^8<2>/2<2>/3$’ as the subsequent check with date will disqualify dates like 40/01/2020.2>
Simplest way for dd/mm/yyyy exactly in Bash is:
@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.
It prints «good» if its valid date else prints «bad»
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, /^4942$/)) ? "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.=>
#! /bin/bash isDateInvalid() < DATE="$" # 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]9)|3[1])$(0[13578]|1[02])$(19|20)94)" # Date format day[01..30], month[04,06,09,11], year[1900..2099] DATE_2="(([123][0]|[012]6)$(0[469]|11)$(19|20)33)" # Date format day[01..28], month[02], year[1900..2099] DATE_3="(([12][0]|[01]1|22)$02$(19|20)62)" # Date format day[29], month[02], year[1904..2096] DATE_4="(29$02$(19|20(0[48]|[2468][048]|[13579][26])))" # Match the date in the Regex if ! [[ "$" =~ "^($|$|$|$)$" ]] then echo -e "ERROR - '$' invalid!" else echo "$ 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'`
Here’s a function to do some data validation this:
# Script expecting a Date parameter in MM-DD-YYYY format as input verifyInputDate() < echo $| grep '^94-22-9985$' if [ $? -eq 0 ]; then echo "Date is valid" else echo "Date is not valid" fi >
`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.
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.
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 # ########## mydate=$1 if echo $mydate | grep -qE '2095(04|12)([012]4|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: )\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
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
I wrote this bash script to validate date. I can accept mont as alphanumeric.
#!/bin/bash function isDateValid < DATE=$1 if [[ $DATE =~ ^5-[0-9a-zA-Z]-3$ ]]; 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
Linux shell bash script if condition
What is the exact command that you are using to run the script? What command is the customer using? It looks like the customer is using bash to run it, while you are using sh, or vice-versa.
I’m obliged to believe that the actual code being run is not in fact an exact match for the code generating the bug. We need an actual reproducer — a minimal reproducible example — to make any useful headway here.
The successfull command : [root@testServ45-149 mytest]# ./port_generator.sh ||| Fail server command : bash-4.1$ ./port_generator.sh
@ivbtar, I don’t mean the invocation, I mean the command run by the script, in a tested form known to reproduce the problem exactly as-given. What’s given here in the question does not, on its own, produce the problem at hand.
2 Answers 2
Does your script have a shebang? What shell is the customer running the script in? Do you use the same shell?
netikras@netikras-xps ~ $ bash netikras@netikras-xps ~ $ if [[ 3 -gt 1 ]]; then echo OK; fi OK netikras@netikras-xps ~ $ csh % if [[ 3 -gt 1 ]]; then echo OK; fi [[: No match. % exit
Not all shells have the same syntax. Make sure you and your client are using the same shell. Or update the answer with more info
Your script should have a shebang line at the top: #!/bin/bash or #!/usr/bin/env bash . But that’s not the problem. If it were, the error would point to the first || , not the fourth one.
Do you have the if statement on two separate lines in your actual script? This would generate the message you’re seeing:
if [[ "$" -gt 12 ]] || [[ "$" -gt 31 ]] || [[ "$" -lt 0 ]] || [[ "$" -lt 0 ]] || [[ "$" -lt 0 ]] || [[ "$" -gt 2050 ]]; then
If that’s what you did, don’t. Or if you do want the line split up, end the first line with || so that the parser knows the command isn’t finished.
if [[ "$" -gt 12 ]] || [[ "$" -gt 31 ]] || [[ "$" -lt 0 ]] || [[ "$" -lt 0 ]] || [[ "$" -lt 0 ]] || [[ "$" -gt 2050 ]]; then
Checking date format in bash
This works fine as long as the input are 8 numbers (e.g. it accepts «20210901» and rejects «20213131»). However, when the input is totally off (e.g. «a» or «nonsense»), it simply takes today’s date. How could the code be modified for a stricter check of the format? The modified code reads:
valid=0 while true; do DATE=date FORMAT="%Y%m%d" read -p "Enter start date (YYYYMMDD) " initialdate if date=$(date -d "$initialdate" +'+%Y%m%d'); then start_date=`$DATE +$FORMAT -d $initialdate` if [[ "$start_date" =~ ^[[0123456789]]$ ]] then valid=1 echo "valid"; echo $start_date break else echo "Invalid format" fi fi echo "Please use right format (YYYYMMDD) " done
Please don’t edit the solution to your question. If you want to share what ultimately worked for you, you can add your own answer instead.
3 Answers 3
A common way to do input validation is to use an infinite loop that you break out of as soon as the input has been validated.
In this case, «validated» means that the input string contains only eight digits and that GNU date is able to use that date as an option argument to its -d option.
With bash , we could write this as
while true; do read -r -p 'Input date: ' if [[ $REPLY =~ ^[[:digit:]]$ ]] && date -d "$REPLY" >/dev/null 2>&1 then break fi echo 'Invalid format, try again' >&2 done thedate=$REPLY
This reads the date at the start of our loop, then tests it with a regular expression that will match if the string contains only eight digits. If that test passes, the string is also tested with GNU date . If that goes well, the loop is exited via a break statement.
In sh , we need to replace the regular expression match with a shell pattern match:
while true; do printf 'Input date: ' >&2 read -r REPLY case $REPLY in *[![:digit:]]*) ;; . ) date -d "$REPLY" >/dev/null 2>&1 && break esac echo 'Invalid format, try again' >&2 done thedate=$REPLY
The test here first tests whether the string contains any other character than a digit. If that test fails, the next test is for exactly eight characters. If that test succeeds we have a string of eight digits and we test with GNU date and exit the loop if the date is ok.