Testing a date in RPGLE (RPG IV). TEST(D) opcode

Do you know that the heading itself of this article is wrong. How come? This is because if you say ‘Date’ you mean something which already is of ‘Date’ data type and you never test something which you know that outcome will always be positive. So, what do we mean here by ‘testing a date’? Well, testing a date means testing a numeric or character field which is supposed to store a valid date in some specific format, in business sense. Be warned that in this article I have to write 'Date' when I actually mean a numeric or character field for date. Also, I have repeated this point several times to mark a clear distinction between a date and corresponding numeric/character field.


Why do we need to store date in numeric and/or character fields?
Two reasons are there. Firstly, old AS400 applications are notorious to store dates in different human readable formats. Somewhere you find date stored as a 6 digit numeric field. Somewhere 7 digits, somewhere 8 digits and in still some other cases dates are stored as 6-10 character long fields with or without separators as per programmer’s convenience!

Since most of the applications on AS400 systems still use the same old method to store dates instead of Date type variables, We need to test whether a date (Actually a numeric or character field) is valid or not.

Second reason, and the most important one, to test a date arises when we have to take input from users. In this situation, irrespective of the destiny of such input value (i.e. whether it is to be stored as numeric, character or date –data type); we must validate it for correctness.

Now that we know ‘WHY’, Let us learn ‘HOW’.

How do validate whether a numeric or character field contains valid date or not?
To test whether a date is valid or not, we need to know,

  1. The date format – Once a date is not a date*, we recognize it by its format. This seems logical too. This is why we convert a date data type into numeric or character fields.
  2. The date itself - The numeric or character value pretending to be a date)
  3. The opcode TEST(D - The opcode TEST(D) is one of several TEST opcodes. This specific opcode tests a numeric or character value aspiring to become a DATE some day(At some statement)

What does it require?
Ans. Do I still need to write here?

The syntax of TEST(D) is as below

DateFormat               TEST(D)                             Numeric_Or_Character_Date

How do we know whether it was a valid or invalid date?
Fact is that, the above syntax is incomplete. Test(D) opcode requires at least one of the followings

  1. An indicator value at low position. RPG style. Indicator is set on if date is invalid.
  2. The error extender E. Modern RPG IV style. The built in function %Error return ‘1’ or *On if date is invalid. Seems logical also, that date is invalid hence error indicator is set on.

What are valid date formats?
Following date formats are valid and recognized by the opcode TEST(D).
  1. *ISO – Default date format. If we do not specify the date format in factor 1, The 'numeric_or_character_date' is checked for *ISO date format.**
  2. *USA (MMDDYYYY)
  3. *MDY(MMDDYY)
  4. *DMY(DDMMYY)
  5. *YMD(YYMMDD)
  6. *CYMD(CYYMMDD)

    Above six formats validate a date with separator. i.e. the date fields must have a separator ‘/’ (As in 31/12/39) if the factor 2 is character type.

  7. *ISO0
  8. *USA0
  9. *CYMD0
  10. *DMY0
  11. *YMD0
  12. *CYMD0

Apart from above mentioned most widely used date formats *LONGJUL, though not so commonly used, is also supported.


Enough concepts, lets be a little practical now. What should we do to validate a user input which is supposed to be stored in database as date. The input is taken in *MDY format.

Scenario 1 – When the screen field is of 6 length numeric type. Keep this field as factor 2 and specify its format as *MDY.

Scenario 2 – When screen field is of 8 character type. You want to give user the flexibility to enter ‘/’ also. (Though it’s possible by applying some suitable edit code in DDS itself with 6 length numeric field, However, for the sake of completeness... you know.)

Test the date first for *MDY and if error occurs, test it for *MDY0. This is because, if we do not specify explicitly that the date does not have any separator, system will always search for it, if length of the character field is greater than possible (6 in this case).

So, in this case something like this should happen

*MDY                        Test(DE)                               W@ScrnFld
                            If             %Error
*MDY0                       Test(DE)                               W@ScrnFld
                            EndIf
                            If             %Error
                            Confirmed invalid date
 ...

One last we should keep in our mind which declaring variables to store acting dates.

  • Portion of a Numeric variable from left which exceeds the required length for calculation, is ignored by program.
  • Portion of a Character variable from right which exceeds the required length for calculation, is ignored by program.

For this reason, you should be as specific as possible while declaring a variable which is supposed to store a date value.

For this reason, in the example above, the following values will be validated to be correct.
121212// or 121212AB!. i.e. no error will be reported for these invalid date fields!

I am leaving now with an example below which can be used to test a date in any format. However, remember that due to above build in intrinsic fault(?) of TEST(D) opcode, some invalid values may also test to be containing valid dates.


Example to test a date in RPGLE
This example takes a supposedly date value and its format and tests whether it actually contains a valid date field.

DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++
D W@Date          S             10                   
D W@Format        S              6                   
                                                     
CL0N01Factor1+++++++Opcode&ExtFactor2+++++++Result+++
C     *Entry        PList                            
C                   Parm                    W@Date   
C                   Parm                    W@Format 
 *                                                   
C                   Select                           
C                   When      W@Format = '*USA'      
C     *USA          Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*USA0'     
C     *USA0         Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*CYMD'     
C     *CYMD         Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*CYMD0'    
C     *CYMD0        Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*YMD'      
C     *YMD          Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*YMD0'     
C     *YMD0         Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*DMY'      
C     *DMY          Test(DE)                W@Date   
 *                                                   
C                   When      W@Format = '*DMY0'     
C     *DMY0         Test(DE)                W@Date  
C                   EndSl                           
 *                                                  
C                   If        %Error                
C     'Invalid'     Dsply                           
 *                                                  
C                   Else                            
 *                                                  
C     'Valid'       Dsply                           
C                   EndIf                           
 *                                                  
C                   Return                          

Warning:- Keep length of your date variable as precise as possible for your date format, lest you should invite incorrect values in the database or frequent program fails.

Sample results:-
Input parameters '02/05/2008' '*USA'
Output Valid
Input parameters '02052008' '*USA'
Output Invalid
Input parameters '02052008' '*USA0'
Output Valid
Input parameters '02052008AB' '*USA0'
Output Valid


End Point:-
All testing is over without specifying the length of the factor2. As you could have easily guessed by now, There’s a provision for minimum length which depends on the date format. So, for a numeric field of *MDY format minimum length of the factor2 variable should be 6, for *CYMD 7 and so on. Similarly for a character field add two spaces for separators.

* Date is not a date! Confusing? Well, here I mean that when date has lost its data type and is being used as numeric or character fields.
** If we have changed the default date format from *ISO to something else in H Spec say *CYMD. The numeric or character field will be checked for *CYMD date format only if factor 1 is empty.

Any comments? Feedback?