Still stuck with legacy date and time fields in your database and programs? Let RPG IV do the heavy lifting for you.
In V5R3, date-handling is superb and calculations are straightforward. Fine, you say, but what about all those pesky character or numeric fields we still have to fight with from designs that originated before the date- and time-field data types were available? Those fields might have two- or four-digit years and might be in YMD order or MDY order or, outside the
No sweat. RPG handles these legacy fields easily and concisely, but based on code I've seen recently, not everyone knows how to do it.
I'm surprised that some developers still write code that uses data structures or sub-stringing or concatenation to manipulate dates—not only in converted RPG III code, but also in new RPG IV development. Much of this code deals with creating legacy date and time fields used to timestamp database records, though it isn't restricted to this, because you often want to add or subtract from one of these stored dates. In many cases, the developer has some reusable, tried-and-tested code snippets. While I applaud code reuse, it wouldn't hurt to upgrade the code, because RPG IV date-handling makes the code shorter and easier to understand.
Some Bad Examples
Following are three examples of code that I came across recently. I found each as shown; all I've done is add standalone fields in place of database fields. See if you find any obvious errors.
This is example 1, from a developer who has been writing code for at least 10 years:
D t_audit_date s 8p 0
D t_audit_time s 6p 0
D #TimeDate DS
D #Time 1 6 0
D #Date 7 14 0
D #CYMD S D DATFMT(*
C time TimeStamp 14 0
C move TimeStamp #TimeDate
C *
C move #cymd wrkdate 8 0
C move wrkdate t_audit_date
C move #time t_audit_time
This is example 2. Similar code also turned up in several programs this developer wrote.
D Todays_Date_C DS 8
D Todays_Date 8 0 Overlay(Todays_Date_C)
D Curr_Date s D inz(*sys)
/free
Todays_Date_C = %subst(%Char(Curr_Date):1:4) +
%subst(%Char(Curr_Date):6:2) +
%subst(%Char(Curr_Date):9:2);
This is example 3. An experienced developer from a large contracting company wrote this about three years ago.
h DATEDIT(*YMD)
D HMDATE S 8A
D HMTIME S 6S 0
c move *Date HMDATE
c TIME HMTIME
Now, what's your opinion of the above code? Do you see any problems?
Better Examples
While I labeled them as bad examples, two of the routines work well, and the third works most of the time. But the code can be better.
Bad example 1 works. This is a snippet of debugged code that the developer uses routinely. The main problem is the wordiness and the obfuscation caused by using the data structure. Data structures have their place, but when used like this, you have to look at two places in the program to see what's going on. Consider this shorter code, which produces the same result without using a data structure.
D t_audit_date s 8p 0
D t_audit_time s 6p 0
D TimeStamp s z
/FREE
TimeStamp = %timestamp();
t_audit_date = %dec(%date(Timestamp):*
t_audit_time = %dec(%time(Timestamp):*HMS);
Bad example 2 also works, and again, the only problem is the wordiness. This much shorter piece of code produces the same results.
D Todays_Date s 8 0
D Curr_Date s D inz(*sys)
/free
Todays_Date = %dec(Curr_Date:*
Bad example 3 really is bad, though it works most of the time. (For reference, check if your boss thinks accuracy 99 percent of the time is acceptable.) I've seen this combination of a reserved word and the TIME opcode in way too many programs. In RPG IV, the reserved words UDATE, *DATE, UMONTH, *MONTH, UYEAR, *YEAR, UDAY, and *DAY all refer to the date the job started to run. This job date does not change. The TIME opcode, on the other hand, comes from the system clock that is constantly changing. If the job runs past midnight, such date and time combinations will be incorrect, and it will look like the later transactions occurred before the earlier ones.
It makes little sense to combine a static date and a variable time to timestamp a record unless you can guarantee that the job will never run past midnight. Why take the risk? This is the correct (and just as easy) way to timestamp a record with legacy fields:
D HMDATE S 8A
D HMTIME S 6S 0
D TS S Z
*=======================================================
/free
TS = %timestamp();
HMDATE = %char(%date(TS):*
HMTIME = %dec(%time(TS):*HMS);
The Cheat Sheet
OK, here's the cheat sheet—short on theory, long on examples. It is in the form of an RPG IV program, with the variable names constructed to let you intuitively know what they contain. CC, for example, represents the century, the first two digits, of a year. YY is the last two digits of a year, MM is the month of a year, and DD is the day of a year. C by itself means 0 for 19 and 1 for 20. At the end of the variable name, _n means it is a numeric field and _c means it is a character field. So CCYYMMDD_n is a numeric field that might contain 20071231.
The first section is for creating legacy fields in a variety of formats. The second section is for converting legacy fields to native data types so that you can easily do calculations on them.
* Legacy Date and Time Handling Cheat Sheet
D TS s z
D Date s d
D CCYYMMDD_n s 8 0
D YYMMDD_n s 6 0
D CYYMMDD_n s 7 0
D MMDDYY_n s 6 0
D
D CCYYMMDD_c s 8
D YYMMDD_c s 6
D CYYMMDD_c s 7
D MMDDYY_c s 6
D HHMMSS_n s 6
D HHMMSS_c s 6
/free
//=== Legacy Date & Time Stamps - from System Clock ===
TS = %timestamp();
//--- Set Numeric Legacy Date Fields ---
CCYYMMDD_n = %dec(%date(TS): *
YYMMDD_n = %dec(%date(TS): *YMD);
CYYMMDD_n = %dec(%date(TS): *CYMD);
MMDDYY_n = %dec(%date(TS): *MDY);
//--- Set Character Legacy Date Fields ---
CCYYMMDD_c = %char(%date(TS): *
YYMMDD_c = %char(%date(TS): *YMD0);
CYYMMDD_c = %char(%date(TS): *CYMD0);
MMDDYY_c = %char(%date(TS): *MDY0);
//--- Set Legacy Time Fields ---
HHMMSS_n = %dec(%time(TS): *HMS);
HHMMSS_c = %char(%time(TS): *HMS0);
//=== Legacy Date Stamp from Job Date ===
CCYYMMDD_n = %dec(%date(*DATE): *
CCYYMMDD_c = %char(%date(*DATE): *
//=== Converting Legacy Dates to True Dates ===
//--- True date from numeric fields ---
Date = %date(122507: *MDY);
Date = %date(251207: *DMY);
Date = %date(071225: *YMD);
Date = %date(12252007: *
Date = %date(25122007: *EUR);
Date = %date(20071225: *
//--- True date from character fields, no separators ---
Date = %date('122507': *MDY0);
Date = %date('251207': *DMY0);
Date = %date('071225': *YMD0);
Date = %date('12252007': *USA0);
Date = %date('25122007': *EUR0);
Date = %date('20071225': *
//--- True date from character fields with separators ---
Date = %date('12/25/07': *MDY);
Date = %date('25/12/07': *DMY);
Date = %date('07/12/25': *YMD);
Date = %date('12/25/2007': *
Date = %date('25.12.2007': *EUR);
Date = %date('2007-12-25': *
See how easy it is? There is no longer any need for data structures, sub-stringing, or concatenation to create legacy dates and times. Nor is any trickery needed to convert a legacy field to a native data type. Just print this code and pin it on your wall as a reference. And be sure to share it with your coworkers who don't read MC Press Online so they know what they are missing.
LATEST COMMENTS
MC Press Online