AKS
AKS

Reputation: 17366

Custom date verification - BASH - using just grep or awk etc

I saw couple of posts (some depends upon date -d $xyz to verify) but I'm trying to create an until loop where the user should be re-prompted to enter the value of a date format until it matches the custom date format.

My date format (what I need for Splunk) is m/d/yyyy:h:m:s or mm/dd/yyyy:hh:mm:ss

which means, if m (month number) is a single digit lets say 1 for January, then both 1 or 01 values are possible for date format but 0 or 00 is NOT a valid value. Value range is 01-to->12 or 1-to->12 but not greater than 12.

Similarly, the same rule applies to d (day number), it can be 01-to->10-to->31 or 1-to->31 but not 00 or more than 31 and all other yyyy (year), h (hour), m (minute), s (second) part.

What could be a minimal code (obfuscated is fine) to do this verification in BASH? It seems like date -d ??? doesn't provides this custom kind of verification for date/times!

OK, I can write one verifyDateFormatfunc() to do this, but I know there are people who have already written a one-liner / minimal snippet to verify this for sure. grep -f .. (where bunch of regex are listed line by line for all possible combinations, again the main code will look very minimal if I follow this? as the patterns sitting in -f file for grep will be transparent to a user) -or creating a map funcation (based on delimiters) for value ranges?

Possible values:

1/03/2017:23:0:15
02/4/2017:0:1:2
09/05/2017:10:10:0
10/6/2017:12:14:16

Upvotes: 3

Views: 140

Answers (2)

Hakan Baba
Hakan Baba

Reputation: 2045

I do not know whether using BSD date is an option for you, but it has what you are looking for.

There the date checker function can look like this

is_datetime_valid() {
   date -j -f "%m/%d/%Y:%T"  $1 1> /dev/null 2>&1
   return $?
}

Upvotes: 1

randomir
randomir

Reputation: 18697

Here's an unholy extended regular expression (POSIX ERE):

^([1-9]|1[0-2]|0[1-9])/([1-9]|0[1-9]|[12][0-9]|3[01])/[0-9]{4}:([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$

that will test for the date/time patterns you specified (m/d/yyyy:h:m:s and mm/dd/yyyy:hh:mm:ss), with:

  • month: 1-12, 01-12
  • day: 1-31, 01-31
  • year: 0000-9999
  • hour: 0-23, 00-23
  • minute: 0-59, 00-59
  • second: 0-59, 00-59

You can use in an awk program that will exit with success (exit code 0) if the (first) line is a valid date/time (wrapped in a shell function that tests the first argument, for convenience):

#!/bin/bash
is_datetime_valid() {
    awk '{exit $0!~"^([1-9]|1[0-2]|0[1-9])/([1-9]|0[1-9]|[12][0-9]|3[01])/[0-9]{4}:([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$"}' <<<"$1"
}

Or, if you prefer a pure bash solution (with ERE support in bash v3.0+):

#!/bin/bash
is_datetime_valid() {
    local pat='^([1-9]|1[0-2]|0[1-9])/([1-9]|0[1-9]|[12][0-9]|3[01])/[0-9]{4}:([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$'
    [[ $1 =~ $pat ]]
}

You can use it like:

if is_datetime_valid "1/03/2017:23:0:15"; then
    # yup, it's valid
else
    # ney, it's invalid
fi

Tested on a few examples:

#!/bin/bash
samples=(
    "1/03/2017:23:0:15" "02/4/2017:0:1:2" "09/05/2017:10:10:0" "10/6/2017:12:14:16"
    "00/03/2017:23:0:15" "1/33/2017:23:0:15"
)
for dt in "${samples[@]}"; do
    if is_datetime_valid "$dt"; then
        echo "$dt is valid"
    else
        echo "$dt is invalid"
    fi
done

Gives:

1/03/2017:23:0:15 is valid
02/4/2017:0:1:2 is valid
09/05/2017:10:10:0 is valid
10/6/2017:12:14:16 is valid
00/03/2017:23:0:15 is invalid
1/33/2017:23:0:15 is invalid

Upvotes: 3

Related Questions