check if date argument is in yyyy-mm-dd format

9,766

Solution 1

This will check for the correct format (YYYY-MM-DD) in bash (with built-in regex match):

if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]
  then echo "Date $1 is in valid format (YYYY-MM-DD)"
  else echo "Date $1 is in an invalid format (not YYYY-MM-DD)"
fi

Run:

./script.sh 2015-12-10

Output:

Date 2015-12-10 is in valid format (YYYY-MM-DD)

It doesn't check if the date itself is valid, only the format (as stated in the question, "Check if a date argument is in the correct format (YYYY-MM-DD"))

If you need to check the date format and validate the date value, try something like:

if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null
      then echo "Date $1 is valid and matches the format (YYYY-MM-DD)"
fi

This will discard invalid dates like 0000-88-77 that pass the regex matching.

(Credit goes to @glenn jackman for this suggestion.)

Solution 2

You have not tagged an OS, and because you're using ksh I'm guessing you're probably not using Linux, and you might be using Solaris. The date command you give will work, but it's for GNU date where -d (or --date) is a GNU enhancement.

So the simple option is to use GNU date, you may need to install the GNU coreutils package, or you may already have it (check in /usr/sfw/bin on Solaris) it may already be available possibly as gdate or gnudate. This will properly validate the date in the YYYY-MM-DD format (unlike the regex examples elsewhere which will accept 2015-31-01)

You may instead be able to use the touch command in a similar and more portable (POSIX) way

touch -c -d 2015-12-12T00:00:00 /tmp/does-not-exist
  • use -c so that a file is not created
  • append T00:00:00 to the date to make a valid time stamp

(However, if you are using Solaris not only do some versions lack -d, I have found some versions at least have a bug/misfeature where they do not validate and instead try to be smart, so "2015-02-29" becomes "2015-03-01" with no error, so you cannot use these versions for date validation.)

I prefer this way, it's robust and portable assuming you have gawk (or mawk instead, but traditional nawk lacks these time functions).

#!/usr/local/bin/gawk -f
BEGIN{
    split(ARGV[1],bb,/-/)
    tm=mktime( bb[1] " " bb[2] " " bb[3] " 00 00 00")
    tms=sprintf("%04i-%02i-%02i",bb[1],bb[2],bb[3])
    if (tms==strftime("%Y-%m-%d",tm)) exit 0
    exit 1
}

The above

  • takes the first argument and splits in on the "-" character.
  • mktime() to determine an epoch time stamp for the indicated YYYY-MM-DD
  • sprintf() to normalise the YYYY-MM-DD (leading 0)
  • strftime() to convert epoch time back to YYYY-MM-DD, and compare as a string

The reason for the extra convert and compare is to catch the case where questionable dates are reinterpreted by the C library mktime(), as is the case with glibc on linux (the same problem as with Solaris touch above).

Caring about the correct format of YYYY-MM-DD may or may not mean caring about the validity of the date (something date will do), and might even mean being able to eliminate YYYY-DD-MM format dates. If you only wish to confirm the format is "date like", then a regular expression will suffice:

grep -qE "^[0-9]{4}-[01]?[0-9]-[0123]?[0-9]$"
grep -qE "^[12][0-9]{3}-(0?[1-9]|10|11|12)-(0?[1-9]|[12][0-9]|3[01])$"

(On Solaris use /usr/xpg4/bin/grep not the default grep) The first one will reject most bogus dates, the second one will reject almost all bogus dates. With -q there will be no output and you can use the return code as expected. Take care to anchor (^...$) or otherwise restrict the regular expression so that it does not simply match valid substring, e.g. 2015-12-12345

Share:
9,766

Related videos on Youtube

ispiro
Author by

ispiro

Updated on September 18, 2022

Comments

  • ispiro
    ispiro over 1 year

    I want to distribute an application (simple one exe file) so that a user installs it once and it'll be installed for all user accounts on that computer. And it should be done without Wix / InstallShield / Setup-project. (Clickonce doesn’t even support multi-user installations.)

    How is it done? Registry edit (Which one?), Manually create an msi file (How? Is there a reference guide for that?)?

    Edit

    My question is not how to have some command execute at installation time. The question is about how does Windows know which applications are installed.

    • Hamlet Hakobyan
      Hamlet Hakobyan about 10 years
      Probably, you must write your own installer.
    • Sinatr
      Sinatr about 10 years
      Distribute means installing. If you don't want to use existing installer, then: 1) you can make own installer 2) you can make application what doesn't required installation (could also be portable edition).
    • ispiro
      ispiro about 10 years
      @Sinatr Not installing would not allow it to be used from the command line, for example.
    • nvoigt
      nvoigt about 10 years
      Is there a reason you want to reinvent the wheel instead of using an existing, well tested wheel known to work?
    • Sinatr
      Sinatr about 10 years
      Based on question edit, you better explain what you need. Typical installer job is to ensure what software is ready to run. You can make program what will run in any case (even if components are missing, by, to example, using bootstrap) and will configure itself even if started from USB-stick on another PC. If you need something to be available in command line, then your exe-file location has to be in PATH or near cmd.exe itself.
    • ispiro
      ispiro about 10 years
      @nvoigt Yes. The setup project wheel is unavailable in VS Express, and the other wheels (mostly the highly acclaimed WIX) have a steep learning curve. I agree with this comment .
    • nvoigt
      nvoigt about 10 years
      Well, you have to learn something new once in a while. Setup projects were discontinued. Learning to create an MSI faking executable yourself seems silly when you could learn to use WiX (or an alternative, somehow you ruled them all out) instead.
  • ispiro
    ispiro about 10 years
    But what would I put there?
  • Christopher Painter
    Christopher Painter about 10 years
    Actually I'd argue that you have the old way and the new way reversed. The old way (1999) was to create a proper installer to do all that. These days all the fresh outs don't have a darn clue how to do that and they reinvent the wheel with new implementations of what you call the "old way".
  • Christopher Painter
    Christopher Painter about 10 years
    @ispiro- There are "installed" programs. MSI has rich component, feature, product registration metadata that is instrumented through Win32 API and WMI. I can tell you exactly what is installed where in my 300,000 machine environment if you are using a properly authored MSI.
  • clerksx
    clerksx over 8 years
    Using a regular expression to parse dates is not a good idea. For one, determination of "valid" dates is non-trivial in regular expressions -- ie. determination that 2015-02-31 is not valid, but 2015-03-31 is.
  • X Tian
    X Tian over 8 years
    I understand your point, the question is to validate the format not check whether a date is valid, I think this is a homework assignment, he asked for another way (than using date). :-]
  • Angel Todorov
    Angel Todorov over 8 years
    I would also use if [[ ... ]] && date -d "$1" >/dev/null to validate both the format and the actual value -- discard invalid dates like 9999-99-99 that pass the regex matching.
  • Dmitry Grigoryev
    Dmitry Grigoryev over 8 years
    If you end up using date anyway, why not use the one-liner from the question?
  • Cezar Todirisca
    Cezar Todirisca over 8 years
    @DmitryGrigoryev Using date is not required to check the format. The format is checked with bash regex matching. Using date additionally validates the value, not format.
  • Dmitry Grigoryev
    Dmitry Grigoryev over 8 years
    date is not required to check the format, but it can be used check it, so why write a custom regex for something which is already there?
  • Cezar Todirisca
    Cezar Todirisca over 8 years
    I don't think you're checking the input format. You're coercing the format with date. The user can supply 20151011 as a value to "$d", and you won't know.
  • Cezar Todirisca
    Cezar Todirisca over 8 years
    @DmitryGrigoryev See my comment to your answer below.
  • Cezar Todirisca
    Cezar Todirisca over 8 years
    It won't be validated, but you won't know why. Maybe because of invalid format (not conforming to YYYY-MM-DD), maybe because of an invalid value (2015-02-31). You will not know. The OP's question was not about value validation, but of format validation. Regarding coercion, I meant date '+%Y-%m-%d' -d $d. This does coerce $d to the specified format.
  • Dmitry Grigoryev
    Dmitry Grigoryev over 8 years
    The question about format vs. value being incorrect is quite philosophical. For example 2015-13-01 can be both incorrect format (YYYY-DD-MM) or incorrect value (there is no 13-th month). Not only my script will not know why validation failed, but it's simply impossible to know.
  • Faither
    Faither about 3 years
    What if just ^[0-9]{4}-(0?[1-9]|10|11|12)-(0?[1-9]|[12][0-9]|3[01])$ to support years from 0000 to 9999 instead of just 1000-2999?
  • mr.spuratic
    mr.spuratic about 3 years
    Feel free, but there are increasingly many bear-traps as you travel back from 1970, so a robust (location aware) time library should be considered for anything better than "date like".