23
Tue, Apr
0 New Articles

Date Formatting

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times
Throughout my years in this industry, one thing that has continuously eluded me was an easy way to format a date field into words. For example, how do you translate 2002-01-12 (in ISO format) into Saturday, January 12, 2002?

I have written and reviewed at least a dozen routines to accomplish this type of transformation. Many of them used compile-time tables and translation tables to convert 02 to February and tricky algorithms that would some how figure out that 2002-01-12 is a Saturday.

Since RPG IV supports procedures and it is not 1976 anymore, there should be easier methods to translate a date or time into words.

I've created a procedure, GetDateString(), that greatly simplifies converting date and time values into words. GetDateString() accepts a date value in any valid date format and translates it into the day of the week and month as words, followed by the day and year as numerals. Optionally, you can specify a format for the returned value. I've used this routine extensively on reports and form-based output, and I've found that it comes in extremely handy in CGI RPG IV programming. Here is the syntax for the GetDateString procedure:

 

charVar  = GetDateString( date-value [ : string-format ] ) 


The return value is a character-formatted version of the date specified on the first parameter. The format is based on the format codes specified on the second parameter.

The first parameter is required and must be a valid date value. The date value is normally specified as a date field. Since the date-value parameter is a CONST parameter, a date value in any format can be specified.

The second parameter is optional, and if specified, must contain valid formatting codes for the return value. The formatting codes are the key to creating the return string.

The formatting pattern is the same formatting pattern supported by the strftime() function, which is a C language runtime function. Why is it the same as strftime()? Because strftime() is called by GetDateString() to do the difficult work of converting a date value to words.

If unspecified, the string-format parameter of GetDateString() defaults to '%A, %B, %d, %Y'. This default value is a format that allows a date to be returned in Day, Month, Day, Year format. For example, 2002-01-12 is returned as "Saturday, January 12, 2002." Other formatting codes are supported; the complete set of formatting codes and their meanings is listed in Figure 1.

Note that format codes are case-sensitive. For example, a lowercase %a returns the day as "Tues" whereas the uppercase %A returns "Tuesday." Formatting codes are similar in functionality to the substitution values in OS/400 messages or what is used in user-defined options in PDM. See Figure 1 for details.

Format Code
Description
%a
Abbreviated weekday name (e.g., Mon, Tues, Wed)
%A
Full weekday name (e.g., Sunday, Monday, Tuesday)
%b
Abbreviated month name (e.g., Jan, Feb, Mar, Apr)
%B
Full month name
%c
Date and time representation appropriate for locale
%d
Day of month as decimal number (01 - 31)
%H
Hour in 24-hour format (00 - 23)
%I
Hour in 12-hour format (01 - 12)
%j
Day of year as decimal number (001 - 366)
%m
Month as decimal number (01 - 12)
%M
Minute as decimal number (00 - 59)
%p
Current locale's A.M./P.M. indicator for 12-hour clock
%S
Second as decimal number (00 - 59)
%U
Week of year as decimal number, with Sunday as first day of week (00 - 53)
%w
Weekday as decimal number (0 - 6; Sunday is 0)
%W
Week of year as decimal number, with Monday as first day of week (00 - 53)
%x
Date representation for current locale
%X
Time representation for current locale
%y
Year without century, as decimal number (00 - 99)
%Y
Year with century, as decimal number
%z, %Z
Time-zone name or abbreviation; no characters if time zone is unknown
%%
Percent sign

Figure 1: Date/Time Formatting Codes


Using C runtime functions is getting very popular. Essentially, any C language runtime can be used in an RPG IV program by doing two things.

  1. Include the QC2LE binding directory on the BNDDIR parameter of the CRTBNDRPG or CRTPGM commands, or in the Header specification of the source code as BNDDIR('QC2LE').
  2. Prototype the function using RPG IV Definition specification.


Including the BNDDIR('QC2LE') statement on the Header specification is pretty standard, and it simplifies the compile process by allowing you to forget to specify it at compile time. Believe me: Long, frustrating hours of recompiling can be avoided by simply including that statement on your Header specifications.

Prototyping the C runtime functions is getting to be a widespread practice in RPG IV. I use a standard /COPY member when prototyping C runtime functions. However, for the strftime() function, I needed some additional declarations, so I created the following source member, which must be /COPY'd into source that calls the strftime() function. I'm not going to get into the details of each line of code in this /COPY member; we'll cover that in a future issue. For now, the /COPY we need for the GetDateString() procedure is as shown in Figure 2:

      ** Source member: RPGTIME
0110 D int2            S              5I 0
0120 D int4            S             10I 0
0130 D ptr             S               *

0150 D shortint        S                   Like(int2)
0160 D int             S                   Like(int4)
0170 D longint         S                   Like(int4)
0180 D size_t          S                   Like(int4)

     ** The C language TM structure contains information
     ** about a time value. Since a point to this structure
     ** is typically what is being manipulated, the
     ** Data structure TM is based on the pTM pointer.
0190 D pTM             S                   Like(ptr)
0200 D tm              DS                  based(pTM)
0210 D  tm_sec                             Like(int4)
0220 D  tm_min                             Like(int4)
0230 D  tm_hour                            Like(int4)
0240 D  tm_mday                            Like(int4)
0250 D  tm_mon                             Like(int4)
0260 D  tm_year                            Like(int4)
0270 D  tm_wday                            Like(int4)
0280 D  tm_yday                            Like(int4)
0290 D  tm_isdst                           Like(int4)


     **  STRFTIME converts a TM structure to text, based-on
     **  the formatting characters specified in the 3rd parameter
0320 D strFTime        PR                  Extproc('strftime')  like(int)
0330 D  szTarget                       *   value
0340 D  nTargetLen                         value  Like(size_t)
0350 D  szFormat                       *   value
0360 D  struct_tm                      *   value

Figure 2: /COPY RPGTIME Source Member


The procedure GetDateString() is illustrated in Figure 3. The first parameter is a date value. As mentioned earlier, any date value may be specified. The CONST keyword gives us this capability.

The second parameter may be a quoted character string, a literal value, or a field that contains a valid date-formatting string. Refer back to Figure 1 for a list of the valid formatting codes. I've limited this string to an arbitrary length of 64 characters. It is unlikely that you'll need a longer value, but if you do, simply increase the length of the second parameter and the MYFORMAT local variable.

     ** Source member: RPGTIME
      /IF       NOT DEFINED(RPG_TIME)
      /DEFINE   RPG_TIME
     D GetDateString   PR           256A   VARYING
     D  dts                            Z   Const 
     D  dtFmt                       256A   Const VARYING
 
     D GetDay          PR            10I 0
     D  InputDate                      D   CONST DATFMT(*ISO)
      /ENDIF

Extra Include File

 
     ** Source member: TIMEDS
      /IF       NOT DEFINED(TIME_DS)
      /DEFINE   TIME_DS
     D int4            S             10I 0

      ** The C language TM structure contains information

      ** about a time value. Since a point to this struct this structure

      ** is typically what is being manipulated, the

      ** Data structure TM is based on the pTM pointer.

     D tm              DS
     D  tm_sec                             Like(int4)
     D  tm_min                             Like(int4)
     D  tm_hour                            Like(int4)
     D  tm_mday                            Like(int4)
     D  tm_mon                             Like(int4)
     D  tm_year                            Like(int4)
     D  tm_wday                            Like(int4)
     D  tm_yday                            Like(int4)
     D  tm_isdst                           Like(int4)
 
     D size_t          S                   Like(int4)
 
     D time_t          DS
     D  tm_seconds                         Like(int4)
 
     D time            PR            10I 0 Extproc('time')
     D  time_t                             Like(time_t)
 
     D strFTime        PR            10I 0 Extproc('strftime')
     D  szTarget                    255A   options(*VARSIZE)
     D  nTargetLen                         like(size_t)     value
     D  szFormat                       *   options(*string) value
     D  struct_tm                          like(tm)
      /ENDIF
 

Source for Procedures

     H NOMAIN BNDDIR('QC2LE')
      
     D/COPY QRPGLESRC,RPGTIME
     D/COPY QRPGLESRC,TIMEDS
     ***************************************************************************
     ** G E T  D A T E as  S T R I N G -  Procedure
     ***************************************************************************
     P GetDateString   B                   Export
     D GetDateString   PI            64A   VARYING
     D  dts                            Z   Const 
     D  dtFmt                       256A   Const VARYING
     D
     D inDate          S               D   DATFMT(*ISO)
     D nLen            S             10I 0
     D baseYear        S               D   DATFMT(*ISO)
     D dftFmt          C                   Const('%A, %B %d, %Y')
     D myFormat        S            256A
     D myTime          S                   Like(time_T)
     D myString        S             50A
     D myTM            S                   Like(TM)
     C                   if        %Parms >= 2
     C                   Eval      myFormat = dtFmt
     C                   else
     C                   Eval      myFormat = dftFmt
     C                   endif
     C                   
     C                   Move      *ALLX'00'     tm
     C                   Extrct    dts:*seconds  tm_sec
     C                   Extrct    dts:*minutes  tm_min
     C                   Extrct    dts:*hours    tm_hour
     C                   Extrct    dts:*Days     tm_mday
     C                   Extrct    dts:*Months   tm_mon
     C                   Sub       1             tm_mon
     C                   Eval      baseYear = D'1900-01-01'
     C     dts           SubDur    baseYear      tm_year:*years
      ** Retrieve the date from the input TimeStamp   
      ** We have to use MOVE since EVAL doesn't do this. :(
     C                   MOVE      dts           inDate
      ** GetDay returns the day of the week as a ones-based value
      ** we need 0-based, so subtract 1.
     C                   Eval      tm_wday = GetDay(inDate)-1
     C                   Clear                   tm_isdst
     C                   Eval      myTm = tm
 
     C                   CallP      strFTime(myString : %size( myString ) :
     C                                        myFormat : myTm)
 
     **   Strip off the null terminated string characters from the
     **   returned value.
     C     X'0040'       checkr    myString      nLen
 
     C                   return    %subst(myString:1:nLen)
     PGetDateString    E
 
     P GetDay          B                   EXPORT
      ***************************************************************************
      ** G E T D A Y  (Get day of week)
      **   Returns 1 through 7 for the day of the week.
      ***************************************************************************
     D GetDay          PI            10I 0
     D  InputDate                      D   CONST DATFMT(*ISO)
     ** Base date is based on calendar changed date
     D BaseDate        S               D   Static INZ(D'1582-10-14') 
     D nDayOfWeek      S             10I
     D nDays           S             10I 0 
     C                   TEST(E)                 InputDate  
     C                   If        %ERROR
     C                   Return    -1
     C                   Endif
     C     InputDate     SubDur    BaseDate      nDays:*DAYS 
 
     ** Passing the number of days since Oct 14, 1582 to CEEDYWK
     ** gives us back the day of the week.
     C                   CALLB     'CEEDYWK'
     C                   Parm                    nDays
     C                   Parm                    nDayofWeek
 
     C                   return    nDayOfWeek 
     PGetDay           E 

Figure 3: GetDateString Procedure


As mentioned, the default formatting string is '%A, %B %d, %Y'. To change the default, simply modify the DFTFORMAT local variable in the GetDateString() procedure.

Note that in this procedure, I use three different LIKE keywords. The reference fields being specified are all declared in the RPGTIME source member (Figure 2), as is the prototype for the strftime() function. Also note that I've located the /COPY statement inside the GetDateString() procedure itself. This causes the fields declared in the /COPY member to be declared as local variables. So there are not name collisions that occur.

Next issue, a report from the COMMON IT Education Conference in Nashville, Tennessee! I hope to see you there.

BOB COZZI

Bob Cozzi is a programmer/consultant, writer/author, and software developer. His popular RPG xTools add-on subprocedure library for RPG IV is fast becoming a standard with RPG developers. His book The Modern RPG Language has been the most widely used RPG programming book for more than a decade. He, along with others, speaks at and produces the highly popular RPG World conference for RPG programmers.


MC Press books written by Robert Cozzi available now on the MC Press Bookstore.

RPG TnT RPG TnT
Get this jam-packed resource of quick, easy-to-implement RPG tips!
List Price $65.00

Now On Sale

The Modern RPG IV Language The Modern RPG IV Language
Cozzi on everything RPG! What more could you want?
List Price $99.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: