Interpreting Stream File Timestamps
May 14, 2014 Ted Holt
Fortunately, IBM i has an Integrated File System (IFS), into which you can store any kind of data your heart desires. Fortunately, IBM provides an API that your programs can use to retrieve information about IFS stream files. Unfortunately, this API is rooted in the Unix world. Unfortunately, timestamps are stored in what I would call a bizarre manner. Fortunately, you are intelligent enough to learn how this API works. When you need to know programmatically about a stream file, you can use the stat API. Like many things UNIX, stat is idiosyncratic, especially in the way that it reports the last time a file was accessed, the last time the contents were changed, and the last time the status changed. These times are reported in epoch time. Epoch time is the number of seconds that have elapsed since January 1, 1970, UTC, not counting leap seconds. Since epoch time is represented as a four-byte signed integer, the range of permissible dates is December 13, 1901, through January 19, 2038. The following example of excellence in programming illustrates the process you must go through to find out when a file was last accessed or changed. H dftactgrp(*no) actgrp(*new) H option(*srcstmt: *nodebugio) bnddir('QC2LE') D g_UTC_Offset s 10i 0 D StmfInfo ds qualified inz D Mode 10u 0 D ID 10u 0 D 5u 0 D 5u 0 D UserID 10u 0 D GroupID 10u 0 D Size 10i 0 D AccessTime 10i 0 D ContentChangeTime... D 10i 0 D StatusChangeTime... D 10i 0 D 10u 0 D BlockSize 10u 0 D AllocationSize... D 10u 0 D ObjectType 11a D 1a D CodePage 5u 0 D CCSID 5u 0 D DeivceID 10u 0 D NumberOfLinks... D 10u 0 D DeviceID 20u 0 D FileSystemID 20u 0 D 36a D Generation_ID... D 10u 0 D stat pr 10i 0 extproc('stat') D path * value options(*string) D buffer likeds(StmfInfo) D StmfName s 256a varying D FileStatus s 10i 0 D AccessTime s z D ContentChangeTime... D s z D StatusChangeTime... D s z /free *inlr = *on; Get_UTC_Offset (); // UTC_Offset = -18,000 seconds StmfName = 'cdcatalog.xml'; FileStatus = stat (StmfName: StmfInfo); if FileStatus >= *zero; AccessTime = ToTime (StmfInfo.AccessTime); ContentChangeTime = ToTime (StmfInfo.ContentChangeTime); StatusChangeTime = ToTime (StmfInfo.StatusChangeTime); endif; return; /end-free * ============================================================== P Get_UTC_Offset b D pi D CEEUTCO pr extproc('CEEUTCO') D Hours 10i 0 D Minutes 10i 0 D Seconds 8f D FC 12a options(*omit) D UTC_Hours s 10i 0 D UTC_Minutes s 10i 0 D UTC_Seconds s 8f /free CEEUTCO (UTC_Hours: UTC_Minutes: UTC_Seconds: *omit); g_UTC_Offset = %int(UTC_Seconds); /end-free P e * =============================================================== P ToTime b D pi z D inSeconds 10i 0 value *** locals D Epoch s z inz(z'1970-01-01-00.00.00') /free return Epoch + %seconds(inSeconds) + %seconds(g_UTC_Offset); /end-free P e The program first calls the Get Offset from Universal Time Coordinated to Local Time (CEEUTCO) API to determine the system offset to UTC in seconds. D g_UTC_Offset s 10i 0 P Get_UTC_Offset b D pi D CEEUTCO pr extproc('CEEUTCO') D Hours 10i 0 D Minutes 10i 0 D Seconds 8f D FC 12a options(*omit) D UTC_Hours s 10i 0 D UTC_Minutes s 10i 0 D UTC_Seconds s 8f /free CEEUTCO (UTC_Hours: UTC_Minutes: UTC_Seconds: *omit); g_UTC_Offset = %int(UTC_Seconds); /end-free P e CEEUTCO loads three parameters:
I ignore a fourth, omissible parameter. I also ignore the first two parameters. I only use the third one. Since my shop is in the Central Time Zone, which is five hours behind UTC, CEEUTCO loads -18000 into the third parameter. The program copies this value into global variable g_UTC_Offset. As a rule I avoid global variables, but global variables are good for unchangeable values such as this one. Next the program runs the stat API to retrieve information about a stream file. D StmfInfo ds qualified inz D Mode 10u 0 D ID 10u 0 D 5u 0 D 5u 0 D UserID 10u 0 D GroupID 10u 0 D Size 10i 0 D AccessTime 10i 0 D ContentChangeTime... D 10i 0 D StatusChangeTime... D 10i 0 D 10u 0 D BlockSize 10u 0 D AllocationSize... D 10u 0 D ObjectType 11a D 1a D CodePage 5u 0 D CCSID 5u 0 D DeivceID 10u 0 D NumberOfLinks... D 10u 0 D DeviceID 20u 0 D FileSystemID 20u 0 D 36a D Generation_ID... D 10u 0 D stat pr 10i 0 extproc('stat') D path * value options(*string) D buffer likeds(StmfInfo) D StmfName s 256a varying D FileStatus s 10i 0 /free StmfName = 'cdcatalog.xml'; FileStatus = stat (StmfName: StmfInfo); Now the program must convert the three timestamps from epoch format to the timestamp data type. AccessTime = ToTime (StmfInfo.AccessTime); P ToTime b D pi z D inSeconds 10i 0 value *** locals D Epoch s z inz(z'1970-01-01-00.00.00') /free return Epoch + %seconds(inSeconds) + %seconds(g_UTC_Offset); /end-free P e This table shows the timestamp values for the example file.
It may seem a bit odd to store timestamps this way, but if every little factory in Mississippi had been running Unix machines instead of System/34s in the 1980s, I would undoubtedly be very familiar with epoch time and consider it normal.
|