Jairo Franchi
Jairo Franchi

Reputation: 377

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 '^[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 ?

Upvotes: 19

Views: 102571

Answers (14)

Abdellatif
Abdellatif

Reputation: 1

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.

Upvotes: 0

Schusch
Schusch

Reputation: 11

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

Upvotes: 1

P̲̳x͓L̳
P̲̳x͓L̳

Reputation: 3653

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.

Upvotes: 30

Karl Kowallis
Karl Kowallis

Reputation: 61

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

Upvotes: 6

Amit Kumar Tyagi
Amit Kumar Tyagi

Reputation: 51

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

Upvotes: 0

Felipe
Felipe

Reputation: 7563

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

Upvotes: 0

Vic Seedoubleyew
Vic Seedoubleyew

Reputation: 10526

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

Upvotes: 19

Manikandan
Manikandan

Reputation: 11

`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.

Upvotes: 1

chelabim
chelabim

Reputation: 19

#! /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'`

Upvotes: 2

chelabim
chelabim

Reputation: 19

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)

Upvotes: -2

javaPlease42
javaPlease42

Reputation: 4963

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
}

Upvotes: 1

VDR
VDR

Reputation: 2853

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"

Upvotes: 2

konsolebox
konsolebox

Reputation: 75458

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}$ ]]

Upvotes: 3

augurar
augurar

Reputation: 13016

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

Upvotes: 5

Related Questions