This is the readme file shipped with my Y2K solution. It contains additional information that didn't made it into the article. If you found any bugs, go ahead and edit it. -- Christof Wollenhaupt
To get the most recent code, follow the Y 2 KCode link.
Year 2000 - Get it done!
by Christof Lange, MVP
This file contains additional information that didn't made into the article because the space there was limited. This file also contains those modifications I made after I submitted the article to advisor. The article focuses on the input problem, while this file contains some additional Year2000 issues and possible solutions.
After the user entered a date, it will be adjusted in Y2K_Valid1 in order to correct the century and handle the 02/29/00 problem. If your VALID function rejects the entered date, then FoxPro restores the old date, in other words, the 19xx date. This will not cause any problems, because the century is not visible at this moment and after leaving the field again, Y2K_Valid1 can correct the date for the second time.
If the user enters 02/29/00, we keyboard a valid date in order to get out of the GET and letting FoxPro execute the valid code. The problem is: It's this very date that will be restored, if your VALID handler returns with .F. or 0. It's the date you pass to Y2K_When, by default an empty date. Thus, if 02/29/00 is invalid by the defintion of your VALID snippet, you get an empty GET field.
A simple work around is to stuff 02/29/00 into the keyboard buffer in Y2K_Valid2. But there is a minor problems with this approach: It's a key stroke, therefore any WAIT WINDOW NOWAIT your valid displayed would disappear. I integrated another workaround. If you define a public variable named gcY2KErrMsg in your custom valid code, Y2K_1 will display this string in a Wait Window. This simulate a window that survives all simulated key strokes. Unfortunately that's a work around that requires you to change existing code, but I haven't found a better solution yet.
Sometimes FoxPro will refresh the GET which has two consequences. First, the cursor is set to the first position of this GET. And second, the date will be re-evaluated, thus an invalid date will be cleared. Unfortunately 02/29/00 is invalid. This will happen whenever you press a key that has been re-directed by ON KEY LABEL and it will happen if you activate the menu and press ESC to leave it again.
We can workaround this almost completely by adding the MESSAGE wrapper. This wrapper will re-type 02/29/00, when either an OKL fired or the menu has been called, the date is currently empty but wasn't empty before. Two less important, IMO, problems remain. If the user entered an invalid date like 56/71/99, or if the user just cleared the field, the GET will be replaced with 02/29/00. If you instead want the default behaviour of having an empty GET when an OKL was pressed, just leave out the MESSAGE wrapper.
Another problem is APPEND FROM and IMPORT. FoxPro exports always as 4-digit-year. Therefore importing files that follow the standard, should not be too complicated. But importing 2-digit files could cause problems, mainly two different ones: Again FoxPro just adds 19xx to any date it finds. Here's a simple fix possible, but not for the second problem: 02/29/00 is ignored.
A workaround would involve an additional function called in the FOR clause of the APPEND/IMPORT command. This function will be evaluated for every single record after it has been added. Therefore changing the date to add 100 years is a simple task. But 02/29/00 would be stored as an empty date. There are two possibilities, depending on the assumptions made for the file. If the file doesn't contain empty dates, we can just replace an empty date with 02/29/00. Otherwise we need to find out wether an empty date is meant or not. Again we have two possibilities. Either we scan the file we're importing from or we first append to a character field into a temporary cursor, and then append from the cursor after adjustinmg the dates.
Due to hardware problem we have a DATE() problem, too. Some computer are not Y2K compliant and will return the wrong date when being turned off after 12/31/99. A workaround is simply in FoxPro. Just write your own DATE() function and add a century if the year is between 1900 and 1996. You can use the #Define technique to replace the built-in function.
Finally there are some further limitations with my solution. First of all, Y2K is not re-entrant. If you call a new read level while being in a date field, and this new read level contains a date field, too, both GET will overwrite their public variables. To work around this, you'd convert all public Y2K-variables into an array. If you don't use BROWSEs with Y2K, you can use RDLEVEL() as index, otherwise you need a custom counter.
There's a simpler solution, too, that doesn't require you to change Y2K, but OTOH is less encapsulated. In the setup snippet of every screen and before every BROWSE that uses Y2K, define a new set of private variables, one for every public Y2K variable. Save the current values into this private variables and then release the public variables. In the cleanup snipper or after the BROWSE you declare them public again and assign the saved values.
This of course requires additional code in the setup snippet which might be created automatically, depending on your application. Additionally you might restore the ON READERROR handler and the SET BELL and SET CONFIRM setting. YOu could refer to the Y2K public variables, to get the previous value. Be sure that you check whether these variables exist, before you access them.
Another problem are programs that use KEYBOARD quite extensively. As I have to clear the keyboard and stuff my one keys, any keyboard macro would stop working in a date field. If you re-direct any of the keys that are used you might get weird results, because executing an OKL procedure has some side-effects in FoxPro.
Using the mouse can cause problems, as well. If the user enters 02/29/00 in a date field without tabbing out of the field, but rather uses the mouse to get to another field, she has to click twice. The first click would move the focus to the next field in the tab order and the second activate the desired GET. This could be a problem if you use a simple READ and the data field is the last GET in this READ. In this situation tabbing out of the field would exit the READ level.
Finally during the wait state (WHEN->MESSAGE->.....->RANGE->VALID) two settings have changed: Set Bell and On Readerror. If they are important in other screens, they have to be explicitely set in every screen and restored on leaving the screen. Otherwise calling this screen while being in a date field, would result in the wrong settings.
Make sure that you always call Y2K_Valid2, otherwise the next GET might be behave quite strange. This is especially important with ESC. Only in a READ with a Cancel button, ESC will fire the VALID. Without such a button or in a Browse, Valid won't fire at all. You should add =Y2k_Valid2() after all READ and BROWSE statement that include Y2K. In a screen add this code in the first line of the cleanup snippet.
In my article and the last version of this file I haven't covered BROWSEs very well. Whenever you use a BROWSE with Y2K you have to use the :W and the :V clause to call Y2K_WHEN() and Y2K_VALID1/2(). In order to do this, all of your BROWSES must have a FIELD clause. BROWSEs that just display all fields of the current table cannot be used here. Additionally the :F clause is required in the :V clause. If you previously haven't got the :F clause, but custom valid code, you could pass this valid code as a string to Y2K_VALID1(). There I check whether the field has changed and only evaluate your valid code in that case. Thus, your valid code doesn't get executed all the time, as :F would cause it.
Since I published the last version I fixed a couple of bugs and made some improvements that are listed below:
- The third line must define Y2K_INVINPUT, not Y2K_INVINPIT.
- I added a call to the Message wrapper to the sample code.
- Instead of only defining a whole new menu bar when SYSMENU is OFF, I now define a popup that contains the Copy and the Select All bar. Having them not in a defined menu was the most common problem that happened to those that used my solution. If you haven't got any menu bar at all, you've to activate it and make it invisble. Otherwise the popup is not used and you will experience some weird problems. A continuous series of beeps or an infinite loop when entering 02/29/00 are symptoms that indicate a missing menu bar.
- In Y2K_Stuff, the SET("MARK") handling has been removed, as it doesn't matter which character is used here. They are ignored by FoxPro anyway when you enter a date.
- In Y2K_Valid1, the SHOW GET command is surrounded by IF not Y2K_Browse(), just in case you have a GET field with the same name as a BROWSE field.
- The code for checking for a changed field within a BROWSE has changed completely. In the previous version youz couldn't leave the current column without changing the date, if an empty string has been passed. This has been fixed now.
- FP2.6 has got a bug with OBJVAR(). Under some circumstances it returns an empty string. In this case a variable is assumed and VarRead() is used to determine the current GET field. If your GET works directly on the table, set the gcY2KObVar variable to the name of this field, in case you experience a problem here.
- Y2K_CTOD() was wrong. It always changed the current SET CENTURY setting and couldn't handle 02/29/00. It's now fixed.
- Y2K_LUPDATE(): I changed the EValuate expression, because FoxPro won't replace a string with a #DEFINE statement.
- And finallly, I extended the comments in the code. Especially, all global variables are now explained. I hope this makes it easier for you to understand the Y2K code.
- In Y2K_2 I moved the line that increases the message counter to the top of this procedure. This will fix the bug that Y2K gets into an infinite loop when 02/29/00 has been entered.
- In Y2K_1 I added some code to handle SET CONFIRM ON. Previously, the user just stuck in the field and couldn't leave it without entering any date different from 02/29/00.
- Y2K_290200: Doesn't work with #Define CTOD Y2K_CTOD. Now Evaluate is used to determine the date.
- Y2K_2: When SET CONFIRM ON is used without the "K" function property, only the last character was copied ("0") and therefore the "Invalid date." message always appeard in that case.
- Y2K does now work when SET CENTURY is ON. Keep in mind that the rollover is still in effect. When the user enters 01/01/1900 and Rollover is set to 1950, it's converted to 01/01/2000. There's no possibility to detect whether the user entered a 2-digit or a 4-digit year.
Did I mention that I just love Visual Source Safe , without it, I'd have had a lot to do to find out all of the modifications I made.
I'll will post any update information in the FOXUSER forum on CompuServe, as well as answer question related to my article and code there. Of course, feel free to send me an e-mail.
Many, many thanks to all who reported bugs and tested this software. There are too many to name here all, but be sure that you all helped making Y2K a better tool!
Thanks to Advisor for letting me post this code on CompuServes FOXUSER forum. You can read the article online in Advisor's article archive on www.advisor.com and, of course, subscribe to Advisor.
This code is Public Domain and can be incooperated in any of your application, no matter whether it's a commercial application or you ship it with source codes. Of course, neither I nor Advisor Publication Inc. are reliable for any damage or problems the use of this code might cause. The code is provided "as is" and you use it on your own risk.
From now on, I'll upload any new version of this file to the FOXUSER forum on CompuServe.
Category Y 2 K
( Topic last updated: 1999.11.15 03:39:22 AM )