Validate user input date as parameter - bash script - bash

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

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

Related

How to validate date in linux bash?

I would like to validate the format of the date input (in YYYY/MM/DD format) and only allow the user to enter current date or date after it.
For example, if the user enters 2022/03/28 (before current date), then error message should appear. If the user enters 2022/04/01 (current date) or 2022/04/06 (after current date), then it will not show error message.
May I know how can I do the validation?
echo "Enter date (in YYYY/MM/DD format): "
read date
It seems to me the code needs to perform two different validations:
did the user enter a valid date (eg, 2020/10/40 is not a valid date)
did the user enter a date that is earlier than today's date
For the 1st validation we can have date process the input as a date, eg:
$ input='2020/10/40' # invalid date
$ date -d "${input}"
date: invalid date ‘2020/10/40’
$ echo $?
1 # non-zero (false) return code
$ input='2020/10/20' # valid date
$ date -d "${input}"
Tue Oct 20 00:00:00 CDT 2020
$ echo $?
0 # zero (true) return code
For the 2nd validation we can let bash compare our dates as strings, eg:
$ input='2020/10/20'
$ today=$(date '+%Y/%m/%d')
$ typeset -p input today
declare -- input="2020/10/20"
declare -- today="2022/03/31"
$ [[ "${input}" < "${today}" ]] && echo 'error'
error
$ input='2022/03/31'
$ today=$(date '+%Y/%m/%d')
$ typeset -p input today
declare -- input="2022/03/31"
declare -- today="2022/03/31"
$ [[ "${input}" < "${today}" ]] && echo 'error'
# no output
$ input='2022/05/31'
$ today=$(date '+%Y/%m/%d')
$ typeset -p input today
declare -- input="2022/05/31"
declare -- today="2022/03/31"
$ [[ "${input}" < "${today}" ]] && echo 'error'
# no output
Pulling all of this into a demo:
today=$(date '+%Y/%m/%d')
while true
do
echo "Enter date (in YYYY/MM/DD format): "
read input
# validate input:
date -d "${input}" > /dev/null 2>&1
[[ $? != 0 ]] && \
echo "ERROR: invalid date [${input}]; please try again." && \
continue
# compare input with today:
[[ "${input}" < "${today}" ]] && \
echo "ERROR: input [${input}] is less than today [${today}]; try again." && \
continue
break
done
echo "Congrats! input [${input}] is greater-than/equal-to today [${today}]."
Testing:
Enter date (in YYYY/MM/DD format):
2020/10/40
ERROR: invalid date [2020/10/40]; please try again.
Enter date (in YYYY/MM/DD format):
2020/10/20
ERROR: input [2020/10/20] is less than today [2022/03/31]; try again.
Enter date (in YYYY/MM/DD format):
2022/03/20
ERROR: input [2022/03/20] is less than today [2022/03/31]; try again.
Enter date (in YYYY/MM/DD format):
2022/03/31
Congrats! input [2022/03/31] is greater-than/equal-to today [2022/03/31].
Enter date (in YYYY/MM/DD format):
2022/03/10
ERROR: input [2022/03/10] is less than today [2022/03/31]; try again.
Enter date (in YYYY/MM/DD format):
2022/05/31
Congrats! input [2022/05/31] is greater-than/equal-to today [2022/03/31].

Check if the date format is in ISO 8601 in bash

How can I validate that my datetime string has a valid ISO-8601 format with timezone (+0200) through a bash script?
The acceptable format is only %Y-%m-%dT%H:%M:%S+0200.
EDIT: I'm fine with having the year as 0001, 9999 or any random permutation of 4 digits. I just need to enforce the syntactical check.
Here's a simple bash script which tests the given parameter against a regex for an extended (not basic/shortened) ISO-8601 format which, following the question, requires all the fields to be present:
#!/bin/bash
if [[ $1 =~ ^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T?[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}([\.,][[:digit:]]+)?\+0200$ ]]
then
echo correct format
else
echo incorrect format
fi
Wikipedia's article on ISO-8601 says that the format allows the omission of the day field, but the above regex requires it, following the question's example. The regex allows the T to be optional, following the Wikipedia article. I've added optional support for fractional seconds (the ([\.,][[:digit:]]+)? portion), again based on the Wikipedia article.
The regex does not do any range checking of the values.
If you want your ISO date to be validated for any timezone, including the format
1970-01-01T00:00:00Z
1970-01-01T00:00:00+0200
Then you can use this regex:
[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|\+[0-9]{4})$
#!/bin/bash
if [[ $1 =~ [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|\+[0-9]{4})$ ]]
then
echo "ISO date";
else
echo "Malformed date";
fi
Keep in mind that [[ ]] is not POSIX compliant, this limits its portability.
Here is a solution that doesn't use any regex and uses date parsing of gnu-date:
s='2021-08-23T12:10:35+200'
[[ $s == *'+200' && $(TZ=CET date -d "$s" '+%FT%T' 2>/dev/null) == "${s%+*}" ]] &&
echo 'ok' || echo 'nope'

validate date in unix for DD-MM-YYYY format

How to validate the date in Unix.
Where the date format is DD-MM-YYYY.
For example if i run one script ./ValidateDate.sh DD-MM-YYYY then it should check that the date is in this format and also that the date exists.
If date is valid and in above format then proceed for next else print message 'Invalid date'
Thanks.
Well... This is a fine can o' worms.
Any shell script that you create may not work on all of the various flavors of Unix/Linux.
The BSD date command (found on OS X and Solaris) does a great job at taking in the date and verifying the format. It requires you to specify your date format, but once you do, it has no problems:
if date -j -f "%d-%m-%Y" "$DATE" 2>&1 > /dev/null # No output
then
echo "ERROR: Date '$DATE' is not a valid format."
exit 2
else
echo "Date is '$DATE'."
fi
Linux and other systems that use GNU's date can also validate the date, but use a different syntax:
date -d "$DATE" 2>&1 /dev/null # With a bit of luck this will work...
if $? -ne 0
then
echo "ERROR: Date '$DATE' is not a valid format."
else
echo "Date is '$DATE'."
fi
I say With a bit of luck because it's up to the date command to figure out your date's format. This normally works, and will work with your YYYY-MM-DD format, but it can be confusing:
Imagine this:
$ DATE="15/6/2014" # It's June 15, 2014
$ date -d "$DATE"
date: invalid date `15/6/2014' # Invalid?
That's because in my timezone, the dd/mm/yyyy format isn't a valid format. In my timezone, the date should be mm/dd/yyyy. To get around this you can use Wintermute's suggestion and format the date into ISO format before using GNU's date format.
A more universal possibility is to use Perl or Python to figure out if the date is correct:
if perl -mTime::Piece -e "Time::Piece->strptime(\"$DATE\", \"%Y-%m-%d\")" 2> /dev/null
then
echo "ERROR: Date '$DATE' is not a valid format."
else
echo "Date is '$DATE'."
fi
This Perl oneliner will return a non-zero error status if $DATE isn't a valid date in %Y-%m-%d format.
You can validate the date with the date utility, but you first have to transform the input into something it can understand. I suggest ISO representation (always). It could, for example, look like this:
#!/bin/sh
PATTERN='\([0-9]\{1,2\}\)-\([0-9]\{1,2\}\)-\([0-9]\+\)'
# if the pattern doesn't match, this will give date an empty string, so it will fail as expected.
if date -d $(echo "$1" | sed -n "/$PATTERN/ { s/$PATTERN/\3-\2-\1/; p }") > /dev/null 2>&1 ; then
echo valid
else
echo invalid
fi
awk way
awk 'split($0,a,"-"){print strftime("%d-%m-%Y",mktime(a[3]" "a[2]" "a[1]" 00 00 00"))==$0?"valid":"not valid"}' <<< "31-12-1992"
It Converts the string to epoch,converts epoch back then checks against the original.
Edit:
Thought i would add after testing this works for dates
FROM 01-01-0
TO 31-12-2147483647
Although a drawback is after you go below the year 1000 you have to remove leading zeros from the year.
You can do this fairly easily if you are willing to break the validation into two steps. First, check that the string is in the right format:
date=$1
[[ $date =~ ([0-9][0-9])-([0-9][0-9])-([0-9]+) ]] || { printf "Invalid date format\n"; exit 1; }
If that test passes, you can extract the day, month, and year fields, then verify that each falls in the correct range.
day=${BASH_REMATCH[1]}
month=${BASH_REMATCH[2]}
year=${BASH_REMATCH[3]}
thirty_one_days='0[1-9]|[12][0--9]|3[01]'
thirty_days='0[1-9]|[12][0--9]|30'
twenty_eight_days='0[1-9]|1[0-9]|2[0-8]'
case $month in
01|03|05|07|08|10|12)
[[ $day =~ $thirty_one_days ]] ;;
04|06|09|11)
[[ $day =~ $thirty_days ]] ;;
02)
# 1-28, but 29 OK in a leap year.
[[ $day =~ $twenty_eight_days ]] ||
(( year % 4 == 0 && $year % 400 == 0 && day == 29 ))
*)
false
esac || { print "Invalid date\n"; exit 1; }

Test if variable is a date with the correct format [duplicate]

This question already has an answer here:
Bash shell: How to check for specific date format?
(1 answer)
Closed 1 year ago.
I would like to test if a variable is a date (not a problem), but if the variable have the correct date format (yyyy-MM-dd).
I tried :
export DATE_REFRESH=01/01/1900
if ! date -d $DATE_REFRESH "+%Y-%m-%d"; then
echo "$DATE_REFRESH is not a valid date. Expected format is : yyyy-MM-dd"
fi
But it doesn't work.
I can try with this :
if [[ $DATE_REFRESH == [0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9] ]]
But I don't want to have a date to 33/19/2000...
You can use this snippet:
isValidDate() {
if [[ "$1" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1">/dev/null 2>&1; then
echo "valid"
else
echo "invalid"
fi;
}
Testing:
isValidDate "1900-12-25"
valid
isValidDate "1900-14-25"
invalid
isValidDate "01/01/1900"
invalid
You have to do both tests because the date string can be everything, see the manpage:
The --date=STRING is a mostly free format human readable date
string such as ...
Use that:
if ! [[ "$DATE_REFRESH" =~ ^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}$ ]] \
|| ! date -d $DATE_REFRESH >/dev/null 2>&1; then
echo "$DATE_REFRESH is not a valid date. Expected format is : yyyy-MM-dd"
fi

user input date format verification in bash

So I'm trying to write a simple script in bash that asks user for input date in
following format (YYYY-dd-mm). Unfortunately I got stuck on first step, which is verifying that input is in correct format. I tried using 'date' with no luck (as it returns actual current date). I'm trying to make this as simple as possible. Thank you for your help!
Using regex:
if [[ $date =~ ^[0-9]{4}-[0-3][0-9]-[0-1][0-9]$ ]]; then
or with bash globs:
if [[ $date == [0-9][0-9][0-9][0-9]-[0-3][0-9]-[0-1][0-9] ]]; then
Please note that this regex will accept a date like 9999-00-19 which is not a correct date. So after you check its possible correctness with this regex you should verify that the numbers are correct.
IFS='-' read -r year day month <<< "$date"
This will put the numbers into $year $day and $month variables.
date -d "$date" +%Y-%m-%d
The latter is the format, the -d allows an input date. If it's wrong it will return an error that can be piped to the bit bucket, if it's correct it will return the date.
The format modifiers can be found in the manpage of date man 1 date. Here an example with an array of 3 dates:
dates=(2012-01-34 2014-01-01 2015-12-24)
for Date in ${dates[#]} ; do
if [ -z "$(date -d $Date 2>/dev/null)" ; then
echo "Date $Date is invalid"
else
echo "Date $Date is valid"
fi
done
Just a note of caution: typing man date into Google while at work can produce some NSFW results ;)

Resources