RPG Developer Network News
www.RPGIV.com
This is not your father's Q38
(c) 1999 by Robert Cozzi Jr. All rights reserved.

Monday, February 15, 1999

Editor: Robert Cozzi, Jr.

Stealing Time
The Easy Way to Format a Date or Time Value


Stealing Time

One of the more useful features of the Integrated Language Environment (ILE) is the ability for one language to call procedures written in any other language. Some of this was illustrated in previous columns here on the RPG Developer Network News pages. (See Adding High-Level Math to RPG IV.)

The C language offers several functions that provide anywhere from the simple to the sophisticated formatting of date and time values. These functions have been around for years and work similarly on all platforms. RPG IV can take advantage of these functions in a manner similar to that used to access the C language runtime Math functions.

It seems as though the Year 2000 issue has sparked an interest in routines that handle date and time values. Many of our custom date routines have been rewritten or completely replaced with more reliable and accurate system-based functions. But what hasn't been widely addressed (in RPG languages) is date formatting issues. Date formatting provides routines that accept a date value and return to the caller, a formatted date value. Certainly RPG IV provides a large selection of date formatting routines, such as *ISO, *USA, and *EUR. But none of these formats will return a date value as "Monday, February 15, 1999" a format needed for on-line displays, reports and forms output.

The time functions in the C runtime library provide this additional date formatting. These functions can be called from within RPG IV, simply and easily. There are several functions that return a date or time in a predefined format, and there is one function that allows you to specify a customized return format.

 QC2LE Binding Directory

There is a special binding directory on every AS/400 named QC2LE. It contains a list of modules and service programs needed for the C language runtime environment.

The C language has several interesting methods for evoking a procedure call. Fortunately one of these methods allows the procedure to be called via a pointer to a procedure, and IBM has provided these functions in service programs. This is where the binding directory comes in. IBM provides with OS/400, the QC2LE binding directory. In this binding directory is a list of all the modules and service programs that make up the C runtime library.

Since the C runtime library functions are stored in service programs. Binding to them is as easy as writing and RPG IV procedure prototype. In fact, it is so easy to interface RPG IV with C that I usually say that the RPG IV language natively supports everything in the C language. Take that you C bigots!

Reading C Function Prototypes

The easiest way to call an ILE procedure in RPG IV is to write a prototype for that procedure. Since all C language functions are actually ILE procedures, writing a prototype is all that is necessary. But how do you translate C function prototypes into RPG IV prototypes? Let's look at a simple math function in C, the sine function. The following line of code is the C function prototype for the asctime (convert to ASCII time) function.

char* asctime( const struct tm *tm);

This function returns the time as a character string. Since C is primarily an ASCII-based language, we can translate the term "ASCII time" into "Convert time to Character". To understand the prototype, let's break it down into its base components.

  1. char* asctime( const struct tm *tm );

The char* identified on the left in item 1, above is the format for the value returned by the function. This identifies the format of the value returned by the function as a pointer to a character string. In RPG IV, this would simply be a pointer declaration since RPG IV doesn't support the concept of a "pointer to" variable.

  1. char* asctime( const struct tm *tm );

The asctime identifier in line 2, above is the name of the procedure that is called when this prototype is evoked. If the function accepts parameters, their definitions are enclosed in parentheses immediately following the function name.

  1. char* asctime( const struct tm *tm );

The asctime function accepts one parameter. This parameter is a pointer to a data structure that contains nine 4-byte integers. The parameter further breaks down as follows:

const struct tm *tm

The const identifier equates to the RPG IV CONST keyword for parameters. It indicates that the parameter's value is not going to be modified by the called procedure.

const struct tm *tm

The struct  identifier indicates that the parameter is a data structure. In this case, it is a data structure with a format that same as the tm data structure. In C, a structure can be created and then used as a format for other references, such as parameter definitions. It is similar to using the LIKE keyword in RPG IV.

const struct tm *tm

The *tm  identifier is actually two things. It is an asterisk followed by a parameter name. The asterisk indicates that the parameter is a pointer (i.e., it is passed by reference). The tm identifier is effectively a documentation aide that gives you the name of the parameter. Like RPG IV, the parameter name on prototypes is considered a comment, and is not a declarative item.

After reviewing this, we now understand that the parameter for the asctime function is the following:

  1. A constant.
  2. A constant data structure of format of tm.
  3. A pointer to a constant data structure of format tm.

To call the C asctime function from RPG IV, we will need a prototype written in RPG IV.

Translating C Prototypes to to RPG IV Prototypes

Knowing the C syntax for the prototype and knowing RPG IV should allow the translation of the asctime function's prototype to RPG IV. To translate the C function prototype to an RPG IV procedure prototype, we need the following:

  1. A prototype that will evoke the C runtime library's asctime function. NOTE: C functions are normally in all lowercase letters.
  2. A prototype that returns a pointer.
  3. A prototype that accepts one parameter--a constant value, passed by reference. NOTE: In C the default behavior for passing parameters is to pass-by-value. When a pointer is required for the parameter, that parameter is a passed-by-reference parameter which is the default behavior for the RPG IV and CL languages.

To translate the C function prototype to an RPG IV prototype, the prototype must call 'asctime' (in lowercase). And it must accept one parameter--the data structure whose format is like the tm data structure.

.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++++++
0001 D asctime         PR              *   Extproc('asctime')
0002 D  struct_tm                          const like(TM)

The first line in this prototype contains the name of the prototype, and the EXTPROC keyword. Normally, prototypes don't contain the EXTPROC keyword, as this keyword is the default. However, since the name of the asctime function in the C language is case sensitive, (i.e., all lowercase letters) and RPG IV automatically converts all non-quoted source code to uppercase, we need to specify the EXTPROC keyword with 'asctime' name enclosed in quotes.

The return value, on line 1, is an asterisk. This simply means that a pointer is being returned. The storage pointed to by the pointer is created by the asctime function. Storage being pointed to, should be copied to an RPG IV field upon return. The %STR built-in function can be used as a wrapper around the asctime time procedure call. This causes the returned value to be converted into a character field that can be handled by more conventionally with RPG IV syntax.

If you are not on OS/400 V3 R7 or later, then the %STR built-in function is not in the RPG IV compiler on your system. I have written a pure RPG IV version of %STR, called STR that can run on V3R2 systems. It is part of the example code included with this article.

The second line of this RPG IV prototype declares the parameter for the procedure. This parameter is a const, and is defined 'like tm'. This kind of declaration indicates that somewhere in the source code a variable or data structure name TM is defined. The parameter inherits the attributes of the like variable. In fact, the TM data structure is included in the same /COPY source member that contains the ASCTIME prototype.

Since the parameter on line 2 is a normal RPG IV parameter, it is passed by reference. This is the same as passing a pointer in the C language. The parameter does not need to be an * data-type. We could have, however, specified the parameter as an * (pointer). In this situation, we would have needed to specify the VALUE keyword. The VALUE keyword causes the parameter to be passed by value. Since the parameter is itself a pointer, the value being passed to the called procedure, is the pointer itself--the same as that required by the C language. In this case, however, the CONST keyword could not have been specified since CONST and VALUE are mutually exclusive. In either case, the technique used in the example above, is the simplest form of declaration, and it is the form I recommend.

TM and TIME_T Definitions

The time functions in C being used by RPG IV typically require a time value in one form or another. Unfortunately the standard C library doesn't know about the native RPG IV time data-type. So it uses a custom time format.

The functions in this article take advantage of two different time formats. The first is the TIME_T format.

TIME_T is a structure that contains a so-called long int value. This long int value contains the time, in seconds. In RPG IV, this structure contains a single subfield. That subfield must be a 4-byte integer. In RPG IV that's a 10i0 field, as follows:

.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++++++
0001 D time_t         DS                  INZ
0002 D   time_Secs                   10i 0

This data structure can be used with the LIKE keyword to format the various time procedure parameters that require a TIME_T data structure. In most situations, however, a pointer to a TIME_T value is required. Again, since RPG IV has a default behavior of pass-by-reference, you could simply specify the parameter as a 10i0 without the LIKE keyword (on the prototype) and everything will work correctly.

Another approach is to simply declare a stand-alone field as an int4 value. This allows the TIME_T value to be passed by value on those few functions that require pass-by-value. I prefer this option, as it is the one recommended by the IBM RPG IV developers. However, since none of our examples or the prototypes for the time functions that I have ported use the pass-by-value technique, either is appropriate.

.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++++++
0001 D time_t         S              10i 0

As mentioned, you don't really need this data structure. It does, however,  help clarify the type of parameter required by the function when it is used.

TM Data Structure

.....DName+++++++++++EUDS.......Length+TDc.Functions++++++++++++++++++++++++++++
     D tm              DS                  
     D  tm_sec                             Like(int4)
     D  tm_min                             Like(int4)
     D  tm_hour                            Like(int4)
     D  tm_mday                            Like(int4)
     D  tm_mon                             Like(int4)
     D  tm_year                            Like(int4)
     D  tm_wday                            Like(int4)
     D  tm_yday                            Like(int4)
     D  tm_isdst                           Like(int4)

 

The TM data structure contains nine 4-byte integer fields. To get a date, only fields 4 through 6 need to be something other than zero. The table below documents each of the TM data structure's subfields.

Type Subfield Description
10i0 (int) tm_sec seconds (0 to 59)
10i0 (int) tm_min minutes (0 to 59)
10i0 (int) tm_hour hour (0 to 23)
10i0 (int) tm_mday day of month (1 to 31)
10i0 (int) tm_mon month (0 to 11)
10i0 (int) tm_year year minus 1900 (e.g., 1978 = 78)
10i0 (int) tm_wday day of week (0 to 6)
10i0 (int) tm_yday day of year (0 to 365)
10i0 (int) tm_isdst "Is daylight savings time?"
0 = No daylight savings
1 = Daylight savings
-1 = Unknown

 

What's Next

Now that we can read and convert the C time functions into RPG IV prototypes, we have some more work to do. We need to create prototypes for the following C time functions.

Function Description
asctime Returns character string version of date/time.
ctime Returns character string version of date/time.
time Gets current time into a time_t value.
localtime Returns a TM structure from a time_t value.
mktime Returns a time_t value from a TM structure.
strftime Returns a formatted character string from a date/time value.

To view the RPG IV prototype for these C functions, click on the function name. The description of each prototype is provided.

To download the source code for this months article, click on the file name in the table below. Note that only the TIMERPG.RG4 source member should be uploaded to the QRPGLESRC source file. The other source should be uploaded to the QCPYSRC source file.  QCPYSRC contains source that is /COPYed into the RPG IV program when it is compiled.

 

Source Member

Description

time.rpgle

RPG IV prototypes for the C language runtime time library.

This is a /COPY source member.

timerpg.rpgle

Example RPG IV program that calls all the C language runtime time routines featured in this article.

datatype.rpgle

Used to include "primitive" data-type variables such as INT, LONG, SHORT, DOUBLE and PTR. These fields are used with the LIKE keyword, for example"

D/COPY datatype
D myValue     S                  Like(int)

This is a /COPY source member.