Inside COBOL #78 (Fixing your system clock with COBOL)
by
Shawn Gordon
President
The Kompany
Back when we I was doing Y2K application software, one of the most common things that came up was the fact that people didn’t have their system clock set up right. There are a number of parameters involved, such as having the timezone correct, and the TZ variable correct. An example of where this can cause serious trouble is with the PowerHouse software family. They use the hardware clock for the date and the software clock for the time (or vice versa, I forget now). In any case you could easily get the wrong date for the time if you didn’t have the TZ variable set up, and the timezone on your system was messed up.
Well, you can do a lot of this stuff manually, but I wrote this program to basically walk a user through the steps of getting the time right. It uses some neat things like the rather difficult to find BITMAPCNV intrinsic. This is documented in the Vplus manual, so it’s hard to find. Basically it will take a bitmask and convert it to a byte array. Since I needed to know if the user had SM or OP to issue the SETCLOCK commands, I needed to call the WHO intrinsic and figure out if they had either of those capabilities. By converting to a byte string, it’s very simple and saves a lot of effort. It’s possible that there are other ways to do this now, but you know how it is when you have figured something out and just keep using it.
Now we also make use of the MOVE FUNCTION CURRENT-DATE to get a date format that takes into account the TZ variable. We will compare this to what is returned to the old time intrinsics for returning date and time values to see if there is a discrepency in what is currently on the system.
Take a look at figure 1 for the dialog that will go on if the hardware and software clocks don’t match. The TIMEZONE portion is usually the difficult part to get right, and this value can take a very very long time to adjust if you let it go GRADUAL. There is actually no way to make the TIMEZONE adjustment be NOW, like setting the clock, however there is an interesting side affect of issueing a CANCEL against a TIMEZONE adjustment, in that it causes the TIMEZONE adjustment to be immediatly executed. So that is what I’ve done in this example.
Figure 2 has our source code, there aren’t any huge tricks here other than knowing the different intrinsic calls to get the information you need. You will notice the HPCIGETVAR intrinsic to retrieve the value of the TZ variable if it exists, and has a value in it. Also we make good use of the STRING verb to get our SETCLOCK command formatted all nice and purty.
I tried to keep my text short this month because the listing is a bit large, and I’m hoping it will all fit, as this is a pretty useful tool. I’ll give you one other gem that has nothing to do with COBOL, but it does have to do with keeping your timezone in sync. I put this in my batch scheduler to run every Sunday.
IF HPMONTH = 10 AND HPDATE > 24 THEN ECHO We are going back to Standard Time SETCLOCK TIMEZONE = W8:00 ENDIF IF HPMONTH = 4 AND HPDATE < 8 THEN ECHO Setting clock for Daylight Savings Time SETCLOCK TIMEZONE = W7:00 ENDIF I’m on the west coast, so adjust the TIMEZONE value to your own timezone. Figure 1 The Kompany: run clockfix.pub.timewarp Current offset from GMT: -04:00 No TZ variable is set, I will show you the correct value, after you answer some questions. WARNING: Hardware clock doesn't match the software clock Hardware clock = 11:08 Software clock = 08:08 Is it currently Daylight Savings Time? Y Please select one of the following as your TimeZone 1. Eastern European Time (EET-2DST) 2. Middle European Time (MET-1DST) 3. Western European Time (GMT0BST) 4. Atlantic Time (AST4ADT) 5. Eastern Time (EST5EDT) 6. Central Time (CST6CDT) 7. Mountain Time (MST7MDT) 8. Pacific Time (PST8PDT) 9. Yukon Time (YST9YDT) Enter Option: 8 I am now ready to fix your hardware clock. Choose one of the following options 1. Gradually change the time over an hour 2. Change the time immediately Enter Option: 2 SETCLOCK TIMEZONE=W7:00 SYSTEM TIME: TUE, JUL 18, 2000, 8:09:19 AM CURRENT TIME CORRECTION: 0 SECONDS TIME ZONE: 7 HOURS 0 MINUTES WESTERN HEMISPHERE SETCLOCK;CANCEL CORRECTION OF 0 SECONDS HAS BEEN CANCELLED. SETCLOCK DATE=07/18/2000;TIME=08:09 ;NOW You will want to set up the following command as a system logon UDC; SETVAR TZ,"PST8PDT " Normal termination at 08:09:00 END OF PROGRAM The Kompany: SHOWCLOCK SYSTEM TIME: TUE, JUL 18, 2000, 8:09:10 AM CURRENT TIME CORRECTION: 0 SECONDS TIME ZONE: 7 HOURS 0 MINUTES WESTERN HEMISPHERE The Kompany: SHOWTIME TUE, JUL 18, 2000, 8:09 AM The Kompany: Figure 2 $CONTROL USLINIT, NOWARN, BOUNDS, POST85 $VERSION "TimeWarp ClockFix Program. Release: 1998/12/10." IDENTIFICATION DIVISION. PROGRAM-ID. WARPFIX. AUTHOR. Shawn M. Gordon. INSTALLATION. S.M.GORDON & ASSOCIATES. DATE-WRITTEN. FRI, APR 17, 1998. * ************************************************************ * This program will check your system to see if you hardware * and software clocks are out of sync. If they are then * we prompt for a few pieces of information. * * ----------- change history --------------- * * 1998/12/10 SMG - Added test for SM or OP cap, and gracefully * terminate if it's not there. ************************************************************ * ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. 01 ERR PIC S9(4) COMP VALUE 0. 01 ERR-LEN PIC S9(4) COMP VALUE 0. 01 ERR-PARM PIC S9(4) COMP VALUE 0. 01 MSG-LEVEL PIC S9(4) COMP VALUE 0. 01 CALC-TZ PIC S9(1) VALUE 0. 01 HOLD-TZ PIC 9 VALUE 0. 01 MY-TZ PIC X(08) VALUE SPACES. 01 MY-INTERVAL PIC X(08) VALUE SPACES. 01 MY-TIME PIC X(06) VALUE SPACES. 01 MY-TIMEZONE. 03 MYT-HEM PIC X VALUE SPACES. 03 MYT-ZONE PIC X(05) VALUE SPACES. 01 MY-BUFF PIC X(80) VALUE SPACES. 01 WS-DST PIC X VALUE SPACES. 01 WS-TZ PIC X VALUE SPACES. 01 WS-GN PIC X VALUE SPACES. 01 VAR-NAME PIC X(40) VALUE "TZ". 01 VAR-STRING PIC X(255) VALUE SPACES. * 01 VAR-STATUS. 03 VS-1 PIC S9(4) COMP VALUE 0. 03 VS-2 PIC S9(4) COMP VALUE 0. * 01 COM-IMAGE. 03 COMMAND-IMAGE PIC X(255) VALUE SPACES. 03 PIC X VALUE %15. 01 COMMAND-ERROR PIC S9(4) BINARY VALUE 0. 01 CAPS. 03 CFULL PIC S9(9) COMP VALUE 0. 03 CREDEF REDEFINES CFULL. 05 CWORD1 PIC S9(4) COMP. 05 CWORD2 PIC S9(4) COMP. 01 NUMBYTES PIC S9(4) COMP VALUE 16. 01 BYTEFUNC PIC S9(4) COMP VALUE 1. 01 BYTERR PIC S9(4) COMP VALUE 0. 01 BYTEMAP. 03 PIC X VALUE SPACE. 88 SMCAP VALUE "1". 03 PIC X(04) VALUE SPACES. 03 PIC X VALUE SPACES. 88 OPCAP VALUE "1". 03 PIC X(10) VALUE SPACES. 01 FULL-CURRENT-DATE. 03 F-DATE. 05 F-YEAR PIC 9(4). 05 F-MONTH PIC 99. 05 F-DAY PIC 99. 03 F-TIME. 05 F-HOUR PIC 99. 05 F-MINUTES PIC 99. 05 F-SECONDS PIC 99. 05 F-SEC-HUND PIC 99. 03 C-TIME-DIFF. 05 C-GMT-DIR PIC X. 05 C-HOUR PIC 99. 05 C-MINUTES PIC 99. 01 DATE-BUFF PIC X(27) VALUE SPACES. 01 C-DATE PIC X(08) VALUE SPACES. 01 C-TIME PIC X(08) VALUE SPACES. 01 CURR-DATE PIC X(10) VALUE SPACES. 01 HOLD-DATE PIC X(06) VALUE SPACES. PROCEDURE DIVISION. A0000-MACROS. $DEFINE %COMIMAGE= DISPLAY !1 INITIALIZE COMMAND-IMAGE MOVE !1 TO COMMAND-IMAGE CALL INTRINSIC 'HPCICOMMAND' USING COM-IMAGE, COMMAND-ERROR, ERR-PARM, MSG-LEVEL# * A1000-INTRO. CALL INTRINSIC "WHO" USING \\, CFULL. CALL "BITMAPCNV" USING CWORD1, @BYTEMAP, NUMBYTES, BYTEFUNC, BYTERR. IF BYTERR <> 0 DISPLAY 'Failure in BITMAPCNV ' BYTERR. IF SMCAP OR OPCAP GO TO A1000-BEGIN. DISPLAY 'Must have SM or OP capability.' STOP RUN. A1000-BEGIN. * retrieve all the dates in all the formats. MOVE FUNCTION CURRENT-DATE TO FULL-CURRENT-DATE. DISPLAY 'Current offset from GMT: ' C-GMT-DIR C-HOUR ":" C-MINUTES. MOVE CURRENT-DATE TO CURR-DATE. ACCEPT C-DATE FROM DATE. CALL INTRINSIC 'DATELINE' USING DATE-BUFF. ACCEPT C-TIME FROM TIME. * format the century into the applicable dates. MOVE C-DATE TO HOLD-DATE. MOVE CURR-DATE(7:2) TO CURR-DATE(9:2). MOVE DATE-BUFF(14:2) TO C-DATE(1:2) CURR-DATE(7:2). MOVE HOLD-DATE TO C-DATE(3:). * get the TZ variable. CALL INTRINSIC "HPCIGETVAR" USING VAR-NAME, VAR-STATUS, 2, VAR-STRING, 0. IF VS-1 = -8106 OR VAR-STRING = SPACES DISPLAY SPACES DISPLAY 'No TZ variable is set, I will ' 'show you the correct value,' DISPLAY 'after you answer some questions.' DISPLAY SPACES GO TO A1000-TEST ELSE DISPLAY 'Current value of TZ var: ' VAR-STRING(1:16). * if the date and time match then there is nothing to do. IF (F-TIME(1:4) = C-TIME(1:4)) AND (F-DATE = C-DATE) DISPLAY 'Your hardware clock is properly set'. STOP RUN. A1000-TEST. IF F-TIME(1:4) <> C-TIME(1:4) DISPLAY 'WARNING: ' "Hardware clock doesn't match the software clock" DISPLAY 'Hardware clock = ' F-HOUR ":" F-MINUTES DISPLAY 'Software clock = ' C-TIME(1:2) ":" C-TIME(3:2) DISPLAY SPACES. IF F-DATE <> C-DATE DISPLAY "WARNING: Hardware date doesn't match the " 'hardware date' DISPLAY 'Hardware date = ' F-DATE DISPLAY 'Software date = ' C-DATE DISPLAY SPACES. DISPLAY 'Is it currently Daylight Savings Time? ' NO ADVANCING. ACCEPT WS-DST FREE. DISPLAY 'Please select one of the following as your ' 'TimeZone'. A1000-TZ. DISPLAY SPACES. DISPLAY '1. Eastern European Time (EET-2DST)'. DISPLAY '2. Middle European Time (MET-1DST)'. DISPLAY '3. Western European Time (GMT0BST)'. DISPLAY '4. Atlantic Time (AST4ADT)'. DISPLAY '5. Eastern Time (EST5EDT)'. DISPLAY '6. Central Time (CST6CDT)'. DISPLAY '7. Mountain Time (MST7MDT)'. DISPLAY '8. Pacific Time (PST8PDT)'. DISPLAY '9. Yukon Time (YST9YDT)'. DISPLAY SPACES. DISPLAY ' Enter Option: ' NO ADVANCING. ACCEPT WS-TZ FREE. EVALUATE WS-TZ WHEN '1' MOVE 'EET-2DST' TO MY-TZ WHEN '2' MOVE 'MET-1DST' TO MY-TZ WHEN '3' MOVE 'GMT0BST' TO MY-TZ WHEN '4' MOVE 'AST4ADT' TO MY-TZ WHEN '5' MOVE 'EST5EDT' TO MY-TZ WHEN '6' MOVE 'CST6CDT' TO MY-TZ WHEN '7' MOVE 'MST7MDT' TO MY-TZ WHEN '8' MOVE 'PST8PDT' TO MY-TZ WHEN '9' MOVE 'YST9YDT' TO MY-TZ WHEN ' ' GO TO C9000-EOJ WHEN OTHER DISPLAY 'Invalid value' GO TO A1000-TZ END-EVALUATE. DISPLAY SPACES. DISPLAY 'I am now ready to fix your hardware clock.'. DISPLAY 'Choose one of the following options'. DISPLAY '1. Gradually change the time over an hour'. DISPLAY '2. Change the time immediatly'. DISPLAY SPACES. DISPLAY ' Enter Option: ' NO ADVANCING. ACCEPT WS-GN FREE. IF WS-GN = '2' MOVE 'NOW' TO MY-INTERVAL ELSE MOVE 'GRADUAL' TO MY-INTERVAL. PERFORM B1000-DO-IT THRU B1000-EXIT. PERFORM B2000-SHOW-TZ THRU B2000-EXIT. GO TO C9000-EOJ. A1000-EXIT. EXIT. * B1000-DO-IT. MOVE CURRENT-DATE TO CURR-DATE. MOVE CURR-DATE(7:2) TO CURR-DATE(9:2). CALL INTRINSIC 'DATELINE' USING DATE-BUFF. MOVE DATE-BUFF(14:2) TO CURR-DATE(7:2). MOVE TIME-OF-DAY TO C-TIME. MOVE SPACES TO MY-TIME. STRING C-TIME(1:2) ":" C-TIME(3:2) DELIMITED BY SIZE INTO MY-TIME. * Fix the timezone first - construct the offset by calculating * the dst value first, and putting the right hemisphere in it IF MY-TZ(4:1) IS NUMERIC MOVE MY-TZ(4:1) TO CALC-TZ. IF MY-TZ(5:1) IS NUMERIC MOVE MY-TZ(5:1) TO CALC-TZ MULTIPLY -1 BY CALC-TZ. MOVE SPACES TO MY-TIMEZONE IF CALC-TZ < 0 MOVE 'E' TO MYT-HEM ELSE MOVE 'W' TO MYT-HEM. IF WS-DST = 'Y' OR 'y' SUBTRACT 1 FROM CALC-TZ. MOVE CALC-TZ TO HOLD-TZ. IF MYT-HEM = 'E' STRING "-" HOLD-TZ ":00" DELIMITED BY SIZE INTO MYT-ZONE. IF MYT-HEM = 'W' STRING HOLD-TZ ":00" DELIMITED BY SIZE INTO MYT-ZONE. MOVE SPACES TO MY-BUFF. STRING "SETCLOCK TIMEZONE=" DELIMITED BY SIZE MY-TIMEZONE DELIMITED BY SPACES INTO MY-BUFF. %COMIMAGE(MY-BUFF#). * Cancel the change to get an immediate timezone impact. %COMIMAGE("SETCLOCK;CANCEL"#). * Now set the correct date and time values. STRING "SETCLOCK DATE=" CURR-DATE ";TIME=" MY-TIME ";" MY-INTERVAL DELIMITED BY SIZE INTO MY-BUFF. %COMIMAGE(MY-BUFF#). B1000-EXIT. EXIT. * B2000-SHOW-TZ. DISPLAY SPACES. DISPLAY 'You will want to set up the following command ' 'as a system logon UDC;'. DISPLAY 'SETVAR TZ,"' MY-TZ '"'. DISPLAY SPACES. B2000-EXIT. EXIT. C9000-EOJ. DISPLAY 'Normal termination at ' TIME-OF-DAY. STOP RUN.