user input date format verification in bash - 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 ;)

Related

Validate user input date as parameter - bash script

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

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'

check if String Date (mm/dd/yyyy) is weekend - bash

I have nested for loops going through dates and create a date. How can I check if that specific date is on the weekend or not?
String date is in the format of mm/dd/yyyy but can easily be changed. Each has its own variable $m $d $y
if [[ $(date +%u) -gt 5 ]] ; then
#do something
fi
above code works with current date, not sure how to translate that to accepting a string date.
You could use Ruby Date#cwday in your bash script. For example:
#!/bin/bash
y=2019
m=11
d=10
ruby -rtime -e "puts ([6,7].include? Date.new($y,$m,$d).cwday)"
which outputs true
I like more to use the variable directly:
if [[ $(date -d $your_variable +%u) -gt 5 ]]; then
#do something
fi
Just makes the code cleaner in my opinion.

How do I convert mm/dd/yy to yymmdd format in bash script?

I have a date field from a file with 50 dates in mm/dd/yy format. How can I convert it to yymmdd?
I have seen questions similar, but going the opposite direction. I cant seem to apply it the way I need it.
Dates are saved in file as 01/20/72 and I need to convert them to 720120
Currently I have $bDate +%y%m%d as the command, but it is wrong.
Thanks in advance!!
string manipulation: bash regular expressions suffice here:
date="04/13/06"
d='[[:digit:]]'
if [[ $date =~ ($d$d)/($d$d)/($d$d) ]]; then
newdate=${BASH_REMATCH[3]}${BASH_REMATCH[1]}${BASH_REMATCH[2]}
fi
echo $newdate
060413
If you're repeatedly doing this, make it a function:
mdy2ymd() {
local d='[[:digit:]]' newdate
if [[ $1 =~ ($d$d)/($d$d)/($d$d) ]]; then
newdate=${BASH_REMATCH[3]}${BASH_REMATCH[1]}${BASH_REMATCH[2]}
fi
echo $newdate
}
you can use sed and its internal regexp storing vars like
echo "01/20/72" | sed -r 's#(..)/(..)/(..)#\3\1\2#g'

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; }

Resources