I’ve been writing a program which process SMF data which has STCK values for dates of events, and STCK values for durations.
In assembler there is a STCKCONV function which takes a STCK ( or STCKE) and converts this to printable date and time, for example 2020/09/21 09:19:18.719641 .
I wrote some code (at the bottom) to call the assembler routine to do the work. it did not work for 64 bit programs.
I had some inspiration in the middle of the night for a much simpler way of doing it.
Quick digression. There are 8 byte STCK values and 16 Byte STCKE which have an extended time stamp to handle the 2042 problem when a STCK will overflow. A STCKE is the first 9 characters of the STCKE with an extra “overflow” byte at the front.
Simple way just using C – should work in 31 and 64 bit.
There are C routines for processing times. For example gmtime (time) take unix time and returns a structure with pointers to the year, month etc.
The unix time is the time in seconds since 00:00:00 January 1 1970.
So to use the C routines, take a STCK(E) convert it to seconds – and subtract the number of seconds which represents midnight January 1 1970.
The logic takes an 8 byte string, shifts it right by 12 bits to get the microseconds bit into the bottom bit, calculates the number of seconds, and returns it.
typedef unsigned long long * ull; void STCKTM(char * pData, struct timespec * pTimespec) { unsigned long long stck = *(ull) pData; stck = stck/4096; // 4096 for stck to get microseconds // in bottom bit long microseconds = stck%1000000; // save microseconds stck = stck/1000000; // seconds from microseconds stck = stck - 2208988800; // number of seconds to Jan 1 1970 pTimespec -> tv_sec = stck; pTimespec -> tv_nsec = microseconds * 1000; }
You call this with
struct tm * tm2; struct tm * tm2; struct timespec ts; STCKETM((char *) headtimeZCentry, &ts ); tm2= gmtime( &ts.tv_sec ); printf("GMTIME yy:%d mm:%d dd:%d h:%d m:%d s:%d\n", 1900+tm2->tm_year, 1+tm2->tm_mon, tm2->tm_mday, tm2->tm_hour, tm2->tm_min, tm2->tm_sec);
This produces
GMTIME yy:2020 mm:9 dd:21 h:9 m:19 s:18
For STCKE to TM. The logic is nearly identical. The 9 byte string only needs to be shifted 4 bits to align the microseconds to the bottom bit.
void STCKETM(char * pData, struct timespec * pTimespec){ unsigned long long stck = *(ull) pData; stck = stck/16 ; // 4096 for stck 16 fot stcke as already // shifted by definion long microseconds = stck%1000000; stck = stck/1000000; // seconds from microseconds stck = stck - 2208988800; // number of seconds to Jan 1 1970 pTimespec-> tv_sec = stck; pTimespec ->tv_nsec = microseconds * 1000; }
The hard way, using the assembler STCKCONV macro.
I could find no function in C to do the same conversion. I used to have some C code (of about 300 lines of code) which did the tedious calculation of converting from microseconds to days, and then allowing for leap years etc. Instead of rereating this, I’ve written a bit of glue code which allows you to invoke the STCKCONV macro from C.
It works with non XPLINK amode 31 C programs. I failed the challenge of getting it to work with XPLINK, and with 64 bit C programs (which has the extra challenge that parameters are passed in as 64 bit pointers.
In your C program you have
#pragma linkage(STCKEDT,OS)
rc = STCKEDT( stckvalue ,length, output);
Quick digression. There are 8 byte STCK values and 16 Byte STCKE which have an extended time stamp to handle the 2042 problem when a STCK will overflow.
For a STCK value specify STCKEDT(stck,8,output).
For a STCKE value specify STCKEDT(stcke,16,output);
The output is a 27 character string with a trailing null.
The return code is either from STCKCONV routine or 20 if the length is invalid.
The code is below
STCKEDT CSECT STCKEDT AMODE 31 STCKEDT RMODE ANY ******** * R1-> A(STCK) * -> length of STCK 8 or 16 * -> Return buffer STCKEDT2 EDCPRLG DSALEN=DLEN The name appears in CEE traceback LA 15,20 preset the return code - invalid parms USING DSA,13 L 2,0(,1) address of input L 5,4(,1) a(length of STCK) L 5,0(5) the length L 6,8(,1) return area CFI 5,8 Is the passed length of STCK 8? BNE TRYSTCKE STCKCONV STCKVAL=(2), x CONVVAL=BUFFER, x TIMETYPE=DEC, hhmmsst.... x DATETYPE=YYYYMMDD BNZ GOBACK B COMMON TRYSTCKE DS 0H CFI 5,16 is length 16? BNE GOBACK r15 has been set already to error STCKCONV STCKEVAL=(2), x CONVVAL=BUFFER, x TIMETYPE=DEC, hhmmsst.... x DATETYPE=YYYYMMDD BNZ GOBACK B COMMON COMMON DS 0H * the macro produced time, date, so rearrange it to date time MVC DT(4),BUFFER+8 Move the date MVC DT+4(8),BUFFER+0 Move the time * put the ED mask in the output field MVC DATETIME,DTMASK * and convert it from packed numbers to readable string ED DATETIME,DT * returned date time string is 26 + 1 for trailing null MVC 0(27,6),DATETIME+1 +1 because of leading pad char SR 15,15 reset the return code GOBACK DS 0H EDCEPIL &DATEMASK SETC '4021202020612120612121' _dddd/dd/ddd &TIMEMASK SETC '4021207a21207a21204b21202020202040' _dd:dd:dd.dddddd_ DTMASK DC X'&DATEMASK.&TIMEMASK.00' Add trailing null for C * Work area DSA EDCDSAD BUFFER DS 4F Time.time ..date .. work d DT DS 3F Date, time,time DATETIME DS CL28 Leading blank, date time, null DLEN EQU *-DSA END
I complied it with
//S1 EXEC PGM=ASMA90,PARM='DECK,NOOBJECT,LIST(133),XREF(SHORT),GOFF', // REGION=4M //SYSLIB DD DSN=SYS1.MACLIB,DISP=SHR // DD DISP=SHR,DSN=CEE.SCEEMAC //SYSUT1 DD UNIT=SYSDA,SPACE=(CYL,(1,1)) //SYSPUNCH DD DISP=SHR,DSN=COLIN.OBJLIB(STCKEDT) //SYSPRINT DD SYSOUT=* //SYSIN DD * ... /*
and included it in my C program JCL as
//BIND.OBJ DD DISP=SHR,DSN=COLIN.OBJLIB //BIND.SYSIN DD * INCLUDE OBJ(STCKEDT) NAME COLIN(R) //*