Search code examples
daterpgle

Editing portions of DATE field to compose a date value


When using RPGLE's %date() function I can "converte" a string displaying a date like '2019-01-01' via %date('2019-01-01':*ISO) or '20190202' via %date('20190202':*ISO0) into a date field.

dcl-s dateISO date(*ISO);

dateISO = %date('2019-01-01':*ISO);
dsply dateISO;
dateISO = %date('20190202':*ISO0);
dsply dateISO;           

On my current database date values are split up into 3 (sometimes 4) different fields rather than stored in fields defined as 'date': On for the day part, one for the month and 1 or 2 (depends on the age of the database file) fields for either the year as 4 digit or one field for the 2 digit century and one for the 2 digit year (2019 -> stored as '2019' or '20' and '19')

In my beginning, I leard to group the 3 zoned date fields with a data structure and use the overlaying "date-subfield" for my DDS (DSPF/PRTF) or "working with dates". I "discovered" for myself the beauty of DATE fields and that I can use the %date() BIF to convert the data structure subfield, which represents the full date, to my needed date field. But for reasons, I don't want to create a new data structure for each "new" date.

So like using %subdt() for extracting portions of a DATE or TIMESTAMP I hoped that I can edit a portion of a DATE field.

Does anyone know how to do it?

Edit:

To all those who thougt I want to GET a portion of a date: NO, I just want to EDIT (as written in the headline) a portion of the date. Like I have a DDS PF definition like this:

 A          R TESTPFR
 A
 A            T1NUMB         6S 0       TEXT('Some number')
 A            T1TEXT        10A         TEXT('Some text')
 A            T1DTDD         2S 0       TEXT('Day part')
 A            T1DTMM         2S 0       TEXT('Month part')
 A            T1DTYY         4S 0       TEXT('Year part') 

My "old" way was like this:

**free
ctl-opt decedit('0,') datedit(*dmy) dftactgrp(*no) option(*nodebugio:*srcstmt:*nounref);

dcl-f testpf disk; // Database File

dcl-ds dateDS qualified; // DS for "converting"
  date zoned(8) pos(1);
  day zoned(2) pos(1);
  month zoned(2) pos(3);
  year zoned(4) pos(5);
end-ds;
dcl-s dateISO date(*ISO); // Destination Date field
dcl-ds testpfrDS likerec(testpfr); // Record definition if the database file


read testpfr testpfrDS;
dow (not %eof(testpf));
  dateDS.day = testpfrDS.t1dtdd ; //t1dtdd is the day field
  dateDs.month = testpfrDS.t1dtmm; //t1dtdd is the month field
  dateDs.year = testpfrDS.t1dtyy; //t1dtdd is the year field

  dateISO = %date(dateDS.date:*EUR);
  dsply dateISO; // Work with the date

  read testpfr testpfrDS;
enddo;

*inlr = *on; 

This ment that I had to define a data structure in each new program, put the PF fields into the subfields and convert the overlaying subfield. In this case this was not that much overhead. But when having DSPF involved where the use input a range of date, I had to write a DS for the FROM date, the TO date and the date from the database. Ok I admit that I could make it like above, just make one DS and use this for the three dates. But my hope was that I can just define a date field and code someting like this:

%subdt(dateISO:*day) = testpfrDS.t1dtdd;
%subdt(dateISO:*month) = testpfrDS.t1dtmm;
%subdt(dateISO:*year) = testpfrDS.t1dtyy; 

So I don't have to built a DS for the date conversion every time.

But as @jmarkmurphy answered, I now came up with the solution of having a DS with the three/four date subfields and one overlaying subfield which is defined as DATE, so I dnon't have to use %date()


Solution

  • There is no function that allows you to edit a portion of a date, time or timestamp field. But as you said, you can do so with a data structure. Given that you don't want to create multiple data structures just for the purpose of setting the day value of multiple date fields, you could use a single Based data structure like this:

    dcl-ds DateIso    Qualified Based(%pDateIso);
      year            Signed(4:0) Pos(1);
      month           Signed(2:0) Pos(6);
      day             Signed(2:0) Pos(9);
    end-ds;
    dcl-s pDateIso    Pointer;
    
    dcl-s startDate   Date(*iso);
    dcl-s endDate     Date(*iso);
    
    pDateIso = %addr(startDate);
    DateIso.day = 1;
    
    pDateIso = %addr(endDate);
    pDateIso.month = 1;
    

    You could also leverage RPGIV to create a sub-procedure that does what you want.

    dcl-proc SetDateDay;
      dcl-pi *n Date(*iso);
        date    Date const;
        day     Int(5) const;
      end-pi
    
      dcl-ds *n;
        dateIso       Date(*iso);
        dateday       Signed(2:0) Pos(9);
      end-ds;
    
      dateIso = date;
      dateday = day;
      return dateIso;
    end-proc;