Check if a date is valid

Following up on the my post Check if a particular year is a LEAP year, here is a routine to check if a date is valid.

By definition, a date is valid if all of these conditions are true …

a) The year is an integer greater than or equal to 0

b) The month is an integer between 1 and 12

c) For months 1, 3, 5, 7, 8, 10 and 12, the day is an integer between 1 and 31

d) For months 4, 6, 9 and 11, the day is an integer between 1 and 30

e) For month 2, the day is an integer between 1 and 29 (Leap Years) or 28 (otherwise)

You deploy this subroutine (IsThisAValidDate) by supplying the value to be checked in variable [IsThisAValidDate/Input] … and upon return, examine the contents of [IsThisAValidDate/Answer] … the contents of which will be “True” or a description of the error encountered.

Here is an example code snippet …

... assume date to be validated was entered in [TextEntry1]
SetVar "[IsThisAValidDate/Input]" "![DateEntered]"
...
SetVar "[IsThisAValidDate/Format]" "!/mdy"
...
GoSub "IsThisAValidDate"
If "[IsThisAValidDate/Answer]" "=" "True"
	AlertBox "OK" "[TextEntry1] is a valid Date"
Else
	AlertBox "Error ..." "[IsThisAValidDate/Answer]"
EndIf

And here is the subroutine you would paste in your App’s SubRoutines section …
:IsThisAValidDate
... examines [IsThisAValidDate/Input], [IsThisAValidDate/Format]
... returns True/False in [IsThisAValidDate/Answer]
...
... split into day, month and year
SubStr "[IsThisAValidDate/Format]" "1" "1" "[IsThisAValidDate/Format1]"
StrParse "[IsThisAValidDate/Input]" "[IsThisAValidDate/Format1]" "[IsThisAValidDate/Part]" "[IsThisAValidDate/Count]"
...
... got 3 parts ?
If "[IsThisAValidDate/Count]" "<>" "3"
   SetVar "[IsThisAValidDate/Answer]" "!Did not find expected 2 separator characters [IsThisAValidDate/Format1]"
   Return
EndIf
...
... identify day, month and year parts
SubStr "[IsThisAValidDate/Format]" "2" "4" "[IsThisAValidDate/Format234]"
If "[IsThisAValidDate/Format234]" "=" "dmy"
   SetVar "[IsThisAValidDate/Day]" "[IsThisAValidDate/Part1]"
   SetVar "[IsThisAValidDate/Month]" "[IsThisAValidDate/Part2]"
   SetVar "[IsThisAValidDate/Year]" "[IsThisAValidDate/Part3]"
Endif
If "[IsThisAValidDate/Format234]" "=" "mdy"
   SetVar "[IsThisAValidDate/Month]" "[IsThisAValidDate/Part1]"
   SetVar "[IsThisAValidDate/Day]" "[IsThisAValidDate/Part2]"
   SetVar "[IsThisAValidDate/Year]" "[IsThisAValidDate/Part3]"
Endif
... other date formats can be added/supported here
...
... is [IsThisAValidDate/Year] an integer ?
Math "[IsThisAValidDate/Year]/1" "0" "[IsThisAValidDate/Y1]"
Math "[IsThisAValidDate/Year]-([IsThisAValidDate/Y1]*1)" "-1" "[IsThisAValidDate/Y2]"
If "[IsThisAValidDate/Y2]" "<>" "!0"
   SetVar "[IsThisAValidDate/Answer]" "YearNotInteger"
   Return
EndIf
... is [IsThisAValidDate/Year] less than zero ?
If "[IsThisAValidDate/Year]" "<" "!0"
   SetVar "[IsThisAValidDate/Answer]" "YearIsNegative"
   Return
EndIf
...
... is [IsThisAValidDate/Month] an integer ?
Math "[IsThisAValidDate/Month]/1" "0" "[IsThisAValidDate/M1]"
Math "[IsThisAValidDate/Month]-([IsThisAValidDate/M1]*1)" "-1" "[IsThisAValidDate/M2]"
If "[IsThisAValidDate/M2]" "<>" "!0"
   SetVar "[IsThisAValidDate/Answer]" "MonthNotInteger"
   Return
EndIf
... is [IsThisAValidDate/Month] an integer between 1 and 12
If "[IsThisAValidDate/Month]" "<" "1"
   SetVar "[IsThisAValidDate/Answer]" "MonthLessThan1"
   Return
EndIf
If "[IsThisAValidDate/Month]" ">" "12"
   SetVar "[IsThisAValidDate/Answer]" "MonthGreaterThan12"
   Return
EndIf
...
... is [IsThisAValidDate/Day] an integer ?
Math "[IsThisAValidDate/Day]/1" "0" "[IsThisAValidDate/D1]"
Math "[IsThisAValidDate/Day]-([IsThisAValidDate/D1]*1)" "-1" "[IsThisAValidDate/D2]"
If "[IsThisAValidDate/D2]" "<>" "!0"
   SetVar "[IsThisAValidDate/Answer]" "DayNotInteger"
   Return
EndIf
... is [IsThisAValidDate/Day] an integer ?
If "[IsThisAValidDate/Day]" "<" "1"
  SetVar "[IsThisAValidDate/Answer]" "DayLessThan1"
  Return
Endif
... jan, mar, may, jul, aug, oct and dec have 31 days
... for [IsThisAValidDate/Month] 1, 3, 5, 7, 8, 10 or 12
... is [IsThisAValidDate/Day] an integer between 1 and 31
SetVar "[IsThisAValidDate/MonthsWith31Days]" "$1,$3,$5,$7,$8,$10,$12"
SearchStr "$[IsThisAValidDate/Month]" "[IsThisAValidDate/MonthsWith31Days]" "[IsThisAValidDate/SearchPos]"
If "[IsThisAValidDate/SearchPos]" ">" "!0"
   ... yes it is one of the 31 day months
   If "[IsThisAValidDate/Day]" ">" "31"
      SetVar "[IsThisAValidDate/Answer]" "DayGreaterThan31"
   Else
      SetVar "[IsThisAValidDate/Answer]" "True"
   Endif
   Return
EndIf
... apr, jun, sep and nov have 30 days
... for [IsThisAValidDate/Month] 4, 6, 9 or 11
... is [IsThisAValidDate/Day] an integer between 1 and 30
SetVar "[IsThisAValidDate/MonthsWith30Days]" "$4,$6,$9,$11"
SearchStr "$[IsThisAValidDate/Month]" "[IsThisAValidDate/MonthsWith30Days]" "[IsThisAValidDate/SearchPos]"
If "[IsThisAValidDate/SearchPos]" ">" "!0"
   ... yes it is one of the 30 day months
   If "[IsThisAValidDate/Day]" ">" "30"
      SetVar "[IsThisAValidDate/Answer]" "DayGreaterThan30"
   Else
      SetVar "[IsThisAValidDate/Answer]" "True"
   Endif
   Return
Endif
... feb (28 or 29 days)
... for [IsThisAValidDate/Month] 2
... is [IsThisAValidDate/Day] 29 (leap year) or 28 (otherwise)
...
... go check if it is leap year
SetVar "[IsThisALeapYear/Input]" "[IsThisAValidDate/Year]"
GoSub "IsThisALeapYear"
If "[IsThisALeapYear/Answer]" "=" "True"
   ... leap year ; day can be 29 or less
   If "[IsThisAValidDate/Day]" ">" "29"
      SetVar "[IsThisAValidDate/Answer]" "DayGreaterThan29"
   Else
      SetVar "[IsThisAValidDate/Answer]" "True"
   Endif
   Return
Else
   ... not a leap year ; day can be 28 or less
   If "[IsThisAValidDate/Day]" ">" "28"
      SetVar "[IsThisAValidDate/Answer]" "DayGreaterThan28"
   Else
      SetVar "[IsThisAValidDate/Answer]" "True"
   Endif
   Return
EndIf
...
SetVar "[IsThisAValidDate/Answer]" "True"
...
Return

Of course, if you plan on using such a function in many projects, you might consider turning the subroutine into a VisualNEO Win Function … something you might Call from any App in your computer … for example …
Call "gkIsThisAValidDate" "[TextEntry1]" "!/dmy" "[Result]"