• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Date-Handling in CL Procedures

    January 19, 2005 Ted Holt

    The code for this article is available for download.

    A common saying is, “It’s the little things that get you.” The maxim was certainly true recently in one iSeries shop. I may not have all the details exactly right, but it seems nobody was able to pry any information out of the computer. Investigation revealed an unanswered message to QSYSOPR and a queue of jobs eagerly waiting for a chance to run. Apparently, the unanswered message owed its existence to the fact that some human being had entered an invalid date value into a prompt screen.

    My impression is that this problem arose because of a combination of sloppy programming and CL’s inadequate date-handling abilities (mostly the former). This problem could have been avoided. The program that accepted the user’s input could have validated the date. But even that might have not been sufficient. If the user had keyed a valid, but unreasonable, date, there might still have been a problem.

    In the following paragraphs I am going to provide you with some routines that can turn CL into a decent date-handler. As for the sloppy programming, that part’s up to you.

    Minimal CL Date-Handling

    CL has not won any awards for its date-handling ability. Here is a complete list of the CL commands that manage dates.

    1. CVTDAT

    That’s not much of a list, huh? The Convert Date (CVTDAT) command was designed to convert dates from one format to another. Since it chokes on invalid dates, it can also be used to verify that a variable contains a valid date value.

    Here’s a typical example that uses minimal CL to validate dates from a prompt screen. When the user requests a report, the system prompts for a range of dates.

                             Generate Some Report                  1/12/05
                                                                   12:00:00
    
    
     Enter a range of dates. You may leave the ending date blank
     if the report is to be run for only one day.
    
     Beginning date ..............................: ______
     Ending date .................................: ______
    
    
    
    
    
    
    
    
    
    
    
    
     F3=Cancel request    Enter=Generate report
    
    

    Here’s the DDS for the display file. Besides the prompt format, there is a message subfile for error messages.

    Here’s the code for an OPM CL program.

    /* =============================================================== */
    /* Prompt for a range of dates and submit to batch.                */
    /* =============================================================== */
    /* To compile:                                                     */
    /*   CRTCLPGM PGM(xxx/JKL002C) SRCFILE(xxx/QCLSRC) SRCMBR(JKL002C) */
    /* =============================================================== */
    
    pgm
    
       dclf JKL002D
    
       dcl  &WBgnDate  *char   8
       dcl  &WEndDate  *char   8
       dcl  &MsgTxt    *char  78
       dcl  &MsgKey    *char   4
       dcl  &Sender    *char  80
    
       MonMsg     cpf0000    exec(GoTo Error)
    
    Prelims:
    
       /* Determine the name of the program so the display */
       /* file can reference the program message queue. */
       SndPgmMsg  msg('/* */') ToPgmQ(*same) +
                    MsgType(*info) KeyVar(&MsgKey)
       RcvMsg     PgmQ(*same) MsgType(*info) MsgKey(&MsgKey) +
                    Rmv(*yes) Sender(&Sender)
       ChgVar     Var(&PgmNam) Value(%SST(&Sender 27 10))
    
       RmvMsg     Clear(*all)
    
       /* loop until F3 pressed or data is valid */
    
    GetInput:
    
       SndF       RcdFmt(MSGCTL)
       SndRcvF    RcdFmt(FORMAT01)
    
       If (&IN03) Then(Do)
          SndPgmMsg Msg('Request was cancelled.') MsgType(*comp)
          Return
       EndDo
    
       /* Validate the input. Error messages go to the subfile. */
    
       RmvMsg     Clear(*all)
    
       CvtDat     Date(&SBgnDate) ToVar(&WBgnDate) +
                    FromFmt(*job) ToFmt(*yymd) ToSep(*none)
    
       If (&SEndDate *eq ' ') Then(Do)
          ChgVar     Var(&SEndDate)  Value(&SBgnDate)
       EndDo
    
       CvtDat     Date(&SEndDate) ToVar(&WEndDate) +
                    FromFmt(*job) ToFmt(*yymd) ToSep(*none)
    
       If (&WEndDate *lt &WBgnDate) Then(Do)
          SndPgmMsg  MsgID(JKL1001) MsgF(JKLMSG) ToPgmQ(*same)
          GoTo       GetInput
       EndDo
    
    EndInput:
    
       SbmJob Job(SomeJob) +
                Cmd(Call JKL009C (&SBgnDate &SEndDate))
       SndPgmMsg Msg('Your request has been submitted for +
                       processing.') MsgType(*comp)
       Return
    
    Error:
    
       RcvMsg     MSGQ(*PgmQ) MsgType(*excp) MSG(&MsgTxt)
       MonMsg     MsgID(cpf0000)
       SndPgmMsg  MSG(&MsgTxt) ToPgmQ(*same)
       MonMsg     MsgID(cpf0000)
       GoTo       GetInput
    
    EndPgm
    

    For this program to run correctly, you’ll need a message description.

    CrtMsgf MsgF(xxx/JKLMSG)
    AddMsgD MsgID(JKL1001) +
               MsgF(JKLMSG) +
               Msg('Ending date must not be before beginning date.')
    

    That doesn’t look so hard, does it? The CVTDAT commands will generate an escape message, such as CPF05555 (Date not in specified format or date not valid) and CPF0557 (Date too short for specified format) if the user keys an invalid date. The global Monitor Message (MONMSG) command kicks in and branches to the Error routine, which sends the message to the program message queue, from which the display file retrieves and displays it to the user. One would think that any CL programmer could at least do this much to verify the accuracy of a date.

    This example also includes one check for reasonableness: the first date in the range must not be after the second date in the range.

    Once the user has entered valid dates, this program submits another program to batch, passing along the dates as parameters. Since it’s possible that this program could run from somewhere else, such as the job scheduler, the submitted program should not assume that the dates are okay, but should perform validation of its own. If a date is found to be invalid, or possibly unreasonable, the program can send an escape message to cancel itself.

     pgm (&FromDate &ThruDate)
    
      dcl &FromDate *char 6
      dcl &ThruDate *char 6
      dcl &TempDate *char 6
      dcl &Abending *lgl
    
      monmsg cpf0000  exec(goto Abend)
    
      cvtdat date(&fromdate) tovar(&tempdate) tosep(*none)
      cvtdat date(&thrudate) tovar(&tempdate) tosep(*none)
    
      call   somepgm
      return
    
      /* Routine to handle unexpected errors */
     Abend:
      if &Abending then(return)
      chgvar   &Abending '1'
    
      sndpgmmsg  msgid(cpf9898) msgf(qcpfmsg) msgtype(*escape) +
                   msgdta('Request for some report ended abnormally. +
                            See the job log')
     endpgm
    

    Had the programmer, who wrote the CL program about which I spoke in the introduction, included such a check, the job queue would not have filled up with requests.

    Robust Date-Handling

    In many situations a minimal amount of verification may be sufficient, but in other situations you may need something more robust. For example, December 25, 1969, is a valid date, but it may not make sense in some contexts. If you ask a user to enter an ending date for sales history inquiry, does it make sense to allow him to enter some date in the future?

    If you really want robust date validation, you’ll have to do what I’ve done, and come up with something of your own. I wrote an RPG module, CLDATERTNS, of date-handling subprocedures designed with CL in mind. You’re free to use it and enhance it. The following table lists the subprocedures that I have chosen to include so far. Here’s the source code for the CLDATERTNS member. The comments at the beginning will tell you how to create the module.

    Subprocedure

    Arguments

    Return
    value

    Description

    AddDays

    Date,
    number

    Date

    Add
    number of days to a date

    AddMonths

    Date,
    number

    Date

    Add
    number of months to a date

    AddYears

    Date,
    number

    Date

    Add
    number of years to a date

    CurrDate

     

    Date

    Job date

    CurrMonthBegin

     

    Date

    First
    date of the current month

    CurrMonthEnd

     

    Date

    Last date
    of the current month

    DaysDiff

    Date,
    date

    Number

    Number of
    days between first and second dates. Positive number means second date is
    after first date. Negative number means second date is before first date.

    IsNotValidDate

    Date

    Logical

    True if
    date is not valid, false if date is valid

    IsValidDate

    Date

    Logical

    True if
    date is valid, false if date is invalid

    MonthBegin

    Date

    Date

    First
    date of a month

    MonthEnd

    Date

    Date

    Last date
    of a month

    PrevMonthBegin

     

    Date

    First
    date of the previous month

    PrevMonthEnd

     

    Date

    Last date
    of the previous month

    The dates used by these routines are six-byte character values in job date format. Although I use them with *MDY dates, I have briefly tested with other date formats and the routines appear to work correctly. Numbers are five-digit packed decimal values with no decimal positions. Logical values are one-byte each, with 0 and 1 meaning false and true, respectively.

    All routines except IsNotValidDate and IsValidDate send escape message USR2101 if you pass them an invalid date. The AddDays, AddMonths, and AddYears routines send escape message USR2102 if you pass them an invalid packed decimal argument. Here are the commands to create the message file and the messages. Feel free to rename the messages or put them in another message file.

    CrtMsgF MsgF(xxx/USRMSG)
    
    AddMsgD MsgID(USR2101)
            MsgF(xxx/USRMSG)
            Msg('Value ''&1'' is not a valid date.')
            Fmt((*char 6))
    
    AddMsgD MsgID(USR2102)
            MsgF(xxx/USRMSG)
            Msg('Value X''&1'' is not a valid decimal value.')
            Fmt((*char 6))
    

    Let’s take the same application but make it more robust. I’ve made a slight change to the display file; I’ve added attributes to display invalid date fields in reverse image.

    The CL program has changed considerably. The biggest change is that I’ve replaced one OPM CL program with an ILE program built from a CL module and the CLDATERTNS module. As I wrote in “Optional Parameters and CL Procedures,” there are good reasons to dump OPM CL. This application provides yet another reason.

    The revised CL uses the same basic logic, but the CVTDAT commands are gone, replaced with calls to procedures from CLDATERTNS. Rather than let the global MONMSG pick up exceptions, I’ve taken control of the validation process. This lets me check both dates for validity in one pass; whereas the minimal version stops looking for errors when a date proves invalid. Since I’m not passing along the system escape messages, I need a message of my own.

    ADDMSGD    MSGID(JKL1003) MSGF(JKLMSG) +
       MSG('Date &1 is invalid.') +
       Fmt((*CHAR 6))
    

    I’ve added one additional test. I’ve added code to verify that the dates are not more than 60 days in the past or 30 days in the future. The error message for this test requires that message JKL1002 be defined in the JKLMSG message file.

    ADDMSGD    MSGID(JKL1002) MSGF(JKLMSG) +
       MSG('Dates must be between &1 and &2.') +
       Fmt((*CHAR 6) (*CHAR 6))
    

    To create the program requires that I first create the two modules, and then bind them together to form a program. The instructions to create module CLDATERTNS are in the source code. Here are the remaining steps.

    CrtCLMod Module(xxx/JKL003C) 
       SrcFile(xxx/QCLSRC) SrcMbr(JKL003C)
    CrtPgm Pgm(xxx/JKL003)
            Module(JKL003C CLDATERTNS)
    


    For Want of a Valid Date

    I’ve concentrated on working with dates, but the lesson we should learn applies to all types of data. Never assume anything is correct. The old rhyme says that a kingdom was lost for want of a nail. In this case, money may have been lost for want of a valid date. Even if no money was lost, the inconvenience caused to users couldn’t have had positive consequences.

    Combine these date-handling routines with Cletus’ short-cut date entry technique, and you may be on to something. But that’s a subject for another day.

    Click here to contact Ted Holt by e-mail.

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    WorksRight Software

    Do you need area code information?
    Do you need ZIP Code information?
    Do you need ZIP+4 information?
    Do you need city name information?
    Do you need county information?
    Do you need a nearest dealer locator system?

    We can HELP! We have affordable AS/400 software and data to do all of the above. Whether you need a simple city name retrieval system or a sophisticated CASS postal coding system, we have it for you!

    The ZIP/CITY system is based on 5-digit ZIP Codes. You can retrieve city names, state names, county names, area codes, time zones, latitude, longitude, and more just by knowing the ZIP Code. We supply information on all the latest area code changes. A nearest dealer locator function is also included. ZIP/CITY includes software, data, monthly updates, and unlimited support. The cost is $495 per year.

    PER/ZIP4 is a sophisticated CASS certified postal coding system for assigning ZIP Codes, ZIP+4, carrier route, and delivery point codes. PER/ZIP4 also provides county names and FIPS codes. PER/ZIP4 can be used interactively, in batch, and with callable programs. PER/ZIP4 includes software, data, monthly updates, and unlimited support. The cost is $3,900 for the first year, and $1,950 for renewal.

    Just call us and we’ll arrange for 30 days FREE use of either ZIP/CITY or PER/ZIP4.

    WorksRight Software, Inc.
    Phone: 601-856-8337
    Fax: 601-856-9432
    Email: software@worksright.com
    Website: www.worksright.com

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    NGS Provides a Quick ‘Dashboard’ View into Business Performance 2005: A SOX Auditor’s Odyssey

    Leave a Reply Cancel reply

Volume 5, Number 3 -- January 19, 2005
THIS ISSUE
SPONSORED BY:

Advanced Systems Concepts
WorksRight Software
Guild Companies

Table of Contents

  • Date-Handling in CL Procedures
  • Extracting Zoned and Packed Decimal Values from Character Fields
  • Admin Alert: More on Preparing for OS/400 V5R1 to V5R3 Upgrades

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Public Preview For Watson Code Assistant for i Available Soon
  • COMMON Youth Movement Continues at POWERUp 2025
  • IBM Preserves Memory Investments Across Power10 And Power11
  • Eradani Uses AI For New EDI And API Service
  • Picking Apart IBM’s $150 Billion In US Manufacturing And R&D
  • FAX/400 And CICS For i Are Dead. What Will IBM Kill Next?
  • Fresche Overhauls X-Analysis With Web UI, AI Smarts
  • Is It Time To Add The Rust Programming Language To IBM i?
  • Is IBM Going To Raise Prices On Power10 Expert Care?
  • IBM i PTF Guide, Volume 27, Number 20

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle