Qbasicnews.com
September 20, 2021, 02:29:43 AM
 Pages: 1 2 3 [4]
 Author Topic: Write a bulletproof date validation routine.  (Read 42947 times)
Moneo
Na_th_an

Posts: 1971

 « Reply #45 on: July 24, 2006, 11:28:48 PM »

Wow, almost a year and half has gone by and I never posted my own solution to this challenge.

This is very old code that I've been using for years. Try it, you'll' like it.
Code:

DEFINT A-Z

DECLARE FUNCTION   NumStrict    (Z\$)
DECLARE FUNCTION   IsLeapYear%  (Z)

rem Setup days-per-month table.
DIM ZMO(1 TO 12)
DATA 31,28,31,30,31,30,31,31,30,31,30,31
FOR ZMM=1 TO 12:READ ZMO(ZMM):NEXT ZMM

REM ***********************************************************************

input "Enter date to be validated as YYYYMMDD ",z\$
gosub date.check
if date.ok=0 then print "Invalid Date" else print "Date ok"
SYSTEM

REM ****************  DATE.CHECK SUBROUTINE  **************************
REM *
REM *** VALIDATE A DATE IN YYYYMMDD FORMAT.
REM *
REM *  INPUT: Z\$       = Given date in format YYYYMMDD.
REM *
REM * OUTPUT: DATE.OK = -1 if input date is VALID.   (true)
REM *                    0 if input date is INVALID. (false)
REM *
DATE.CHECK:
DATE.OK = 0                          'preset to false
IF LEN(Z\$)<>8 THEN RETURN
IF NOT NUMSTRICT(Z\$) THEN RETURN
ZDD=VAL(RIGHT\$(Z\$,2))                'Set day
ZMM=VAL(MID\$(Z\$,5,2))                'Set month.
ZYY=VAL(LEFT\$(Z\$,4))                 'Set year.
IF ZMM<1 OR ZMM>12 OR ZDD<1 OR ZDD>31 THEN RETURN
IF ZMO(ZMM)+1*(-(ZMM=2 AND ISLEAPYEAR(ZYY))) < ZDD THEN RETURN
'      If expression (month=2 and is leapyear) is TRUE which is -1, then
'      taking the negative of this issues a plus 1. Conversely, FALSE
'      always gives a zero. Multiplying the +1 by this result of 1 or 0
'      will either add 1 or not to the number of days in the month.
'      The logic wants to add 1 only when it is February and leap year.
DATE.OK = -1        '-1=valid (true)
RETURN

END

' ====================== ISLEAPYEAR ==========================
'         Determines if a year is a leap year or not.
' ============================================================
'
FUNCTION IsLeapYear (Z) STATIC
' If the year is evenly divisible by 4 and not divisible
' by 100, or if the year is evenly divisible by 400, then
' it's a leap year:
IsLeapYear = (Z MOD 4 = 0 AND Z MOD 100 <> 0) OR (Z MOD 400 = 0)
END FUNCTION

' =================================================================

FUNCTION NumStrict (Z\$)
'
' *** CHECK FOR STRICTLY NUMERIC (NO NULL, NO NEGATIVE, NO DECIMAL)
'
NumStrict=0         'Init to False

IF Z\$="" THEN EXIT FUNCTION

FOR X = 1 TO LEN(Z\$)
A=ASC(MID\$(Z\$,X,1))
IF A<48 OR A>57 THEN EXIT FUNCTION
NEXT X

NumStrict = -1          'True

END FUNCTION
 Logged
Neo
Na_th_an

Posts: 2150

 « Reply #46 on: July 26, 2006, 08:06:13 AM »

Looks good... a year later.

I'd dearly suggest though to reform it in the more "modern" style of coding

Also, hi! *hides into lonely darkness again*
 Logged
yetifoot
Ancient Guru

Posts: 575

 « Reply #47 on: July 27, 2006, 12:17:08 AM »

Sorry for being off topic, but I wonder if you are able to check your email Moneo?  I sent you some mail about a topic I thought you may be interested in.
 Logged

EVEN MEN OF STEEL RUST.
Moneo
Na_th_an

Posts: 1971

 « Reply #48 on: July 27, 2006, 01:51:15 AM »

Quote from: "Neo"
Looks good... a year later.

I'd dearly suggest though to reform it in the more "modern" style of coding

Ok, give me a rundown on what is considered modern style code.

Hope you don't ask for colors 'cause I don't have that kind of editor, nor do I use any IDE.

Hi, back at you,
Moneo
 Logged
Moneo
Na_th_an

Posts: 1971

 « Reply #49 on: July 27, 2006, 01:53:50 AM »

Quote from: "yetifoot"
Sorry for being off topic, but I wonder if you are able to check your email Moneo?  I sent you some mail about a topic I thought you may be interested in.

Sorry, got my email on another machine. Will check it tomorrow, and get back to you.

*****
 Logged
yetifoot
Ancient Guru

Posts: 575

 « Reply #50 on: July 27, 2006, 02:52:31 AM »

Moneo: The date routine has one minor flaw, that other posters in the thread checked for.

I would check for Len(Z\$) <> 8 first, because this would indicate a bad date.

One entry that passes your check is 200002228 even though it is 9 chars.

Otherwise, great job.

I would probably do this for the day and month check, it appears more logical to me, it took me a while to figure yours out.

Code:
IF ZMM < 1 OR ZMM > 12 THEN RETURN
IF ZDD < 1 OR ZDD > (ZMO(ZMM) - ((ZMM = 2) AND ISLEAPYEAR(ZYY))) THEN RETURN
 Logged

EVEN MEN OF STEEL RUST.
Moneo
Na_th_an

Posts: 1971

 « Reply #51 on: July 28, 2006, 12:07:18 AM »

Quote from: "yetifoot"
Moneo: The date routine has one minor flaw, that other posters in the thread checked for.

I would check for Len(Z\$) <> 8 first, because this would indicate a bad date.

One entry that passes your check is 200002228 even though it is 9 chars.

Otherwise, great job.

I would probably do this for the day and month check, it appears more logical to me, it took me a while to figure yours out.

Code:
IF ZMM < 1 OR ZMM > 12 THEN RETURN
IF ZDD < 1 OR ZDD > (ZMO(ZMM) - ((ZMM = 2) AND ISLEAPYEAR(ZYY))) THEN RETURN

You're very observant, Yetifoot.

I fixed the code above to add the check for len(Z\$)<>8.
Sorry, the program of mine, that I lifted this code from, had already checked this, and my poor testing of this new program didn't reveal the error. Thanks.

Yeah, the code regarding February and leapyear is too hairy. That's why it needed 5 lines of comments to explain. You're right. It's unnecessarily complex. I thought it was cute at the time, back in 1994, when I was enamored with bitwise logic. I won't bother to change it now, because it does work. But feel free to make a change to simplify it.

Regards and thanks..... Moneo
 Logged
yetifoot
Ancient Guru

Posts: 575

 « Reply #52 on: July 28, 2006, 03:52:41 PM »

I prefered your method of hard coding the number of days per month, whilst i enjoyed the other posts methods of determing the number of days in a month (complicated, but interesting), I agree with the idea of keeping it fairly understandable when possible.

I like the IsLeapYear function also, I guess at first i would have been tempted to write a multiline IF..THEN based code, but it really is not necessary as shown in yours.  Earlier in the thread you said you felt your IsLeapYear was overcomplicated, but i disagree, i felt it was still very understandable.
 Logged

EVEN MEN OF STEEL RUST.
Moneo
Na_th_an

Posts: 1971

 « Reply #53 on: July 28, 2006, 08:11:34 PM »

Quote from: "yetifoot"
I prefered your method of hard coding the number of days per month, whilst i enjoyed the other posts methods of determing the number of days in a month (complicated, but interesting), I agree with the idea of keeping it fairly understandable when possible.

I like the IsLeapYear function also, I guess at first i would have been tempted to write a multiline IF..THEN based code, but it really is not necessary as shown in yours.  Earlier in the thread you said you felt your IsLeapYear was overcomplicated, but i disagree, i felt it was still very understandable.

Yeti, thanks for you constructive comments. I think we are now pretty much in agreement on all the coding issues of this task.

I hope we have impressed on others the need of having such a date validation routine tested and ready to be used in any program which receives a date as input from the user or even as input from another file.
Most programs don't contain good date validation logic simply because the programmer doesn't want to take the time to do it and test it while writing the main program. Often, the same happen with other validations like numeric amounts, numerics with decimals, validating codes, zipcodes, phone numbers, name and address fields, etc. These can be a pain in the butt if you don't have the logic ready-made, so they usually go unchecked --- "garbage in, garbage out."

Regards..... Moneo
 Logged
 Pages: 1 2 3 [4]