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.

Given that IBM provided the UDATE reserved word to return the job date and that you could code *DATE and *TIME in display files, it seemed logical to assume that there would be a UTIME reserved word to return the time. There wasn't. The closest most developers could come was the TIME opcode. It was still quite easy to use UDATE to set the date and the TIME opcode to set the time. And therein was the crux of the problem, because this date and time combination is inherently dangerous.

What many failed to consider is that the value returned by UDATE or *DATE is static. The IBM manuals are quite clear that UDATE and *DATE access the Job date (so do UMONTH, UDAY, UYEAR, *MONTH, *DAY, and *YEAR). The Job date is the date the job started running, and while the job exists, it does not change.

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

/FREE

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.

Conclusion

Sam Lennon is an analyst, developer, consultant and IBM i geek. He started his programming career in 360 assembly language on IBM mainframes, but moved to the AS400 platform in 1991 and has been an AS400/iSeries/i5/IBM i advocate ever since.