3

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
randomir
  • 17,989
  • 1
  • 40
  • 55
AKS
  • 16,482
  • 43
  • 166
  • 258
  • So, would you say `2/31/2017:0:0:0` is a valid date? – randomir Oct 16 '17 at 19:35
  • You can use your arithmetic operators to validate date/time components with leading zeros, e.g. `echo $((01))` is `1`. – David C. Rankin Oct 16 '17 at 19:37
  • @randomir :) nice catch. As splunk accepts both 01 or 1 as valid, Im now thinking just go with no leading zero format. Still Feb is a special case. – AKS Oct 16 '17 at 20:22
  • Doing this will give remove leading 0's from a given number: `a="9/09/2019:00:00:00"; echo "$a" | grep -o "[0-9]\{1,2\}/[0-9]\{1,2\}/[1-9][0-9]\{3\}:[0-9]\{1,2\}:[0-9]\{1,2\}:[0-9]\{1,2\}" | sed "s/\([\/:]\)0/\1/g" ; echo $? 9/9/2019:0:0:0 0(success)` and will give me the date time format which Splunk can read, next I can put some checks around individual values (delimited by `/` or `:`)... or wrap it in a minimal code for a verifyDateTime function. I'll share what I can get but would be great if there's any other minimal one-liner/code to do the same. – AKS Oct 16 '17 at 20:39

2 Answers2

3

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
randomir
  • 17,989
  • 1
  • 40
  • 55
  • Awesome, except it'll catch 0000 for yyyy so first number for year can't be `0`. – AKS Oct 16 '17 at 20:54
  • What's the valid range for year, in your case? – randomir Oct 16 '17 at 20:55
  • I would say, more precisely anytime after Splunk was born (2003 onwards), but i'll do it. Thanks. – AKS Oct 16 '17 at 21:00
  • You can then use `[2-9][0-9]{3}` for the year field (if `2000-9999` is ok). – randomir Oct 16 '17 at 21:03
  • This is great! I think the only thing I have to nail down now is checking for February for day# 28/29 thingy based on a given leap year. I'll try that later but initial verification looks perfect. This is indeed very simple one-liner solution. Thanks. `is_splunkdatetime_valid 2/30/2017:23:0:15; echo $? 0 `. Lol: http://www.refinery29.com/2017/02/142002/why-does-february-have-28-days – AKS Oct 16 '17 at 21:09
  • You're welcome. The leap year and Feb29 is only one of the issues. There are leap seconds, DST changes, "missing days/years", [etc](https://stackoverflow.com/a/6841479/404556). Truly validating dates is really, really, really hard. – randomir Oct 16 '17 at 21:13
1

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 $?
}
Hakan Baba
  • 1,897
  • 4
  • 21
  • 37
  • nice! That's good to know and very clean one liner. Most probably the slave server used by Jenkins is based on an Ubuntu image so I'll prefer the grep awk way but this one is definitely one of the answer for a BSD based system – AKS Oct 17 '17 at 20:50
  • @ArunSangal : try `date --date="13/3/2017 23:00:15" "+%m/%d/%Y:%T"`, this will produce `date: invalid date \`13/3/2017 23:00:15'`, running on a debian type linux (and any others with a modern `date` (If you're lucky ;-) ). Good luck to all. – shellter Aug 07 '18 at 19:44