Qbasicnews.com

QbasicNews.Com => Challenges => Topic started by: Moneo on October 10, 2004, 10:00:45 PM



Title: Write a bulletproof date validation routine.
Post by: Moneo on October 10, 2004, 10:00:45 PM
Bulletproof date validation routine, subroutine or function.

GIVEN: An input string in the format of: YYYYMMDD

where:
YYYY  is the year (past, present or future: from 1600 to 3999)
MM is the month
DD is the day

CHALLENGE: Read the date input string and make absolutely sure that the represented date is valid.

VALIDITY: To be valid:
* The year must be from 1600 to 3999)
* The month must be from 01 to 12
* The day must be from 01 to the last day of the above month, considering leap year.
* Obviously, if the date input string is not 8 bytes long, the date is invalid.

OUTPUT: The message "VALID" or "INVALID".

If you haven't needed this routine yet, for sure you will need it someday.

I will test most entries, and tell you if it works, or on what date it fails.
*****


Title: My entry
Post by: Meg on October 11, 2004, 01:03:08 PM
Here's my entry. It's pretty straightforward. The DinM% formula looks pretty beastly, but I've triple-checked it, and it does the same thing as several lines of code that check the requirements for leap year. Lemme know if it needs further explanation.

*peace*

Meg.

Code:
FUNCTION Validate$ (d$)

'ASSUME TRUE UNLESS PROVEN FALSE
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Validate$ = "VALID"

'CHECK STRING LENGTH
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
IF LEN(d$) <> 8 THEN Validate$ = "INVALID": EXIT FUNCTION

'CHECK NUMERIC, LEADING ZERO, "-", "D" NOT A PROB
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
IF INSTR(UCASE$(d$), "D") OR INSTR(d$, "-") THEN Validate$ = "INVALID": EXIT FUNCTION
IF LTRIM$(RTRIM$(STR$(VAL(d$)))) <> d$ THEN Validate$ = "INVALID": EXIT FUNCTION

'EXTRACT YEAR, MONTH, DAY
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
y% = VAL(LEFT$(d$, 4))
m% = VAL(MID$(d$, 5, 2))
d% = VAL(RIGHT$(d$, 2))

'GET DAYS IN MONTH. IF FEB, CHECK LEAP YEAR
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DInM% = 31 + (m% MOD 2) * (m% > 7) + (m% MOD 2 XOR 1) * (m% < 8)
IF m% = 2 THEN DInM% = 28 - ((y% MOD 4 = 0) + (y% MOD 100 = 0) * NOT (y% MOD 400 = 0))

'CHECK THE RANGES
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
IF y% < 1800 OR y% > 3999 THEN Validate$ = "INVALID": EXIT FUNCTION
IF m% < 1 OR m% > 12 THEN Validate$ = "INVALID": EXIT FUNCTION
IF d% < 1 OR d% > DInM% THEN Validate$ = "INVALID": EXIT FUNCTION

END FUNCTION


-=-EDITS-=-
10/12/2004: included check for "-" and "D". Changed date from 1600 to 1800.


Title: Write a bulletproof date validation routine.
Post by: Z!re on October 11, 2004, 04:40:14 PM
:o

wow... Thats... short... I'd imagine lots of more code, then again, i didn't even try to do it :P


Title: Write a bulletproof date validation routine.
Post by: Meg on October 11, 2004, 05:09:30 PM
Moneo made the challenge a lot easier by only allowing the years 1600-3999.  If he included earlier dates, the challenge gets harder because you have to take into account all the calendar shifts with respect to leap years.


Title: Write a bulletproof date validation routine.
Post by: Antoni Gual on October 11, 2004, 08:11:42 PM
In thta case you must consider the country you are on, as the calendar shifts did'nt take place in ths same day.

For example UNESCO has declared 23 April the World Book day,  because in that day of 1616 both William Shakespeare and Miguel de Cervantes died. In fact they died  with  twenty-one days of interval. Spain  was using then the Gregorian (modern) calender and England was using the Julian calender....


Title: Write a bulletproof date validation routine.
Post by: Moneo on October 12, 2004, 12:21:42 AM
Antoni has a valid point. My first choice of the "from" date was 1800. Maybe we should change the 1600 to 1800 to cover the differences mentioned by Antoni.

MEG, you're solution looks pretty good except for the VAL statement. My experience has been that certain values containing a "D" make QB think it is an exponential value. I have to look up an old validation routine where the "D" made it fail.
Give me a few days to thoroughly check out your routine. I intend putting it inside a loop to check all the date combinations against a routine that has been working for me for years.
*****


Title: Write a bulletproof date validation routine.
Post by: Z!re on October 12, 2004, 06:52:56 AM
Code:
If INSTR(d$, "D") Then Validate$ = "INVALID"
If INSTR(d$, "-") Then Validate$ = "INVALID"


Title: updated
Post by: Meg on October 12, 2004, 12:01:14 PM
Moneo:  Yes, you're right.  The program snags on "200D1010" with an overflow error.  I added a line to cover this, and changed the date from 1600 to 1800, as noted.


Title: Write a bulletproof date validation routine.
Post by: Moneo on October 14, 2004, 11:53:59 PM
Meg, I ran your validation logic side by side with mine in the same program, in a loop from years 1800 to 3999, months from 1 to 12, and days from, 1 to 31. The days going to 31 will generate "invalid" dates.

Well, your logic and mine agreed 100% with invalids and valids.

Excellent work, Meg. :bounce:
*****


Title: Write a bulletproof date validation routine.
Post by: Moneo on October 16, 2004, 10:55:46 PM
Meg,

I can't understand the DinM logic. Could you explain it? Did you derive this or did you "lift" it from somewhere?

I get the number of days in a month from a little table which has 28 for February, month 2. Then if month 2 and leap year, I add one. Not elegant, but easy to understand.

A suggestion on the next line that tests if month 2 and adds 1 if it's leap year. The leap year logic here is embedded and specifically tailored to adding 1 if leap year. I suggest that you have one and only one function for determining leap year, and that you always use it regardless of the program's needs. Otherwise, you have different implementations of the leap year logic depending on the program, which increases the risk of error.

The following is the leap year function I've been using for about 15 years. The result is -1 (true) or 0 (false).
Code:
' ====================== 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

*****


Title: Write a bulletproof date validation routine.
Post by: Meg on October 17, 2004, 12:49:21 PM
These are formulas I came up with.  I'll try to explain them.

ok when you test an equation, QB returns 0 (false) or -1 (true).

even numbered months before august (month#8) and odd numbered months after July (month#7) have 30 days (except feb, but that's covered in line 2)

So, line 1 is an equation that starts with 31 days and adds the validity of the above statement.  If it's true, it subtracts 1, leaving 30 days.

Code:
DInM% = 31 + (m% MOD 2) * (m% > 7) + (m% MOD 2 XOR 1) * (m% < 8)


I'll break it apart:

Code:
31                      default value
m% MOD 2                returns 0 if date is even, 1 if date is odd
m% > 7                  returns -1 if date is > 7, 0 if date < 8
m% MOD 2 XOR 1          returns 0 if date is odd, 1 if date is even
m% < 8                  returns -1 if date is < 8, 0 if date > 7


now, we multiply the first two and the second two together:

Code:
(m% MOD 2) * (m% > 7)                returns -1 if date is odd and > 7
(m% MOD 2 XOR 1) * (m% < 8)          returns -1 if date is even and < 8


so if either one of these is true, 31 gets added to -1 and 0 for a result of 30
otherwise, 31 gets added to 0 and 0 for a result of 31
since both products can't be -1, you never end up with 31+ -1 + -1 = 29.

Code:
IF m% = 2 THEN DInM% = 28 - ((y% MOD 4 = 0) + (y% MOD 100 = 0) * NOT (y% MOD 400 = 0))


This line does pretty much the same thing, only it starts with 28 as the default value and adds 1 if:

1. the year is evenly divisible by 4, unless the year is evenly divisible by 100 and not evenly divisible by 400.

Code:
28                      default value
y% MOD 4 = 0            returns -1 if year is divisible by 4, otherwise 0
y% MOD 100 = 0          returns -1 if year is divisible by 100, otherwise 0
y% MOD 400 = 0          returns -1 if year is divisible by 400, otherwise 0


I hope this clears it up!

*peace*

Meg.

edit:  another alternative to line #1 is:

Code:
DinM% = VAL(MID$("312831303130313130313031", m% * 2 - 1, 2))


so using your bit of code (which i like!) it would be this:

Code:
DinM% = VAL(MID$("312831303130313130313031", m% * 2 - 1, 2))
IF DinM% = 2 then DinM% = 28 - ((m% MOD 4 = 0 AND m% MOD 100 <> 0) OR (m% MOD 400 = 0))


Title: Write a bulletproof date validation routine.
Post by: Moneo on October 17, 2004, 09:16:23 PM
Meg,

Thanks for breaking down the explanation of your DinM logic. It's very clever, perhaps too clever since it takes more than 10 lines to explain it. I prefer the table-of-days-per-month approach, since it requires no explanation.

I have some similarly clever boolean logic on one line of code in one of my utility programs. It's been working for over 10 years, but every time I look at it, I vow to change it to something simple the next time I update it. This line of code is followed by about 8 lines of comments to explain it, and I myself still have trouble understanding it. I wonder who said KISS (Keep It Simple Stupid). There's a lot of truth in this.

Back when I was an assembly language programmer, we would go  running to a colleague and say "look, I was able to code that logic in only 5 instructions". Normally, the less instructions, the more efficient it was. The thinking was: the shorter the program, the less bugs it should have. But, of course, you break the barrier of precise, efficient code when your code gets so clever that others can't figure it out --- not even yourself in time.

I'm glad you liked my leap year function. It also is on the borderline of being too clever. I'm sure you're aware that the leap year logic has been expanded for some time now to include an exception for years that are a multiple of 4000, similiar to the current logic for multiples of 400. I chose not to worry about it, so I limit my years up to 3999.
*****


Title: Write a bulletproof date validation routine.
Post by: Meg on October 18, 2004, 02:13:58 AM
I think I like this one best:

Code:
DinM% = VAL(MID$("312831303130313130313031", m% * 2 - 1, 2))


I wish I'd thought of that when I wrote the original.


Title: Some thoughts from the real world experience...
Post by: ToohTooh on October 22, 2004, 04:18:03 PM
Yes, some thoughts from the real world experience...
    NOTICE:
    Code below was **NEVER** tested! I'm bored at the office and compiled this one!.. If possible, debug it and, kindly ask me to edit the post (or, post the correct one)!..[/list]To catch your attention later, list starts off from 2, and this is not a bug -- keep reading! :-D

    Quote
    OUTPUT: The message "VALID" or "INVALID".
    (2) To me, this doesn't necessarily mean the function returning the actual word. If not needed, functions **DO NEVER RETURN STRINGS** (yes, they're like math functions)!..

    (3) You set beautiful traps for validating the date. Now, let's think: If traps are there, they want to end the function in error. If every trap means 'error,' you can assume FALSE at the beginning to shorten your code and make up a good logic (and this is a convention for this type of code -- altered version goes like the one below).

    You'll think: Doesn't it do the job? Well, it might... But I can't figure out how it does. So...

    (4) Break it into the most granule form. If you cannot at first (which is natural), study on paper before typing it out. I remember spending days on how to simplify and globalize a drag-and-drop support. It wasn't easy: Not only should it have worked flawlessly on icons, but on "every thing." (Like some piece of text you'll select and drag to an input box).

    You might have this:
    Code:
    ' ***
    '-- Moneo's Date Validation Challenge: An Alternative Approach
    '-- Explore under QB IDE.
    ' ***

    '-- by ToohTooh.


    FUNCTION IsAValidDate(Da$)

    IsAValidDate = 0  '>> Assume false

    '-- Break Da$ into 'day,' 'month,' and 'year.'
    IF NOT (Tokenize(Da$, day, month, year)) THEN EXIT FUNCTION

    '-- Sanity checks.
    IF (day < 1) OR (day > MaxDayForMonth(month, year)) THEN EXIT FUNCTION
    IF (month < 1) OR (month > 12) THEN EXIT FUNCTION
    IF (year < 1600) OR (year > 3999) THEN EXIT FUNCTION

    '-- If we got here, then all traps were defeated. Send success.
    IsAValidDate = -1

    END FUNCTION  '>> IsAValidDate()


    FUNCTION MaxDayForMonth(Mo, Ye)

    SELECT CASE Mo
    CASE 1, 3, 5, 7, 8, 10, 12: MaxDayForMonth = 31
    CASE 4, 6, 9, 11: MaxDayForMonth = 30
    '-- Sniff the leap year case for February.
    CASE 2: IF (IsALeapYear(Ye)) THEN MaxDayForMonth = 29 ELSE MaxDayForMonth = 28
    '-- Think about below:
    '    Q: Why 100 but not 99?  A: Tokenize() will have trapped it.
    '    Q: Do we need it?       A: Both yes and no. Think.
    CASE ELSE: MaxDayForMonth = 100  '>> Just being safe...
    END SELECT

    END FUNCTION  '>> MaxDayForMonth()


    FUNCTION IsALeapYear(Ye)

    '-- Based on re-interpreting Moneo's info.

    IF (Ye MOD 4 = 0) THEN
        '-- Divisible by 4. Probably a leap year. Now see if it has to do with
        '    some rare exceptions.
        IF (Ye MOD 100 = 0) THEN
            IsALeapYear = (Ye MOD 400 = 0)
        ELSE
            IsALeapYear = -1
        END IF
    ELSE
        IsALeapYear = 0
    END IF

    END FUNCTION  '>> IsALeapYear()


    FUNCTION Tokenize(Da$, day, month, year)

    Tokenize = 0

    Da$ = RTRIM$(LTRIM$(Da$))  '>> Idea from Meg.
    IF (LEN(Da$)) <> 8 THEN EXIT FUNCTION

    day = VAL(MID$(Da$, 1, 2))    '>> Move rover once, and...
    month = VAL(MID$(Da$, 2, 2))  '>> ...twice, and...
    year = VAL(MID$(Da$, 4, 4))   '>> ...thrice to extract.

    Tokenize = -1  '>> All done.

    END FUNCTION  '>> Tokenize()
    You are trying to show off some kind of binary and conventional math skills, but the explanation is longer than the code itself... Your mentor wouldn't like it...

    (5) Take every situation into account. You already did: Your R/LTRIMming was awesome... I never thought it...

    (6) TEST, TEST, TEST, and TEST... You needn't for a small one like this, but remember: In daily builds, we are using automatas which can use applications just like humans. Have that in mind.

    (7) No hacks. This means re-organizing code so that exceptional cases are minimal. This often requires good planning. Some are inevitable and must be clearly commented: 0! = 1 (Factorial 0 is an exception and equals to 1 -- was it?).

    (8) Minimal hard-coding! You should centralize data to retrieve it from a minimum number of places! (In fragments of Win2K source, they hard-coded file extensions into actual source code in some places! Say it changed! What then? Touch the code base, find the source, update it, inform everyone, update API docs, RECOMPILE, blah blah...
      NOTICE: I'm not judging Microsoft's expertise here, and as of yet, I am not skilled enough to.[/list]And...

      (1) DO A RESEARCH! Find the most acceptable, efficient ways of completing tasks!.. Are you retrieving files? May be dynamic hashing is for you. Go and look for some articles. Are you developing a word processing app? Tries are just the ticket for you!.. Are you sniffing for the presence of some text, some file name in your linked list? Why aren't you keeping the 128-bit signature of the complete list in array[0] to guess it on-the-fly? Ways are infinite!..

      For violation of (7), (8) and (1), thread http://forum.qbasicnews.com/viewtopic.php?t=7054 codes are great examples.

      If I am to tweak a saying in the famous book, 'Language, Proof and Logic,' it should go like
        'Computer languages are just like our every day languages... The more we practice, the more we talk among native speakers, it'll get better and better.'
      However, we should watch ourselves carefully while talking: I once read in a CV writing how-to book which said,
        "Avoid this in your CV:
      Quote
      'Please find those listed hereunder enclosed within.'
      Do we talk that way?"[/list]
      So, do we? We don't talk that way in real life. This post won't be a magic wand to correct BASICers' programming practices, but know one thing all that is: We don't talk that way.

      I know, I know... I can hear mumbles of 'but this is a challengeeeee...' Well, do challenges need to encourage poor programming practices?

      Last but not least, considering the ages of QBasicers, I think their ways of doing tasks should be accepted extra-ordinary. Having written this, I must send anyway!.. :,-(

      Anyway, some thoughts...

      NOTICE: BASIC is a great lab for us, this is true!.. But, you know... We should change labs when we advance. This doesn't mean hating the old one, or forgetting about it. Just leaving it...


      Title: /nod
      Post by: Meg on October 22, 2004, 05:34:18 PM
      I agree with almost everything ToohTooh says here.

      #2 - If this were a real-world piece of code I was writing, I'd definitely make it numeric instead of text function.  This was text because the challenge said it was supposed to be.

      #3 - I agree that the default value should be false.  It would lead to more efficient code.

      #4 - I'm a bit iffy on this one.  Generally, I'm in favor of splitting code apart into tiny routines.  However, if I have a routine that's short--like this one--and easy to follow, I find that splitting it into more routines can make it unnecessarily difficult to follow if I have to go back to for understanding later.  In my opinion, some routines are fine to have as self-contained code, particularly ones that contain no loops which can be followed easily from top to bottom.

      #5,6,7,8,9 - Agreed.

      Quote
      You are trying to show off some kind of binary and conventional math skills, but the explanation is longer than the code itself... Your mentor wouldn't like it...


      In all honesty, I was being a bit lazy.  I lifted the days-in-a-month code from an earlier challenge I entered:

      http://forum.qbasicnews.com/viewtopic.php?t=3402

      Moneo's been good about kicking me for writing code that's too complex for the problem.  In this instance, I feel that this code would have been better for calculating base days in a month:

      Code:
      DinM% = VAL(MID$("312831303130313130313031", m% * 2 - 1, 2))


      and Moneo's code was far simpler than mine for doing the leap-year adjustment.

      Thanks for the feedback and real-world spin!

      *peace*

      Meg.


      Title: Re: Some thoughts from the real world experience...
      Post by: oracle on October 23, 2004, 12:06:11 AM
      Quote from: "ToohTooh"
      (5) Take every situation into account. You already did: Your R/LTRIMming was awesome... I never thought it...


      In particular, it's a good idea to write as many test cases as possible before writing the actual function/method/class (in whatever language you're coding in). (Black box testing).


      Title: Re: Some thoughts from the real world experience...
      Post by: Moneo on October 23, 2004, 10:38:09 PM
      Quote from: "ToohTooh"
      Yes, some thoughts from the real world experience...
        NOTICE:
        Code below was **NEVER** tested! I'm bored at the office and compiled this one!.. If possible, debug it and, kindly ask me to edit the post (or, post the correct one)!..[/list]To catch your attention later, list starts off from 2, and this is not a bug -- keep reading! :-D

        Quote
        OUTPUT: The message "VALID" or "INVALID".
        (2) To me, this doesn't necessarily mean the function returning the actual word. If not needed, functions **DO NEVER RETURN STRINGS** (yes, they're like math functions)!.....


        Your remarks about the Valid Invalid strings are true. I used these specific strings to emphasize the result for the challenge. For "real world" as you say, they would return true (-1) or false (0).

        Your remark of "code below was never tested!" completely turned me off regarding any appreciation of your suggested code "improvements".
        *****


        Title: Write a bulletproof date validation routine.
        Post by: Neo on October 24, 2004, 07:22:15 AM
        Here's what I made today (in 20 minutes):

        [syntax="QBasic"]DECLARE FUNCTION IsValidDate% (TheDate AS STRING)

        '$DYNAMIC
        DEFINT A-Z

        '+-----------------------------------------------------+
        '| FUNCTION IsValidDate                                |
        '|                                                     |
        '| This function takes a string of 8 characters. This  |
        '| string indicates a specific date in the format      |
        '| YYYYMMDD. In which YYYY must be in the range 1800   |
        '| to 3999. MM must be in the range 01 to 12. And DD   |
        '| must have a leading zero if DD < 10.                |
        '|                                                     |
        '| The function returns -1 if the specific date is a   |
        '| valid date, otherwise it returns 0.                 |
        '|                                                     |
        '| Neo                                                 |
        '+-----------------------------------------------------+
        FUNCTION IsValidDate (TheDate AS STRING)
           'first check for correct input (only numbers, and string must be 8 characters in length)
           TD$ = LTRIM$(RTRIM$(TheDate))
           Allowed$ = "0123456789"
           FOR I = 1 TO LEN(TD$)
              IF INSTR(Allowed$, MID$(TD$, I, 1)) = 0 THEN IsValidDate = 0: EXIT FUNCTION
           NEXT I
           IF LEN(TD$) <> 8 THEN IsValidDate = 0: EXIT FUNCTION

           'check for the year (must be >= 1800 and <= 3999)
           Year = VAL(LEFT$(TD$, 4))
           IF NOT(Year >= 1800 AND Year <= 3999) THEN IsValidDate = 0: EXIT FUNCTION

           'check for the month (must be >= 1 and <= 12)
           Month = VAL(MID$(TD$, 5, 2))
           IF NOT(Month >= 1 AND Month <= 12) THEN IsValidDate = 0: EXIT FUNCTION

           'check if year Year is a leap year
           IsLeapYear = ((Year MOD 4 = 0) AND (Year MOD 100 <> 0)) OR (Year MOD 400 = 0)

           'prepare maxdays variable (numbers of days in the month)
           I = Month
           MaxDays = 30 + ((I + (I > 7)) MOD 2) + 2 * (I = 2) + (I = 2) * IsLeapYear

           'check for the day (must be >= 1 and <= MaxDays)
           Day = VAL(MID$(TD$, 7, 2))
           IF NOT(Day >= 1 AND Day <= MaxDays) THEN IsValidDate = 0: EXIT FUNCTION

           IsValidDate = -1
        END FUNCTION[/syntax]

        I hope it works ;) (I've tested it numerous times and it seems to work ;))

        Note that this entry was written for clarity. I.e., it could have been done shorter, but I think it's much more clear this way :)

        Another note: this entry was created in 20 minutes, without any help from online resources or previously submitted entries.

        Btw, if you like this function to return "VALID" or "INVALID", you could easily make a wrapper:
        [syntax="QBasic"]DECLARE FUNCTION IsValidDateWrapper$ (TheDate AS STRING)

        FUNCTION IsValidDateWrapper$ (TheDate AS STRING)
           IsValidDateWrapper$ = LTRIM$(MID$("IN  ", 2 * ABS(IsValidDate(TheYear)) + 1, 2) + "VALID")
        END FUNCTION
        'note: wrapper not tested but should work ;)[/syntax]

        Also, if you like to see what the short version looks like (the unclear one): ;)
        [syntax="QBasic"]FUNCTION IsValidDate (TheDate AS STRING)
           IsValidDate = 0
           TD$ = LTRIM$(RTRIM$(TheDate))
           FOR I = 1 TO LEN(TD$)
              IF INSTR("0123456789", MID$(TD$, I, 1)) = 0 THEN EXIT FUNCTION
           NEXT I
           IF LEN(TD$) <> 8 THEN EXIT FUNCTION
           IF VAL(LEFT$(TD$, 4)) < 1800 OR VAL(LEFT$(TD$, 4)) > 3999 OR VAL(MID$(TD$, 5, 2)) < 1 OR VAL(MID$(TD$, 5, 2)) > 12 THEN EXIT FUNCTION
           I = VAL(MID$(TD$, 5, 2)) : J = VAL(LEFT$(TD$, 4))
           IF VAL(MID$(TD$, 7, 2)) < 1 OR VAL(MID$(TD$, 7, 2)) > 30 + ((I + (I > 7)) MOD 2) + 2 * (I = 2) + (I = 2) * (((J MOD 4 = 0) AND (J MOD 100 <> 0)) OR (J MOD 400 = 0)) THEN EXIT FUNCTION
           IsValidDate = -1
        END FUNCTION[/syntax]


        Title: Clarification of suggestion numbered (4), and an example.
        Post by: ToohTooh on October 25, 2004, 05:15:25 AM
        Hello, Moneo.

        You're right on the discouraging header "note (!)" This was 22:30 coding, and, truly speaking, it went the way you saw to mean that I "didn't have a QBasic interpreter at my office computer and, was too lazy and tired to download one!" For the healthiest mind, this was too misleading!

        The remark conflicts with those I have written, but have in mind: My experience doesn't rely on such a simple one.

        Your appreciating "criterias" are another story, and I'm quite shocked that you have been much short sighted: Can a single line stand for the whole? Programming methodology and documenting are different. If you would have spotted some technical places you oppose, and shown relationships of them with your own, we could talk. But, as of yet, there's no point in that.

        And, Meg. Hello!

        I'd like to clarify your concerns about my suggestion numbered (4) by giving a brief understanding of Discrete Maths. This is the area of maths which deals with discrete, unconnected objects, events which we use in analyzing our programming methodology -- that is algorithms, artificial intelligence applications, and so forth. Let me divide this into two to make it more clear:

        (a) Algorithmic thinking: This deals with the ways we use to build unique solutions to the unique problems. My IsAValidDate() is a good example of this, and it is pretty well written: You can subdivide it into discrete sets to understand or test separately [IsALeapYear(), Tokenize()].

        (b) Discrete structures: This deals with the ways we include "others' solutions" to our projects. What are they? Binary trees they are, linked lists they are, hashing they are. On many of them, much discussion have been made, and they are 'de facto' standards in some cases. Please refer to my suggestion numbered (1) of my past post to understand what I mean.

        What is common in (a), and (b)? They let us develop our programming methodologies in a way so that we can examine and analyze them, and represent them in some metrics (Big-O, anyone?).

        So, my efforts on suggesting 'break them down!' aren't just a matter of obsession, but the Computer Science itself.

        Let's say our IsAValidDate() went international to meet different sorting of Date, Month, Year trilogy. If we were to pack everything into a single function, testing and updating it would be tad more painful. Think of giant games, database systems, or operating systems functions!.. But in this case, we only need to update and test result = Tokenize(...) or better, just implement
        Code:
        result = Tokenize(FormatDa$(Da$), ...)
        and only test FormatDa$() to see if it meets our criterias.

        If you are interested, I may suggest you some books on Discrete Maths. But, better, search for them in your favorite searching engine. Even better, go to your local library and find the one which suits you best.

        Glad if I was of some help.
        _____

        Edit history:
        (i) Some spelling corrected.


        Title: Re: Clarification of suggestion numbered (4), and an example
        Post by: oracle on October 25, 2004, 08:35:57 PM
        Quote from: "ToohTooh"
        (Big-O, anyone?).


        Mememe!

        Having said what you did however, creating a whole function to do some very simple algorithm may do nothing more than clutter up the namespace and confuse some people... not so much a problem in object orientated languages however!


        Title: Write a bulletproof date validation routine.
        Post by: Moneo on October 25, 2004, 11:03:38 PM
        Quote from: "Neo"
        Here's what I made today (in 20 minutes):.....

        Looks interesting. Give me some time to run a test on it (more than 20 minutes) like I did with Meg's solution. I'll get back to you.
        *****


        Title: Re: Clarification of suggestion numbered (4), and an example
        Post by: Moneo on October 25, 2004, 11:10:55 PM
        Quote from: "ToohTooh"
        Hello, Moneo......

        ToohTooh,
        Your dissertations are much Tooh much for me. :wink:
        *****


        Title: Write a bulletproof date validation routine.
        Post by: Neo on October 26, 2004, 06:59:22 AM
        Moneo:

        Ok :)
        Btw:
        [syntax="QBasic"]'prepare maxdays variable (numbers of days in the month)
                I = Month
                MaxDays = 30 + ((I + (I > 7)) MOD 2) + 2 * (I = 2) + (I = 2) * IsLeapYear[/syntax]
        If this piece of code needs some explanation, just tell me ;)


        Title: Foo
        Post by: ToohTooh on October 26, 2004, 02:36:36 PM
        Let's handle those!..

        Hello, oracle!..
        Quote
        Having said what you did however, creating a whole function to do some very simple algorithm may do nothing more than clutter up the namespace and confuse some people... not so much a problem in object orientated languages however!
        :humm: I think my 'dissertation' wasn't to the point on your side. *Sigh*, and such...

        And, Mexico City!.. Hola!..
        :o
        Quote
        ToohTooh,
        Your dissertations are much Tooh much for me. :wink:
        *****
        You are getting on a bit, Moneo. :D


        Title: Reply on Clarification Suggestion
        Post by: Neo on October 26, 2004, 03:37:14 PM
        ToohTooh:

        Quote from: "ToohTooh"
        I'd like to clarify your concerns about my suggestion numbered (4) by giving a brief understanding of Discrete Maths. This is the area of maths which deals with discrete, unconnected objects, events which we use in analyzing our programming methodology -- that is algorithms, artificial intelligence applications, and so forth.

        To be exact, Discrete Maths (a part of a.o. Linear Algebra) is the face of maths that deals with functions that cannot be expressed in multiple "normal" maths functions (like +, -, *, /, ^, int, diff, etc). Also, the return value of these discrete functions may differ completely from those in Algebra. Resulting from these 2 statements, we conclude that Discrete Maths cannot be analyzed by standard algebra only. That's why there is Discrete Maths. The easiest example of a discrete formula is:
        Code:
        f(x): x -> INT(x);
        As you can see, this formula cannot be expressed in a normal algebraic formula.
        Using this Discrete Maths, we can now analyze trends without using incredibly difficult algebra. The perfect example for this is trying to find an discrete formula for the amount of days in a specific month of a specific year. ;) This would then look something like this (my code):
        Code:
        f(m, y): m, y -> 30 + ((m + (m > 7)) MOD 2) + 2 * (m = 2) + (m = 2) * (((y MOD 4 = 0) AND (y MOD 100 <> 0)) OR (y MOD 400 = 0))

        Of course, this formula will only be valid from a specific date in the past, as there were severe calendrical changes around year 1600-1700 and earlier.

        Quote from: "ToohTooh"
        (a) Algorithmic thinking: This deals with the ways we use to build unique solutions to the unique problems. My IsAValidDate() is a good example of this, and it is pretty well written: You can subdivide it into discrete sets to understand or test separately [IsALeapYear(), Tokenize()].

        Exactly, but keep in mind that we need not build unique solutions for each unique problem. Many solutions for completely different problems may well suit on a specific other problem.

        Quote from: "ToohTooh"
        (b) Discrete structures: This deals with the ways we include "others' solutions" to our projects. What are they? Binary trees they are, linked lists they are, hashing they are. On many of them, much discussion have been made, and they are 'de facto' standards in some cases. Please refer to my suggestion numbered (1) of my past post to understand what I mean.

        I'd like to make a note about this, correct me if I'm wrong. I think Binary Trees and Linked Lists are no specific solutions to any problem, so I don't think they will fit in as an example. This, as binary trees and linked lists were made in order to accomplish a specific goal using those things. In most cases, binary trees and linked lists are just ways or utilities to accomplish a goal. When cut loose from its purpose-serving environment, Binary Trees and Linked Lists are useless and are no goal on its own.

        Quote from: "ToohTooh"
        So, my efforts on suggesting 'break them down!' aren't just a matter of obsession, but the Computer Science itself.

        Correct. It is essential in any field of computer science (and other) to break apart bigger problems into smaller, easier to solve problems. Of course, many big problems can be split apart in many different ways, and it's the trick to find a way that suits well your needs.

        Quote from: "ToohTooh"
        Let's say our IsAValidDate() went international to meet different sorting of Date, Month, Year trilogy. If we were to pack everything into a single function, testing and updating it would be tad more painful. Think of giant games, database systems, or operating systems functions!.. But in this case, we only need to update and test result = Tokenize(...) or better, just implement
        Code:
        result = Tokenize(FormatDa$(Da$), ...)
        and only test FormatDa$() to see if it meets our criterias.

        Make your functions orthogonal! It's the key in large scale programming. Making your projects this way will save you extremely much hassle later on, either when you need to correct bugs, or when you need to globalize your program (make it accept multiple data formats, often bound to specific countries).
        As said before, the ideal orthogonal function doesn't depend on anything else but itself. In reality though, this ideal goal is often too imaginary to pursue. Try to make it as orthogonal as possible.

        Quote from: "ToohTooh"
        If you are interested, I may suggest you some books on Discrete Maths. But, better, search for them in your favorite searching engine. Even better, go to your local library and find the one which suits you best.

        I have a very good book "The Pragmatic Programmer". It teaches extremely helpful things needed in programming.

        Hope I was a bit useful,

        Neo


        Title: Re: Reply on Clarification Suggestion
        Post by: oracle on October 26, 2004, 06:00:56 PM
        Quote from: "Neo"
        correct me if I'm wrong. I think Binary Trees and Linked Lists are no specific solutions to any problem, so I don't think they will fit in as an example. This, as binary trees and linked lists were made in order to accomplish a specific goal using those things. In most cases, binary trees and linked lists are just ways or utilities to accomplish a goal. When cut loose from its purpose-serving environment, Binary Trees and Linked Lists are useless and are no goal on its own.


        That's like saying the square root function has no use on it's own ;). The square root function only helps you to accomplish a goal, but by itself it only calculates square roots. Binary trees only help you to accomplish a goal, but by itself only stores data providing log(n) add, search and remove (providing the tree remains height balanced).

        ADTs are a way of storing collections of data, so in a sense they solve the problem of storing collections of data ;)


        Title: Hmm...
        Post by: ToohTooh on October 27, 2004, 06:12:11 AM
          Tin openers, binary trees, and Anatolia Music;
          Car radios, orthogonality, and OO.
        Neo, I like it!..

        Let's take a peek... :wink:

        Quote
        I'd like to make a note about this, correct me if I'm wrong. I think Binary Trees and Linked Lists are no specific solutions to any problem, so I don't think they will fit in as an example. This, as binary trees and linked lists were made in order to accomplish a specific goal using those things. In most cases, binary trees and linked lists are just ways or utilities to accomplish a goal. When cut loose from its purpose-serving environment, Binary Trees and Linked Lists are useless and are no goal on its own.
        Well, no... This is like saying
        Quote
        I have a thing for tin openers; will they teach me how to design one if I am to enroll at Mechanical Engineering?
        When you enroll at mechanical engineering, they won't be teaching you to design a tin opener. But, you will learn 'principles of designing.'

        Binary Trees are just the same: It's your own duty to tailor them to your own needs. Computer Scientists of Turkish Ministry of Culture are designing a virtual 'Music Museum.' Artists will be able to search for matching notes. They are using Patricia Tries. Were scientists thought musical searching through Patricia Tries? Probably not (well, some new programmes have 'Musical Searching' CS courses in Turkey).

        Quote
        Make your functions orthogonal!
        Your intent isn't clearly understood on the 'orthogonal design.' May be you are confused. Did you mean 'orthogonal expansions of recursive functions?' Or, 'orthogonal system design' of software engineering? I was thought some 'orthogonality' in Business Statistics when was in Business School, too. Either case, it would go beyond the scope of this forum.

        'Orthogonally designed software system' is one which enables us to make complex designs feasible and compact. Orthogonality is achieved when technical effects (produced by a component) of modifying the system do not create or propagate side effects to other components of the system. I like it best when orthogonality meets encapsulation of object oriented languages.

        Let's give an example: Does your car's engine start when you turn the radio on? Probably, not: Turning the radio on should influence no system other than the radio itself. System is orthogonal.

        Much software is designed in a non-orthogonal fashion: Even much of Windows. Because, it is very hard to design one orthogonally. Personally, I haven't designed one, either (hey, I'm only 22).

        Some books should occupy the rest!.. Happy reading!..

        Orthogonality:
        The Art of Unix Programming (http://www.faqs.org/docs/artu/).

        Programming methodology in general and programming language theory:
        Concepts in Programming Languages (Cambridge University Press, 2002), and
        Theoretical Aspects of Object-Oriented Programming (MIT Press, 1994).

        Discrete Mathematics:
        (No books at my office -- I'll compile when I get home!)

        Do a related search at:
        http://www.lib.metu.edu.tr/
        http://www.library.itu.edu.tr/
        http://www.library.boun.edu.tr/
        _____

        Edit history:
        (i) Links added, spelling corrected.


        Title: Write a bulletproof date validation routine.
        Post by: Meg on October 27, 2004, 11:11:26 AM
        ... am I the only person that has absolutely no idea what is going on in this thread?


        Title: Re: Hmm...
        Post by: Neo on October 27, 2004, 02:12:23 PM
        Canopeners... music...

        Quote from: "ToohTooh"
        When you enroll at mechanical engineering, they won't be teaching you to design a tin opener. But, you will learn 'principles of designing.'
        Binary Trees are just the same: It's your own duty to tailor them to your own needs. Computer Scientists of Turkish Ministry of Culture are designing a virtual 'Music Museum.' Artists will be able to search for matching notes. They are using Patricia Tries. Were scientists thought musical searching through Patricia Tries? Probably not (well, some new programmes have 'Musical Searching' CS courses in Turkey).

        I noticed my thinking mistake after oracle posted his reply. Sorry about it. (I think I missed some steps in my logical thinking process).

        Quote from: "ToohTooh"
        Your intent isn't clearly understood on the 'orthogonal design.' May be you are confused. Did you mean 'orthogonal expansions of recursive functions?' Or, 'orthogonal system design' of software engineering? I was thought some 'orthogonality' in Business Statistics when was in Business School, too. Either case, it would go beyond the scope of this forum.

        Every time I post "serious" posts like last one, it is preceded by a lot of thinking and logical deduction. I couldn't have possibly been confused, as I clearly have the same definition of orthogonality. The only thing might be that I explained it fuzzy and perhaps even incorrect. Again, sorry.

        The orthogonality principle has much to do with the DRY principle. (DRY stands for "Don't Repeat Yourself"). And the programming rule derived from orthogonality:
        Code:
        PROGRAMMING RULE 13:
        ELIMINATE EFFECTS BETWEEN UNRELATED THINGS
        Design components that are self-contained, independent, and have a single, well-defined purpose.

        (If you want to see more programming rules, just ask or PM me ;))

        This is my definition of orthogonality:
        Orthogonality is a term borrowed from geometry. In computing, this term has come to signify a kind of independence or decoupling. Two or more things are orthogonal if changes in one do not affect any of the others.

        The perfect example of a non-orthogonal system is an aircraft or helicopter. (Let's talk about aircrafts, I know more about them than about helicopters ;)).
        Each normal aircraft has 3 basic movements, pitch using the elevators, yaw using the rudder and roll/bank using the ailerons. Though it might seem easy to control an airplane, all these movements have side-effects on other controls.
        When you pitch, you speed will change as well, due to the change in angle of attack of the wings (inclination angle).
        When you yaw, the airplane will tend to roll in the opposite direction, due to the fact that the rudder generates a force that will not only generate a moment in the topview axis (y), but also a moment on the longitudinal axis (x).
        And when you roll, the airplane will tend to yaw in the opposite direction, since when you roll, the drag of the far wing increases, which causes the airplane to tilt back around it's topview (y) axis.
        An airplane or helicopter are definitely not orthogonal.

        Orthogonal systems have many advantages (semi-quoted from the TPP book).
        - Gain productivity: changes are localized, development time and testing time are reduced.
        - Orthogonal approach promotes reuse.
        - A subtle gain in productivity.
        Also, orthogonality reduces risk:
        - Diseased sections of code are isolated.
        - The resulting system is less fragile.
        - Orthogonal systems will probably be better tested.

        Quote from: "ToohTooh"
        Much software is designed in a non-orthogonal fashion: Even much of Windows. Because, it is very hard to design one orthogonally. Personally, I haven't designed one, either (hey, I'm only 22)

        I already said something similar in my previous post, and I think it can be stated that very few *real* orthogonal systems have been designed yet, as it is so difficult to make one. As far as I can see, even major software developers have trouble making it as orthogonal as possible.

        Quote from: "ToohTooh"
        Some books should occupy the rest!.. Happy reading!.

        As I said, I already have a good book which almost covers every aspect of projects and programming ;) Btw, I don't really have the time to read books atm ;)

        I hope I was of any use,

        Neo

        ___________________
        Note: This post has been created after thoroughly searching the book "The Pragmatic Programmer. From journeyman to master" by Andrew Hunt and David Thomas.
        ___________________

        Quote from: "Meg"
        ... am I the only person that has absolutely no idea what is going on in this thread?

        Well, I think we're discussing some of the deeper topics of programming ;)


        Title: Write a bulletproof date validation routine.
        Post by: oracle on October 27, 2004, 06:38:26 PM
        Orthoganality is like the concept of independence in statistics, as well as a concept from gemotry I think :). Object orientated libraries are a prime example of this - plug it in, read the API documentation, code your use of it. Performance gains using this method are huge :)


        Title: Write a bulletproof date validation routine.
        Post by: Neo on October 29, 2004, 06:28:32 AM
        Quote from: "Moneo"
        Looks interesting. Give me some time to run a test on it (more than 20 minutes) like I did with Meg's solution. I'll get back to you.

        So, how's it getting along? I hope it works (although it might be a bit slower than Meg's solution, due to the fact I implemented more checks) ;) I'm glad the solution looked interesting :)

        Quote from: "The Oracle"
        Orthoganality is like the concept of independence in statistics, as well as a concept from gemotry I think . Object orientated libraries are a prime example of this - plug it in, read the API documentation, code your use of it. Performance gains using this method are huge

        Exactly ;) I'm not experienced in statistics, but in geometry e.g. most axis systems are orthogonal.
        Code:
        y
         ^
         |
         |
         |
         |
         +---->
               x

        The common Carthesian coordinate system is orthogonal as well. When you have coordinate in this coordinate system, like (1,1), changing the x-value of this coordinate (i.e. sliding it left/right), doesn't affect the y-value of the coordinate. And vice versa changing the y-coordinate doesn't affect the x-coordinate. Carthesian is orthogonal.

        Now you'd say, aren't all axis systems orthogonal? No, like this para-carthesian:
        Code:
              y
              _
             /|
            /
           /
          /
         +---->
               x

        In this coordinate system, the y-coordinate is the intersect y-value of the perpendicular line through the y-axis. In this case, if you change x, y changes as well, and vice versa.
        ;)
        Of course, OOP was invented to increase orthogonality ;)


        Title: Write a bulletproof date validation routine.
        Post by: Moneo on October 30, 2004, 03:13:39 PM
        NEO:
        Ok, I ran the same test on your version as with Meg's, and it ran without any errors. Congrats!
        I don't like your code for computing days in month. It works, but I consider it to be extremely complex. What's wrong with a table of days per month like in Meg's final version? Even a Cobol programmer could understand that. :wink:


        MEG:
        Yes, this thread has become like a programmers' cocktail party --- who can impress who.

        *****


        Title: Write a bulletproof date validation routine.
        Post by: Neo on October 31, 2004, 05:21:41 PM
        Moneo:
        Thanks for testing! ;) I'm glad it worked perfectly!
        I know you don't like my super-binary-logic-days-in-month-computation, I already expected it :)
        Ok, to make you happy... ;)
        [syntax="QBasic"]SELECT CASE Month
           CASE 1, 3, 5, 7, 8, 10, 12: MaxDays = 31
           CASE 2: MaxDays = 28 - IsLeapYear
           CASE 4, 6, 9, 11: MaxDays = 30
        END SELECT[/syntax]
        Everyone can indeed understand this... ;) Perhaps even a Cobol programmer...

        Quote from: "Moneo"
        Yes, this thread has become like a programmers' cocktail party --- who can impress who.

        Sorry. I just had to bring in something ;)


        Title: Write a bulletproof date validation routine.
        Post by: Moneo on October 31, 2004, 08:29:12 PM
        Neo, I really like your CASE MONTH solution. Can I borrow it next time I need it? Thanks.
        *****


        Title: Write a bulletproof date validation routine.
        Post by: Z!re on October 31, 2004, 08:43:46 PM
        Quote from: "Moneo"
        Neo, I really like your CASE MONTH solution. Can I borrow it next time I need it? Thanks.
        *****



        Only in the qmmunity would you see such a thing... Publicly posted code, and people asking for permission to use it. Only in the Qmmunity... :)


        Title: Write a bulletproof date validation routine.
        Post by: Neo on November 01, 2004, 04:13:27 AM
        Quote from: "Moneo"
        Neo, I really like your CASE MONTH solution. Can I borrow it next time I need it? Thanks.
        *****

        Of course, go ahead :D But make sure the IsLeapYear variable is either -1 (TRUE) or 0 (FALSE). If you don't know this sure, you can use this line instead:
        [syntax="QBasic"]   CASE 2: MaxDays = 28 + ABS(IsLeapYear)[/syntax]
        Use the code when you deem necessary ;) (Woohoo! Moneo wants some of my code! :D)

        Quote from: "Z!re"
        Only in the qmmunity would you see such a thing... Publicly posted code, and people asking for permission to use it. Only in the Qmmunity...

        Only here...


        Title: Write a bulletproof date validation routine.
        Post by: Neo on November 01, 2004, 08:14:47 AM
        Just a note, ToohTooh posted a similar solution in the 14th post on the first page of this thread:
        [syntax="QBasic"]SELECT CASE Mo
        CASE 1, 3, 5, 7, 8, 10, 12: MaxDayForMonth = 31
        CASE 4, 6, 9, 11: MaxDayForMonth = 30
        '-- Sniff the leap year case for February.
        CASE 2: IF (IsALeapYear(Ye)) THEN MaxDayForMonth = 29 ELSE MaxDayForMonth = 28
        '-- Think about below:
        '    Q: Why 100 but not 99?  A: Tokenize() will have trapped it.
        '    Q: Do we need it?       A: Both yes and no. Think.
        CASE ELSE: MaxDayForMonth = 100  '>> Just being safe...
        END SELECT[/syntax]

        I just found out a few minutes ago. The URL is http://forum.qbasicnews.com/viewtopic.php?p=80848#80848

        ToohTooh: I hope you understand.


        Title: Thank you.
        Post by: ToohTooh on November 01, 2004, 08:54:40 AM
        Your watchful eye, enthusiasm on computing, and deep knowledge should be not called a divine model, but what?..

        "Keep walking..."


        Title: Write a bulletproof date validation routine.
        Post by: Neo on November 01, 2004, 08:57:45 AM
        Thanks ToohTooh for your inspiring information. ;)

        Btw, wasn't "Keep walking" from "Johnny Walker"? Or how was he called? :P


        Title: Write a bulletproof date validation routine.
        Post by: dark ninja on November 19, 2004, 10:10:03 PM
        I have no idea what you all are talking about now, but my program's worked on everything I've tried so far.
        Code:

        SUB DateVerify(FullDate$)
        IF LEN(FullDate$) <> 8 THEN PRINT "Invalid": END
        year$ = LEFT$(FullDate$, 4)
        month$ = MID$(FullDate$, 5, 2)
        day$ = RIGHT$(FullDate$, 2)
        year = VAL(year$)
        month = VAL(month$)
        day = VAL(day$)
        IF year <> INT(year) OR month <> INT(month) OR day <> INT(day) THEN PRINT "Invalid": END

        IF year > 3999 OR year < 1600 THEN PRINT "Invalid": END
        IF month < 1 OR month > 12 THEN PRINT "Invalid": END
        IF day < 28 OR day > 31 THEN PRINT "Invalid": END
        IF month = 2 THEN
          IF year / 4 = INT(year / 4) THEN
            IF day > 29 THEN PRINT "Invalid": END
          ELSE
            IF day > 28 THEN PRINT "Invalid": END
          END IF
        END IF

        IF month = 4 OR month = 6 OR month = 9 OR month = 11 THEN
          IF day > 30 THEN PRINT "Invalid": END
        END IF
        PRINT "Valid": END
        END SUB


        I wrote the whole thing as one program, so the SUB commands might not have perfect syntax.  I also made a point to not look at anybody else's work before I made mine, so any similarities in design are coincidental.


        Title: Write a bulletproof date validation routine.
        Post by: Neo on November 20, 2004, 09:50:51 AM
        Quote from: "dark ninja"
        Code:
        IF year / 4 = INT(year / 4) THEN

        Dark Ninja, your code for testing whether a year is a leap year is incorrect. The definition of a leap year (they added something to it, but I can't remember what) is:
        Leapyear
        #1. Factor of 4
        #2. Not a factor of 100
        #3. Rule 2 drops if year is factor of 400

        So, the proper code would be:
        Code:
        LeapYear = ((Year MOD 4 = 0 AND Year MOD 100 <> 0) OR (Year MOD 400 = 0))
        IF LeapYear THEN blablabla


        Hope you can do something with it ;)

        EDIT:
        Oh and:
        Code:
        IF day < 28 OR

        Why are days lower than 28 invalid? :lol: That means your program will output 20000101 (1st Jan. 2000) as an invalid date.


        Title: Write a bulletproof date validation routine.
        Post by: dark ninja on November 20, 2004, 10:29:25 AM
        Thanks, easily fixed.
        Code:

        SUB DateVerify(FullDate$)
        IF LEN(FullDate$) <> 8 THEN PRINT "Invalid": END
        year$ = LEFT$(FullDate$, 4)
        month$ = MID$(FullDate$, 5, 2)
        day$ = RIGHT$(FullDate$, 2)
        year = VAL(year$)
        month = VAL(month$)
        day = VAL(day$)
        IF year <> INT(year) OR month <> INT(month) OR day <> INT(day) THEN PRINT "Invalid": END

        IF year > 3999 OR year < 1600 THEN PRINT "Invalid": END
        IF month < 1 OR month > 12 THEN PRINT "Invalid": END
        IF day > 31 THEN PRINT "Invalid": END
        IF month = 2 THEN
          IF year / 4 = INT(year / 4)  THEN
             Leap$ = "yes"
             IF year / 100 = INT(year / 100) THEN
               Leap$ = "no"
               IF year / 400 = INT(year/400) THEN Leap$ = "yes"
             END IF
          END IF
          IF (day > 28 AND leap$ = "no") OR (day > 29 AND leap$ = "yes")   THEN
            PRINT "Invalid": END
          END IF
        END IF
               
        IF month = 4 OR month = 6 OR month = 9 OR month = 11 THEN
          IF day > 30 THEN PRINT "Invalid": END
        END IF
        PRINT "Valid": END
        END SUB


        Title: Write a bulletproof date validation routine.
        Post by: Neo on November 20, 2004, 01:30:36 PM
        Erm... the code looks functional, but there are still some things that could be improved ;)

        #1. I don't like my program to quit when I run your function (d'oh :D)

        #2. It can be made much faster if you use integers.

        Moneo, Meg, add comments if you wish :)


        Title: Write a bulletproof date validation routine.
        Post by: Moneo on November 20, 2004, 11:23:16 PM
        Quote from: "Neo"
        Erm... the code looks functional, but there are still some things that could be improved ;)

        #1. I don't like my program to quit when I run your function (d'oh :D)

        #2. It can be made much faster if you use integers.

        Moneo, Meg, add comments if you wish :)

        Neo,
        "... the code looks functional" --- you're being so kind.

        About #2 above: Speed is really not an issue, but it is nice to write the speediest code you can, just as a matter of habit.

        The same applies to writing tight, straight-line code with as few if's as possible. The more code your program has, and especially the more if's, then the more chances of bugs. Just like our friend DarkNinja's code; his enhanced leap year logic has so many if's, that I don't even want to bother checking it.
        *****


        Title: Write a bulletproof date validation routine.
        Post by: Neo on November 21, 2004, 09:12:46 AM
        Quote from: "Moneo"
        Neo,
        "... the code looks functional" --- you're being so kind.

        I am always kind... ;)

        Quote from: "Moneo"
        About #2 above: Speed is really not an issue, but it is nice to write the speediest code you can, just as a matter of habit.

        Yeah, that is true, but I implicitly referred to Dark Ninja using strings as temporary storage (leapyear$ = "yes", leapyear$ = "no"). It's much less efficient than just leapyear = 0 or leapyear = -1.
        It doesn't need to be ultrafast, just clean code with an acceptable algorithm.

        Quote from: "Moneo"
        The same applies to writing tight, straight-line code with as few if's as possible. The more code your program has, and especially the more if's, then the more chances of bugs. Just like our friend DarkNinja's code; his enhanced leap year logic has so many if's, that I don't even want to bother checking it.

        I had the same thoughts.


        Title: Write a bulletproof date validation routine.
        Post by: Moneo 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


        Title: Write a bulletproof date validation routine.
        Post by: Neo 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*


        Title: Write a bulletproof date validation routine.
        Post by: yetifoot 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.


        Title: Write a bulletproof date validation routine.
        Post by: Moneo 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


        Title: Write a bulletproof date validation routine.
        Post by: Moneo 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.

        *****


        Title: Write a bulletproof date validation routine.
        Post by: yetifoot 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


        Title: Write a bulletproof date validation routine.
        Post by: Moneo 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


        Title: Write a bulletproof date validation routine.
        Post by: yetifoot 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.


        Title: Write a bulletproof date validation routine.
        Post by: Moneo 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