Powerful Y2K-compliant date manipulation operations are a key feature of the ILE programming environment. This date support is great for ILE, but it does not directly benefit programming date routines with the Original Program Model (OPM)-based systems.
A tool that has helped me deal with dates in RPG III and OPM COBOL programs is the Convert Date and Time Format (QWCCVTDT) API (for more information, see "The Convert Date and Time Format (QWCCVTDT) API," MC, ). IBM's first release of QWCCVTDT handled two-digit years only. IBM later enhanced QWCCVTDT to work with four-year date formats as well. Now, QWCCVTDT can do all this:
o Retrieve the current date in four-year date formats (RPG III has *DATE, but OPM COBOL has nothing)
o Convert date values from one format to another, including four-year date formats
o Test for the validity of dates, including four-year date formats
o Facilitate date arithmetic, such as adding a number of days to a date
o Replace calls to OPM CL date conversion programs that use CVTDAT
o Determine the day of the week for the current date
o Work with dates in Julian and long Julian formats
One of the nicest features of QWCCVTDT is that it's free; it's a part of OS/400 that your shop already paid for. All your shop needs to implement QWCCVTDT is the programming knowledge.
If you're interested in Y2K-compliant OPM date manipulation and can fit a no-cost solution into your MIS budget, then read on. You may find the solution to some of the Y2K challenges you're
facing.
In the past, QWCCVTDT returned data in a 16-byte data structure that supported only dates with two-year date formats. The century value was symbolized by 0 for 19xx and 1 for 20xx. IBM added two new data structure formats to the API. They are the 17-byte Character Date and Time Value data structure and the DOSGetDateTime structure.
The 17-byte Character Date and Time Value data structure allows variations of the eight-digit date formats and long Julian dates, which use a four-year date format. The subfields of this data structure are listed in Figure 1.
The DOSGetDateTime structure has a day of the week field for the current date, as well as other date/time fields. Figure 2 illustrates this data structure.
This article will focus exclusively on the use of these new data structures, since they readily support Y2K-compliant date manipulations.
OS/400 APIs, including QWCCVTDT, frequently make use of parameters in the form of data structures. The values and format of the parameters vary depending on the application. Once these parameters are set up correctly, the particular API is ready to be called. The parameter list forQWCCVTDTisshowninFigure3.
The input format parameter specifies the format of the date/time values that QWCCVTDT will use. The input parameter specifies the date/time values to be converted. In a similar fashion, the output format parameter specifies the format of the output date/time values. The output date/time values generated by QWCCVTDT will be contained in the output parameter. The last parameter is the standard OPM API Error Information data structure. If any errors occurred when QWCCVTDT was called from the high-level language (HLL), they can be trapped by using this structure.
In Figure 4, you'll see an RPG III program I wrote to illustrate how to use QWCCVTDT with these two new data structures. You can find a COBOL version of this program on MC's Web site. Figure 5 shows the output produced when I ran this program.
Let's examine the program in Figure 4 in more detail.
In order to retrieve the current date in the YYYYMMDD format, the first process of the example RPG program will be to set up and call the QWCCVTDT API using the 17-byte Character Date and Time Value data structure. The program does this in subroutine YYMDDT. First, the program moves *CURRENT to the input format parameter and *YYMD to the output format parameter. Immediately after this step, the program invokes the routine CALL17, which calls QWCCVTDT using the 17-byte Character Date and Time Value data structure. Retrieved information is then passed back to the HLL, using the output date/time variable parameter. Subsequently, the current date in the format YYYYMMD is displayed upon the console.
Next, the program runs subroutine LJULDT to retrieve the current date in the long Julian format. The value *CURRENT is moved to the input format, and *LONGJUL is moved to the output format. After QWCCVTDT is called, the results are displayed to the console.
Using QWCCVTDT to retrieve the current date is safe. QWCCVTDT will not detect an error because OS/400 "knows" what the value of the current date is. In the real programming world, the possibilities of encountering invalid date values are quite numerous. QWCCVTDT was designed to trap invalid date errors as well, as the example program demonstrates.
If an error occurs on a call to an OPM API, the API Error Information data structure gets loaded with error information. In our program, this data structure is named APIERR. In the event of an error, field AEBYAV of APIERR, which represents the bytes of error information, will be greater than zero. When an error occurs, the output date/time variable parameter will be blank, and the 7-Character Message ID field, referred to as AEEXID, will indicate the OS/400 error message ID of the error that occurred. For a complete list of the errors QWCCVTDT can return, see the OS/400 Miscellaneous APIs manual.
In programming, trapping errors is usually the most difficult when the case is an unusual exception. With date routines, one case that is most likely to cause trouble is leap year. February 29, 2000, is a valid date, but February 29, 2001, is not. By using these cases to test the QWCCVTDT API, we can verify whether the API handles leap year correctly. We are especially interested in the Year 2000 leap year.
As the first step in the date checking/conversion process, our program checks and converts the valid date of February 29, 2000. In order to convert the format of a date using QWCCVTDT, one must pass the value of the date to QWCCVTDT using the input date/time variable parameter. If there are no errors, the requested conversion value will then be returned in the output date/time variable parameter after the API is called. Using the 17-byte Character Date and Time Value data structure, the RPG program checks and converts the DTEOK of "02292000" in VALDT. The program also moves *MDYY to the input format and *LONGJUL to the output format. A subsequent call to QWCCVTDT using the valid date values is then issued. Once again, the error- free results are displayed to the console.
Next, in subroutine VALDT, we will use 02/29/2001 as a date value. With this value, we will finally be able to generate an error that can be immediately trapped within the program. First of all, we will move the invalid date value, which our example program refers to as DTEBAD, to the date value of the 17-byte Character Date and Time Value data structure. We are still using *MDYY as the input format and *LONGJUL as the output format. Then, the program issues a call to the API again.
Notice that when the QWCCVTDT is called, an error is promptly generated and trapped. The error is CPF1060, which means that the date is not valid. Also note that because an error occurred, the output information is blank. This is the way that QWCCVTDT is supposed to work when errors are encountered.
The DOSGetDateTime data structure can be used by QWCCVTDT to retrieve current date and time information, the current day of the week, and time zone.
According to IBM documentation, the DOSGetDateTime output format, known as *DOS, can be used only when either *CURRENT or *DTS is specified in the input format parameter. Our example program will use the *CURRENT setting for the input format parameter.
Once again, calling QWCCVTDT is fairly straightforward, provided that the parameters are set
up correctly. However, setting up the parameters and using their values in an HLL is somewhat tricky because of the use of 1-byte binary integers for some of the values, such as the day of the week. The example RPG program moves this data into a two-digit alphanumeric data structure and then back to numeric fields in order to display the results on the console. The DOSGetDateTime values that our example program will use are the current year, time zone, and the current day of the week. All these functions are executed in subroutine GETDOS, which also displays the results to the console.
The time zone subfield does not return the value in system value QUTCOFFSET, as you might expect. To get the value in QUTCOFFSET, divide the time zone subfield by -60.
QWCCVTDT does not do date arithmetic, but it can help. If you have routines that do date arithmetic by converting dates to Julian format, you can use QWCCVTDT to retrieve the long Julian formats instead.
You can use the same principle for date arithmetic with other date formats. That is, you can convert a date to long Julian format, add or subtract a number of days-allowing for leap years where necessary-then convert the answer back to the original format.
However, QWCCVTDT is probably not the best way to do date arithmetic in most cases. When I'm working in RPG, I prefer to dynamically call an RPG IV program to handle date arithmetic. ILE COBOL does not do date arithmetic, so in a COBOL environment, I call an ILE COBOL program that uses the CEE APIs.
Notice that the example program does not use date separators. Since programmers frequently have a need to do this, it can be easily accomplished through the use of data structures and editing routines when appropriate.
Using a date/time API on the AS/400 is one of the fastest and most efficient methods available for date manipulation. As a result, the computing resources necessary to take advantage of this API are actually much more efficient than many other programming methods.
Programming Y2K-compliant date routines on the AS/400 is considered a formidable task. QWCCVTDT can make this task much easier. I have shared this information with other programmers who were wondering how to keep their OPM programs running smoothly into the new millennium, and they have found it helpful to them. I hope it will be helpful to you, too.
Mario Martinez has a BA in economics from the University of Maryland and a BS in computer information systems from Arizona State University. He has been working as a programmer/analyst on midrange and microcomputer platforms for eight years. He can be reached by email at
API concepts for RPG Programmers Carlsbad, Cal.: IIR Publications, Inc., 1996.
OS/400 Miscellaneous APIs (SC41-4880, CD-ROM QBKAM901)
Road Map to the Year 2000 (G325-6338-00)
Figure 1: The 17-byte Character Date and Time Value data structure
Figure 2: The DOSGetDateTime value structure (all parameters are returned as binary integers)
Figure 3: Parameter list for QWCCVTDT
Figure 4a: RPG III program illustrating how to use the new data structure formats of QWCCVTDT
*===============================================================
* To compile:
*
* CRTRPGPGM PGM(XXX/DATEPGMRPG) SRCFILE(XXX/QRPGSRC)
*
*===============================================================
*
* APIERR - Standard OPM OS/400 API error data structure
* AEBYPR - Bytes provided for error information
* AEBYAV - Bytes of error information available
* AEEXID - Exception ID of Error
* AERESV - Reserved
* AEEXDT - Exception Data
*
IAPIERR DS
I B 1 40AEBYPR
I B 5 80AEBYAV
I 9 15 AEEXID
I 16 16 AERESV
I 11 116 AEEXDT
* 17 Bytes Character Date and Time Structure
IDTETIM DS
I 1 8 DTNUM8
I 9 14 DTIME
I 15 17 DTMLS
* DOSGetDateTime structure.
IDOSDS DS
I 1 1 DHOURS
I 2 2 DMIN
I 3 3 DSECS
I 4 4 DHSECS
I 5 5 DDAY
I 6 6 DMONTH
I B 7 80DYEAR
I B 9 100DTZONE
I 11 11 DDOW
* Values used to convert binary to a working format to display
IDSPVAL DS
I B 1 20DSPBN
IDIGTS DS
I 1 1 DIGT1
I 2 2 DIGT2
* Message information literal constants
IMSGINF DS
I 1 42 MSCMSG
I 'The resulting date v-C RETMSG
I 'alue is I' :'
I 'Error in Date Conver-C ERRMSG
I 'sion, Message IDI ' is :'
I 'DOSGetDateTime Value-C DOSVL
I's :'
I 'Year -C YRMSG
I '='
I 'Time Zone -C TZMSG
I '='
I 'Day of Week -C DOWMSG
I '='
* 17 Character Date/Time Message information data structure
IDTMSG DS
I 1 42 DTTXT
I 43 50 DATE8
* DOSGetDateTime Message information data structure
IDOSMSG DS
I 1 25 DOSTXT
I 26 290DOSZON
* Date Values to be checked
IDTVALS DS
I 1 8 DATES
I '02292000' C DTEOK
I '02292001' C DTEBAD
** Program Control Section
C EXSR PSETUP Init API Parms
C EXSR YYMDDT Rtv YYYYMMDDate
C EXSR LJULDT Rtv YYYYDDDate
C EXSR VALDT Check dates
C EXSR GETDOS Get DOS values
C MOVE *ON *INLR
*C YYMDDT BEGSR Rtv current
C MOVEL'*CURRENT'CVTDIF date in
C MOVEL'*YYMD' CVTDOF YYYYMMDD
C EXSR CALL17 format
C ENDSR
*C LJULDT BEGSR Rtv current
C MOVEL'*CURRENT'CVTDIF date in
C MOVEL'*LONGJUL'CVTDOF YYYYDDD
C EXSR CALL17 format
C ENDSR
*C VALDT BEGSR Setup call to
C EXSR PSETUP QWCCVTDT with
C MOVEL'*MDYY' CVTDIF the valid
C MOVEL'*LONGJUL'CVTDOF date of
C CLEARDTETIM 02/29/2000
C MOVELDTEOK DTNUM8
C MOVELDTETIM CVTDIV
C EXSR CALL17
C CLEARCVTDIV
C CLEARCVTDOV QWCCVTDT with
C CLEARDTETIM 02/29/2001
C MOVELDTEBAD DTNUM8 this is not
C MOVELDTETIM CVTDIV a leap year
C EXSR CALL17
C ENDSR
*C CALL17 BEGSR This QWCCVTDT
C CALL 'QWCCVTDT' call is setup
C PARM CVTDIF for the 17 Char
C PARM CVTDIV data structure
C PARM CVTDOF
C PARM CVTDOV
C PARM APIERR
*
C AEBYAV IFEQ 0 If *EQ ZERO
C MOVELCVTDOV DTETIM then No Error
C MOVELDTNUM8 DATE8 display
C MOVELRETMSG DTTXT values
C DTMSG DSPLY
CELSE Else
C MOVELCVTDOV DTETIM Error occured
C MOVELAEEXID DATE8 display Error
C MOVELERRMSG DTTXT message and
C DTMSG DSPLY Msg ID
C MOVELCVTDOV DTETIM and display the
C MOVELDTNUM8 DATE8 results which
C MOVELRETMSG DTTXT are blank
C DTMSG DSPLY
C ENDIF
C ENDSR
** Subroutine to retrieve/display DOSGetDateTime values
C GETDOS BEGSR
C EXSR PSETUP
C MOVEL'*CURRENT'CVTDIF When the
C MOVEL'*DOS' CVTDOF DOSGetDateTime
C CALL 'QWCCVTDT' executed the
C PARM CVTDIF DOSDS data
C PARM CVTDIV structure
C PARM CVTDOF attributes must
C PARM DOSDS be used
C PARM APIERR
* Code to Move/Display all DOSGetDateTime values
C DOSVL DSPLY
* Move/Display DOSGetDateTime Year
C MOVELYRMSG DOSTXT
C MOVE DYEAR DOSZON
C DOSMSG DSPLY
* Move/Display DOSGetDateTime Time Zone
C MOVELTZMSG DOSTXT
C MOVE DTZONE DOSZON
C DOSMSG DSPLY
* Move/Display DOSGetDateTime Day of Week
C CLEARDIGTS
C MOVE *ZEROS DSPBN
C MOVELDDOW DIGT2
C MOVE DIGTS DSPBN
C MOVELDOWMSG DOSTXT
C MOVE DSPBN DOSZON
C DOSMSG DSPLY
C ENDSR
** Subroutine to setup QWCCVTDT parameters
C PSETUP BEGSR
C CLEARAPIERR
C Z-ADD116 AEBYPR Err Data Len
C MOVE *BLANKS CVTDIF 10 Input Format
C MOVE *BLANKS CVTDIV 17 Input Values
C MOVE *BLANKS CVTDOF 10 Output Format
C MOVE *BLANKS CVTDOV 17 Output Values
C ENDSR *========================================================
*
* To compile :
*
Figure 4b: COBOL version of the code
*
CRTCBLPGM PGM(XXX/DATEPGMCBL) SRCFILE(XXX/QLBLSRC)
*
*========================================================
PROCESS XREF MAP QUOTE.
IDENTIFICATION DIVISION.
PROGRAM-ID. DATEPGMCBL.
AUTHOR. MARIO MARTINEZ.
DATE-WRITTEN. 01/21/98.
**
* Documentation on how to setup and use this API
* using RPG was printed in the issue of
* Midrange Computing. The article was written by Craig Pelkie.
* In February 1998 another article covering the changes to
* QWCCVTDT was published by MC magazine. The article was written
* by Mario Martinez.
*
* The example code to the article was written in RPG. This
* program is written in Cobol and functions like it's RPG
* counterpart.
*
* This OPM COBOL/400 program will call the "QWCCVTDT"
* OS/400 API, which was developed by IBM to change
* dates from one format to another. This API has been
* further enhanced to provide the dates with the
* actual century value.
*
* After the API is called, the results will be displayed
* on the console.
*
* The program will retrieve the current date in several
* different formats - using the actual century value.
*
* After the QWCCVTDT API will be used to change the format
* of the date 02/29/2000, which is a leap year,
* to the long Julian format. The same operation will be performed
* on the invalid date 02/29/2001, which is not a leap year.
* This is to demonstrate how invalid date error
* can be trapped using this API.
*
* Summary of program functions using OPM Cobol/400 :
*
* 1.) Retrieve the actual century value in different formats,
* using QWCCVTDT.
*
* 2.) Convert a date value to another format,
* using QWCCVTDT.
*
* 3.) Trap an invalid date error
* using a standard OPM OS/400 error data structure.
*
* 4.) Use the DOSGetDateTime data structure to retrieve date/time
* information including the Day of the Week numeric value.
*
* Numeric Value - 0 1 2 3 4 5 6
* Text Value - Sun Mon Tues Wed Thurs Fri Sat
*
* 5.) Maintain "Year 2000" compliance at all times.
*========================================================
* Program Output :
* call datepgmcbl
* The resulting date value is :19980121
* The resulting date value is :1998021
* The resulting date value is :2000060
* Error in Date Conversion, Message ID is :CPF1060
* The resulting date value is :
* DOSGetDateTime values :
* Hours = 0011
* Minutes = 0001
* Seconds = 0042
* Hundreths of Seconds = 0060
* Day of Month = 0021
* Month = 0001
* Year = 1998
* Time Zone = 0360
* Day of Week = 0003
*========================================================
*
*-
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
**
* These terms are exactly the same as Craig Pelkie
* usedinFig.4ofhisMay1993MCarticle.
*
* APIERR - Standard OPM OS/400 API error data structure
* AEBYPR - Bytes provided for error information
* AEBYAV - Bytes of error information available
* AEEXID - Exception ID of Error
* AERESV - Reserved
* AEEXDT - Exception Data
*-
01 APIERR.
05 AEBYPR PIC S9(9) COMP-4 VALUE 116.
05 AEBYAV PIC S9(9) COMP-4.
05 AEEXID PIC X(7).
05 AERESV PIC X(1).
05 AEEXDT PIC X(100).
** Values used to retreive, convert and display date conversion
* results.
*
01 date-conversion-values.
05 valid-date PIC X(08) VALUE "02292000".
05 invalid-date PIC X(08) VALUE "02292001".
05 error-message PIC X(41)
VALUE "Error in Date Conversion, Message ID is :".
05 result-message PIC X(41)
VALUE "The resulting date value is :".
01 API-PARMS.
05 input-date-format PIC X(10) VALUE SPACES.
05 output-date-format PIC X(10) VALUE SPACES.
05 input-date-api-info.
10 date-value PIC X(8).
10 time-hhmmss-value PIC X(6).
10 milleseconds-value PIC X(3).
05 output-date-api-info.
10 date-value PIC X(8).
10 time-hhmmss-value PIC X(6).
10 milleseconds-value PIC X(3).
** Parameters used when
* the DOSGetDateTime function is executed.
*
01 DOS-api-parms.
05 DOS-input-date-format PIC X(10) VALUE "*CURRENT".
05 DOS-output-date-format PIC X(10) VALUE "*DOS".
05 DOS-input-date-filler PIC X(01).
** Under the QWCCVTDT documentation this
* is known as "DOSGetDateTime" structure.
*-
01 DOS-date-time-value.
05 DOS-hours PIC X.
05 DOS-minutes PIC X.
05 DOS-seconds PIC X.
05 DOS-hundredths-of-secs PIC X.
05 DOS-day PIC X.
05 DOS-month PIC X.
05 DOS-year PIC 9(4) BINARY.
05 DOS-time-zone PIC 9(4) BINARY.
05 DOS-day-of-week PIC X.
** This working storage data structure will be used
* be used to display the retrieved DOSGetDateTime information in
* a usefull fashion.
*-
01 binary-conversion-values.
05 two-binary-bytes-value PIC 9(4) BINARY VALUE ZERO.
05 binary-conversion-value
REDEFINES two-binary-bytes-value.
10 FILLER PIC X.
10 one-byte-binary-value PIC X.
*-
PROCEDURE DIVISION.
main-process.
PERFORM setup-yyyymmdd-date-format.
PERFORM setup-longjul-date-format.
PERFORM convert-valid-date-format.
PERFORM convert-invalid-date-format.
PERFORM retrieve-dsplay-DOSGetDateTime.
GOBACK.
*-
setup-yyyymmdd-date-format.
MOVE "*CURRENT" TO input-date-format.
MOVE "*YYMD" TO output-date-format.
PERFORM call-date-api-17char.
PERFORM display-date-api-output.
*-
setup-longjul-date-format.
MOVE "*CURRENT" TO input-date-format.
MOVE "*LONGJUL" TO output-date-format.
PERFORM call-date-api-17char.
PERFORM display-date-api-output.
*-
convert-valid-date-format.
INITIALIZE API-PARMS.
MOVE valid-date
TO date-value OF input-date-api-info.
MOVE "*MDYY" TO input-date-format.
MOVE "*LONGJUL" TO output-date-format.
PERFORM call-date-api-17char.
PERFORM display-date-api-output.
*-
convert-invalid-date-format.
INITIALIZE API-PARMS.
MOVE invalid-date
TO date-value OF input-date-api-info.
MOVE "*MDYY" TO input-date-format.
MOVE "*LONGJUL" TO output-date-format.
PERFORM call-date-api-17char.
PERFORM display-date-api-output.
**
* Routine to call the QWCCVTDT API using
* the 17 Character Date and Time Value Structure
*
call-date-api-17char.
CALL "QWCCVTDT" USING
input-date-format,
input-date-api-info,
output-date-format,
output-date-api-info,
APIERR.
*-
display-date-api-output.
*
* IfAEBYAVisnotequaltozerothismeanstherewas
* error when the QWCCVTDT was called. The variable
* AEEXID will contain the OS/400 Message ID of the
* specific error encountered.
*
*
* Code to display other returned information on console
*
DISPLAY result-message date-value OF output-date-api-info.
retrieve-dsplay-DOSGetDateTime.
** Code to access and display returned information
* for the DOSGetDateTime data structure.
*
* Setup and call QWCCVTDT API
*
INITIALIZE API-PARMS.
CALL "QWCCVTDT" USING DOS-input-date-format
DOS-input-date-filler
DOS-output-date-format
DOS-date-time-value
APIERR.
IF (AEBYAV IS NOT EQUAL TO ZERO)
DISPLAY error-message AEEXID
END-IF.
*
* Display all DOSGetDateTime values upon console
*
DISPLAY "DOSGetDateTime values : "
INITIALIZE binary-conversion-values.
MOVE DOS-hours TO one-byte-binary-value.
DISPLAY "Hours = " two-binary-bytes-value.
INITIALIZE binary-conversion-values.
MOVE DOS-minutes TO one-byte-binary-value.
DISPLAY "Minutes = " two-binary-bytes-value.
INITIALIZE binary-conversion-values.
MOVE DOS-seconds TO one-byte-binary-value.
DISPLAY "Seconds = " two-binary-bytes-value.
INITIALIZE binary-conversion-values.
MOVE DOS-hundredths-of-secs TO one-byte-binary-value.
DISPLAY "Hundreths of Seconds = " two-binary-bytes-value.
INITIALIZE binary-conversion-values.
MOVE DOS-day TO one-byte-binary-value.
DISPLAY "Day of Month = " two-binary-bytes-value.
INITIALIZE binary-conversion-values.
MOVE DOS-month TO one-byte-binary-value.
DISPLAY "Month = " two-binary-bytes-value.
DISPLAY "Year = " DOS-year.
DISPLAY "Time Zone = " DOS-time-zone
INITIALIZE binary-conversion-values.
MOVE DOS-day-of-week TO one-byte-binary-value.
DISPLAY "Day of Week = " two-binary-bytes-value.
DSPLY The resulting date value is : 19971209
DSPLY The resulting date value is : 1997343
DSPLY The resulting date value is : 2000060
DSPLY Error in Date Conversion, Message ID is : CPF1060
DSPLY The resulting date value is :
DSPLY DOSGetDateTime Values :
DSPLY Year = 1997
DSPLY Time Zone = 0360
DSPLY Day of Week = 0002
Figure 5: Output Produced by the Illustrative RPG
Program
Program
LATEST COMMENTS
MC Press Online