UDATE may not be the right way to get the current date, but old habits are hard to break.
When you need to put a date and a time together in your RPG IV program, be sure you understand how to use UDATE, *DATE, the TIME opcode , %DATE, %TIME, and %TIMESTAMP.
History of UDATE Misuse
In early versions, RPG provided the UDATE reserved word as an easy way to refer to the six-digit job date. The vast majority of RPG date code I have run into revolves around UDATE, and I expect this holds true in many shops. UDATE makes it really simple to put a date in a report heading or on a display screen. *DATE is a reserved word in later RPG versions that provides similar simplicity with an eight-digit date.
Where some developers ran into trouble was when they needed to put a date and a time together. In my experience, this most frequently occurred when time-stamping a database record, but also occasionally in report headings or on display screens. Historically, database timestamps consisted of two fields: a date field and a time field. Most of the existing code to create these two fields works most of the time, but every now and then the operational environment changes and the results are just plain wrong.
What many failed to consider is that the value returned by UDATE or *DATE is static. The
In contrast, the TIME opcode and the %DATE, %TIME, and %TIMESTAMP BIFs return dynamic data from the system clock. Time values are always changing, and the date value changes at midnight.
Pairing an unchanging Job date with a changing time works in most situations. As long as the job does not continue running past midnight, you get valid date and time combinations. However, once the job runs past midnight, timestamps suddenly appear to have jumped back 24 hours.
A Recent Bad Example
This recently came to my attention and provoked this article. The shop floor in a local company runs two shifts five days a week from 7:30 a.m. to 11:30 p.m. There are 5250 devices all over the place, and the floor manager gets production statistics from the transactions that track items through the systems. Recently, they had to work an extra four-hour Saturday shift. While this was an exciting change in today's economic climate, the overtime caused the floor manager's budget to be closely scrutinized. The following week, he found his productivity reports didn't make sense. It looked like an enormous amount of overtime work had been done on Saturday and productivity on Monday was way down.
It turned out the code that puts the timestamps in the transactions uses the UDATE and TIME opcode combination.
The floor workers sometimes (often?) forget to end their 5250 sessions, so the regular Monday through Friday day-end process always ends the QINTER subsystem at 11:45 p.m. The regular day-end process doesn't run on Saturday or Sunday, so some 5250 sessions that started on Saturday morning were still running on Monday morning at 7:30 a.m. and continued running until QINTER was ended at 11:45 p.m. on Monday evening. An interactive 5250 session, from sign-on to sign-off, is a single job, so some of these sessions had a job date that remained constant--with Saturday's date--until the day-end process on Monday. Many of the transactions that occurred on Monday had a Saturday timestamp. Hence the discrepancy, the floor manager's annoyance, the IT department's embarrassment, and a significant amount of wasted time chasing down the problem.
Maybe QINTER should be brought down every evening. Maybe inactive 5250 sessions should time out after eight hours. Maybe there are other ways to stop jobs running over midnight.
Nevertheless, with RPG IV, it is a trivial effort to create timestamps that will always be correct, regardless of when the job starts or how long it runs, so there is no excuse for timestamp code that doesn't work all the time.
When to Use UDATE
Use UDATE with the clear understanding that it might not be the current date. It might be the prior day if the job started before midnight. It might be almost any date if the job was submitted with a DATE(newdate) parameter, or the job itself may have issued a CHGJOB DATE(newdate) command.
If you want the current date, use %DATE(), recognizing that this really is the current date and it changes at midnight. For example, you probably don't want to code a page header routine that issues %DATE() for every new page, because if the program runs over midnight, you will get a new date after midnight. Instead, just code %DATE() once during program initialization.
When Not to Use UDATE
Do not use UDATE when you want to pair a date with a time. If you are designing a new system with a timestamp in the records, consider using a field with a timestamp data type, type Z. Populate it with the %TIMESTAMP() BIF.
If you are working with a legacy system that has separate date and time fields for the time stamp, use code like this:
D TS s z
TS = %timestamp();
CHGDTE = %dec(%date(TS):*YMD);
CHGTME = %dec(%time(TS):*HMS);
Granted, you could just use %DATE() followed by %TIME(), but there is a remote chance that the system clock could roll to the next day between the two instructions.
(If you need help working with legacy dates, see "RPG IV Legacy Dates Cheat Sheet.")
UDATE isn't inherently evil. Just be sure you understand what it reflects when you use it, and seriously consider using a BIF when there is a time involved.