Portfolio...Alternative Date Entry using Clipper
Originally published in Grumpfish Aquarium magazine in March of 94
(You can use the NaturalDate library to achieve similar functionality in .NET)
Imagine the following conversation between two friends attempting to schedule a tennis match:
Ken: "How about Friday?" Joe: "No, Friday is no good, how about tomorrow?" Ken: "Tomorrow is out, is Thursday alright?" Joe: "No, next Monday is open!" Ken: "Ok, I will get a court for next Monday!"
Now imagine that same conversation using CA-Clipper to handle the date portions:
Ken: "How about 01/14/94?" Joe: "No, 01/14/94 is no good, how about 01/11/94?" Ken: "01/11/94 is out, is 01/13/94 alright?" Joe: "No, 01/17/94 is open!" Ken: "Ok, I will get a court for 01/17/94!"
While the second conversation is much more precise, it also is not very easy to do without a calendar in front on you. Why do we then make our end-users enter dates in this fashion? (if you answer 'Because we don't like them!', then do not bother reading this article.)
Entering GuessDate()
Clipper provides a wide variety of date functions and powerful date arithmetic. Using the functions from Clipper, we can easily write an easy to use date conversion function, so the first conversation could be handled in Clipper. In this article, we will provide such an option. Its name is GuessDate() and what is does is attempt to interpret the user's input as a valid date. The syntax of GuessDate() is:
<dActual> := GuessDate(cInput)
where When you are using this routine, keep in mind that the routine is making a guess as to what the user is trying to say. Once you are
done, be sure to show the user the date that they have entered. It is possible that our guess has come up with a valid date, but not the
one the user had in mind. So allow the user to correct the date if they did not quite get what they expected.
The GuessDate() function handles a variety of inputs. For starters, the function handles regular old date entry, such as mm/dd/yy. It
does this by taking the input parameter and passing it to Clipper's CTOD() function. If CTOD() can evaluate it and produce a date, then
GuessDate() uses that date; there is little sense in duplicating what Clipper can already do.
In addition, GuessDate() recognizes certain keywords. For example, yesterday, today, and tomorrow all return valid dates.
So do Christmas, Easter, and Thanksgiving. These key words are easy to recognize. Christmas is easy to deal with, since it always falls
on the same date.
Easter is a little trickier, so I use the code from the FT_Easter() function of the Nanforum Toolkit to determine the date
that Easter falls on. The calculation for deriving Easter was done by Donald Knuth in his books on algorithm. If you look at the
algorithm, you will conclude that Donald has way too much time on his hands!
For Thanksgiving, I wrote a function called FindDay(). This function takes a month number, the day of the year you are looking for,
the year, and the occurrence. Since Thanksgiving is always the fourth Thursday of November in the United States and the second Monday in
October in Canada, the call:
Will return the date that Thanksgiving falls on. Labor day is handled in a similar manner.
Finally, the GuessDate() function will recognize day names, such as Tuesday or Friday. So if you call GuessDate() on Wednesday,
January 12, 1994 and pass it Friday, it will return the date 01/14/94.
You can easily expand the list of single word to handle things like Standard or Rush. For example:
If the user enters STANDARD, then dDate might contain date()+7 while for an entry of RUSH, dDate might contain date() +3. OVERNIGHT could
produce a value of date()+1, and so on. This truly makes the system simple for the end user to understand.
In addition to single word entries, GuessDate() also handles two word entries. For example, 'Next Tuesday' would return 01/18/94 if you
typed it on Monday, January 10, 1994. It also recognizes the modifier 'Last' and 'This', so items like 'Next Friday' or 'This Easter' or
'Last Thanksgiving' all are acceptable.
To handle two word entries, GuessDate() breaks the input into two words, using a space as the separation character. If the first word
is 'THIS', 'LAST', or 'NEXT', it is considered a modifier. The second word determines the date and the modifier changes it. 'Next Easter'
will call the FT_Easter() function with next year instead of the current year. 'Next Monday' will add seven days to the Monday that it finds.
'Last Tuesday' will work backwards from the current date.
In addition, the following two word inputs are recognized:
You can easily modify the GuessDate() function to handle other key dates.
While it requires a bit more code and introduces ambiguities that might not exist otherwise, writing software that talks the user's
language makes your program much easier to use. Unfortunately, you can sometimes make things too easy, because my users are always
typing in 'Friday' or 'Next Weekend' to their contact managers, which display 'Invalid date'. Oh well, maybe someday date entry will
become standardized...
cInput := space(25)
dDate := ctod( ' / / ' )
@ 09,10 SAY "When is the meeting scheduled? " ;
GET cInput VALID !empty( dDate := GuessDate(cInput) )
READ
What To Handle - The Easy Part
What To Handle - Tricky parts
dActual := FindDay( 11, ; // Month of November
5, ; // Thursday
1994, ; // Year
4 ) // Fourth occurence
@ 10,10 SAY "Delivery date: ";
GET cDate VALID (dDate := GuessDate(cDate) )
What To Handle - The Harder Part
Summary