How do I format a STCK from a C program?

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)
//*

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s