There are some things you need to consider when using epoch-based time values.
Last month, in the article "Understanding and Using UNIX-Type Time Values," we looked at how to answer the specific question, "Does anyone know how to convert the time values that are returned from the stat() IFS api for an IFS file?" Two possible solutions were reviewed: using RPG timestamp support and also using C run-time API support. Today, we'll look at bit deeper into the time values returned by many UNIX-type APIs and some considerations related to the use of these time values.
As a review of the stat() API, this API (along with many other UNIX-type APIs) currently returns time values as a 4-byte signed integer value representing the number of seconds since the epoch, where the epoch is defined as January 1 1970 at time 00:00:00 UTC. A signed 4-byte integer can support a range of values from -2,147,483,648 to +2,147,483,647, which certainly seems like a whole lot of seconds, but just how much time is that?
If you add 2,147,483,647 seconds to January 1 1970 00:00:00, you end up with a maximum time that such an integer-based epoch time value can represent of January 19 2038 03:14:07 UTC. Rather reminiscent of Y2K, UNIX-type APIs such as stat() have a problem sometimes referred to as Y2K38. The problem is that depending on how an application is written, the second after January 19 2038 03:14:07 UTC may result in an error situation such as a numeric overflow, an incorrect date and time value such as January 1 1970 00:00:01 being returned, or some other failure just sure to ruin someone's day. This particular consideration, as it relates specifically to the stat() API, is not overly critical today (assuming that your system is currently running in the year 2012, which not all systems do, a little fact that might surprise some readers) as stat() only returns time values representing the date and time of a file's last access, last data change, and last status change.
The C run-time library does provide a set of APIs to expand the range of UNIX epoch-based time values. Last month, we looked at the ANSI C run-time APIs of ctime(), localtime(), and asctime(). Two of these APIs, ctime() and localtime(), have Y2K38 considerations as they represent time as 4-byte (32-bit) signed integer values. Starting with IBM i 6.1, the ILE C run-time library has provided extensions to these ANSI library functions that allow time to be represented as 8-byte signed integer (64-bit) values. Two of these APIs, named ctime64() and localtime64(), provide the same functionality of ctime() and localtime(), respectively, while supporting dates beyond the year 2038. Some other time-oriented APIs supporting 8-byte integer values based on the 1970 epoch are difftime64(), gmtime64(), mktime64(), and time64(). These APIs calculate the difference in seconds between two time values, convert a time value to a UTC-based time structure, convert a time value to a local-time-based time structure, and access the current UTC time, respectively.
An 8-byte integer (RPG 20i 0) can support a range of values from -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807. This expanded range of values, when based on the January 1 1970 epoch, then allows for a maximum year value well in excess of the year 290 billion (109), a value I consider to provide sufficient breathing room over the previous Y2K38 limitation (though some of the time APIs encounter additional considerations earlier—for instance, after the year 9999 due to formatting considerations).
Use of these 64-bit APIs, if you're familiar with or already using the previous 32-bit APIs, is quite straightforward. Besides the change in the name of the API, the only other change is in the declaration of the time parameter (or for some APIs, the return value) being used, changing from 10i 0 to 20i 0. The one thing to keep in mind is that most industry APIs, like stat(), continue to define only 32-bit versions of the APIs, so you may need to assign the 32-bit time values to 64-bit values in order to use the 64-bit enabled APIs.
I'm confident that at some point in the future we'll see industry-defined APIs that replace existing APIs such as stat() and that support years beyond 2038. I also suspect that these new definitions may involve more changes than just going to 8-byte integer values for seconds since the 1970 epoch (that is, how the 64-bit ILE C extensions are implemented). Past industry discussions, for instance, have involved keeping track of time in milliseconds or microseconds as opposed to seconds. Increasing the granularity of how time is measured would appear to make sense, given the rather small likelihood of 21st century (or even 25th century) applications needing to support years in excess of year 9999, let alone dates billions of years in the future.
For now, you will want to keep in mind that use of signed 4-byte integer epoch-based time values is going to require some application change prior to 2038. In order to minimize future impacts of Y2K38, you may want to come to a company agreement on how to track epoch-based time usage within your applications and how to implement coding standards in order to minimize future application program source code changes.
In the case of the stat() API, these coding standards might include the use of one central service program to actually call the stat() API, where the service program returns an 8-byte time value for subsequent application program usage. Alternatively, you may elect to replace calls to the stat() API with calls to the Get Attributes API (Qp0lGetAttr) documented here. The Get Attributes API, available since V4R3, returns creation time, last access time, and last data modification time as unsigned 4-byte integer values. Using an unsigned time value that is relative to the 1970 epoch does have the downside that stat()-like dates prior to 1970 are returned as Jan 1 1970 (which should not be a major concern for those systems running with a current year of 2012 as the i and the IFS didn't exist prior to 1970) and on the plus side, when used with the 64-bit enabled time APIs discussed earlier, provides an additional 60+ years of date support beyond Y2K38.