Date addition is nice, but what about hours?
In response to the recent articles on working with date durations, I received a note from Lori N. asking if similar support exists for smaller time frames. Specifically, Lori says, "Thanks for the articles; they are very helpful. I have a monitoring job that checks a data area to make sure we have sent updates to another system within a specific time frame. I used CEEDATE to get today and yesterday, but I'd also like to use a time interval so I can say 'If we have not sent anything in the last 6 hours, send an alert.' Is there something that takes the current time and subtracts a specific interval so I can compare the last times? If not, I'll have to be satisfied with checking dates. Thank you for your help with this."
To which I respond, "Be satisfied with checking dates rather than hours? We should never compromise on such a basic need when working on i!"
There are many ways to satisfy this requirement, and in today's article we'll examine one approach based on the CEELOCT API. In future articles, we'll also look at some of the many alternatives that exist for the CL developer. Back in "So You're Looking for a Date?," we introduced the Get Current Local Time (CEELOCT) API, which returns the local time. The API documentation can be found here, and I've repeated the API's parameter list below:
Required Parameters:
1 | output_Lilian | Output | INT4 |
2 | output_seconds | Output | FLOAT8 |
3 | output_Gregorian | Output | CHAR23 |
Omissible Parameter:
4 | fc | Output | FEEDBACK |
In that article, we concentrated on the first parameter, output_Lilian, which is a 4-byte signed integer representing the current local date in Lilian format. At that time, we briefly discussed the other parameters but effectively ignored them. Today we'll take a closer look at the second parameter, output_seconds.
Output_seconds is an 8-byte floating point value that represents the number of seconds since 00:00:00 October 14, 1582. That is, an output_seconds value of 86 401 represents 00:00:01 October 15, 1582; a value of 13 431 693 600 represents 10:00:00 June 1, 2008; and 13 431 693 601 is equivalent to 10:00:01 June 1, 2008. Similar to how output_Lilian allows us to easily add and subtract durations involving days, output_seconds allows us to easily add and subtract seconds. In our example program, we'll use a duration of 21 600 seconds, the number of seconds in six hours.
The one minor hurdle we have to overcome is that CL doesn't support floating point variables. But we won't let a little problem like that deter us from our mission! We will define output_seconds as an 8-byte character variable and then use the Copy Numeric Value (CPYNV) API (actually a Machine Interface instruction) to convert the 8-byte floating point value to a 15-digit decimal value. We won't go into the details of the Copy Numeric Value API, but the API basically allows us to convert a numeric value from one format to another. In our case, we will convert from a floating point format to a decimal (*DEC) format. The Copy Numeric Value API documentation can be found here and is briefly discussed a bit later in this article.
First, we need a program to perform some action--for instance, sending updates, as in Lori's note, and then updating a data area with the time the send was complete. The following program, Send Update (SNDUPD, performs such a function:
Pgm
Dcl Var(&Snd_Float) Type(*Char) Len(8)
Dcl Var(&CurLilDt) Type(*Int)
Dcl Var(&CurGregDt) Type(*Char) Len(23)
/* Send the updates and then: */
CallPrc Prc('CEELOCT') Parm((&CurLilDt) (&Snd_Float) +
(&CurGregDt) (*Omit))
ChgDtaAra DtaAra(QGPL/LSTSNDTIME) Value(&Snd_Float)
EndPgm
The above program sends the updates (shown as a comment), calls the CEELOCT API to retrieve the current date and time, and then stores the returned floating point value for the current date and time (the variable &Snd_Float) in the data area QGPL/LSTSNDTIME. This data area is created with the following command:
CRTDTAARA DTAARA(QGPL/LSTSNDTIME) TYPE(*CHAR) LEN(8)
The following program, Check Last Send (CHKLSTSND), retrieves the value of the data area QGPL/LSTSNDTIME, determines if more than six hours have elapsed since the last send operation, and then takes appropriate action.
Pgm
Dcl Var(&Snd_Float) Type(*Char) Len(8)
Dcl Var(&Snd_Dec) Type(*Dec) Len(15 0)
Dcl Var(&Cur_Float) Type(*Char) Len(8)
Dcl Var(&Cur_Dec) Type(*Dec) Len(15 0)
Dcl Var(&Alert_Time) Type(*Dec) Value(21600)
Dcl Var(&Delay_Time) Type(*Dec)
Dcl Var(&Float_Dfn) Type(*Char) Len(7) +
Value(X'01000800000000')
Dcl Var(&Dec_Dfn) Type(*Char) Len(7) +
Value(X'03000F00000000')
Dcl Var(&CurLilDt) Type(*Int)
Dcl Var(&CurGregDt) Type(*Char) Len(23)
Loop: RtvDtaAra DtaAra(QGPL/LSTSNDTIME) RtnVar(&Snd_Float)
CallPrc Prc('_LBCPYNV') Parm((&Snd_Dec) (&Dec_Dfn) +
(&Snd_Float) (&Float_Dfn))
CallPrc Prc('CEELOCT') Parm((&CurLilDt) (&Cur_Float) +
(&CurGregDt) (*Omit))
CallPrc Prc('_LBCPYNV') Parm((&Cur_Dec) (&Dec_Dfn) +
(&Cur_Float) (&Float_Dfn))
If Cond((&Cur_Dec - &Snd_Dec) > &Alert_Time) +
Then(Do)
SndPgmMsg Msg('Time to send alert') +
ToPgmQ(*Ext)
ChgVar Var(&Delay_Time) Value(300)
EndDo
Else Cmd(ChgVar Var(&Delay_Time) +
Value(&Alert_Time - (&Cur_Dec - &Snd_Dec) +
+ 1))
DlyJob Dly(&Delay_Time)
GoTo CmdLbl(Loop)
EndPgm
Within CHKLSTSND, we first declare the variables we will be using. &Snd_Float is an 8-byte character field that will contain the floating point value stored by the SNDUPD program. Note that there is an assumption that the SNDUPD program has run at least one time prior to calling program CHKLSTSND. &Snd_Dec is defined as TYPE(*DEC) with 15 digits and no decimal positions. This declared size for &Snd_Dec is sufficient to hold a number of seconds since October 14, 1582, that exceeds the year 9999 (which should be sufficient for our needs) and will be used to hold the decimal equivalent of &Snd_Float. &Cur_Float is an 8-byte character field that will store the floating point value of the number of seconds since October 14, 1582, and the current local date and time. &Cur_Dec is the 15-digit decimal equivalent of &Cur_Float. &Alert_Time is a decimal value used to store the number of seconds in a duration of six hours (21 600 seconds), and &Delay_Time is a decimal value used to control how often the CHKLSTSND program runs.
&Float_Dfn is a 7-byte character field that defines the type of numeric value the Copy Numeric Value API is converting from. The initialized value x' 01000800000000' defines an 8-byte floating point value. &Dec_Dfn is a 7-byte character field that defines the type of numeric value the Copy Numeric Value API is to convert to. The initialized value x' 03000F00000000' defines a 15-digit packed decimal variable with no decimal positions. Details on these initialization values can be found here for those of you with inquiring minds.
The last two variables, &CurLilDt and &CurGregDt, are needed when calling the CEELOCT API but are not used in the current program.
When run, CHKLSTSND first retrieves the floating point value stored by SNDUPD in the data area QGPL/LSTSNDTIME. CHKLSTSND then uses the Copy Numeric Value API to convert this floating point value to a decimal value and stores the result in &Snd_Dec. The program calls the CEELOCT API to retrieve the current date and time in seconds (the variable &Cur_Float) and calls Copy Numeric Value to convert this floating point value to the decimal variable &Cur_Dec.
Having now obtained the time in seconds for both the last send function and the current time, CHKLSTSND compares the difference (&Cur_Dec - &Snd_Dec) to the maximum number of seconds that should elapse between send operations (the variable &Alert_Time). If more than &Alert_Time seconds have elapsed, CHKLSTSND sends an alert (shown for simplicity as a SNDPGMMSG) and then sets &Delay_Time to 300 seconds (or five minutes).
If the amount of elapsed time is less than &Alert_Time, the program calculates how many seconds of &Alert_Time remain after the last successful send operation and sets &Delay_Time to this value (plus one second).
The program then uses the DLYJOB command to delay &Delay_Time seconds and loops back to retrieving the floating point value stored by SNDUPD. If CHKLSTSND is in an alert status, the program will alert the operator every five minutes that something is wrong and continue with such alerts until SNDUPD successfully updates the data area QGPL/LSTSNDTIME. Otherwise, the program will sleep until an alertable situ
LATEST COMMENTS
MC Press Online