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.