Am trying to validate that the first (and only) parameter a user passes into a script is a valid date in the format dd/mm/yyyy e.g. 13/01/2022.
I started off with a regex which was fine but doesn't validate the date. I found the suggestion (on stack overflow) to use date -d "$1" '<date format>'
and then use that to move forwards or error.
I can get this to validate a YYYY-MM-DD date but DD-MM-YYYY or my preferred DD/MM/YYYY always throw an invalid date.
I have hardcoded this to today's date in the code example below and have been changing to date format string.
I can get date ' %d/%m/%Y'
on the command line to return today's date in the format I want. Is there a limitation on the format I can validate?
This throws Invalid date for 02/12/2022
(today's date of posting).
#datestr=$1
datestr=$(date ' %d/%m/%Y')
echo $datestr
if [[ "$datestr" == $(date -d "$datestr" ' %d/%m/%Y') ]]; then
echo "Valid date"
else
echo "Invalid date"
fi
TIA
[Edit - my starting point for the solution] Check if a string matches a regex in Bash script
CodePudding user response:
Using GNU date
and bash
:
#!/bin/bash
datestr=$1
if [[ $datestr = [0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] ]] &&
date -d "${datestr:6}-${datestr:3:2}-${datestr:0:2}" &>/dev/null
then
echo "Valid date"
else
echo "Invalid date"
fi
CodePudding user response:
Here's a pure bash function that does the full validation of a date in the format %d/%m/%Y
:
#!/bin/bash
date_is_dmY() {
[[ $1 =~ ^([0-9]{2})/([0-9]{2})/([0-9]{4})$ ]] || return 1
local year month day
((
year = 10#${BASH_REMATCH[3]},
month = 10#${BASH_REMATCH[2]},
day = 10#${BASH_REMATCH[1]}
))
(( 1 <= day )) || return 1
case $month in
( 2) (( day <= ((year@0 == 0 || (year%4 == 0 && year0 != 0)) ? 29 : 28) )) || return 1 ;;
( 4|6|9|11) (( day <= 30 )) || return 1 ;;
(1|3|5|7|8|10|12) (( day <= 31 )) || return 1 ;;
( *) return 1 ;;
esac
}
Here's an example on how to use it:
for datestr in 31/04/2022 29/02/2000 29/02/1900
do
if date_is_dmY "$datestr"
then
printf 'valid date: %s\n' "$datestr"
else
printf 'invalid date: %s\n' "$datestr" >&2
fi
done
invalid date: 31/04/2022
valid date: 29/02/2000
invalid date: 29/02/1900
Pros:
- It works with any bash on any OS
- It doesn't fork a
date
command, so it should be faster (at least when used inside a loop).
Cons:
- It only supports a specific date format