Although the IBM i has had a date data type for a long time, many IBM i shops still use a numeric field to store dates in data files. Many use an eight-digit scheme (in the format yyyymmdd) that's pretty easy to work with in AVR for .NET. However, many others use a seven-digit scheme (in the format cyymmdd) where the first digit designates the century.

The seven-digit date scheme is heavily used by JBA financial applications and other 1990's era ERP apps. This date data type is pretty substantially constrained because it can only render dates with years that start with either 19 or 20. If you need to work dates from before the 20th century or after the 21st century, the seven-digit date isn't for you. This date type seems pretty silly today, but back in its day it was borderline revolutionary. After all, compared to the widely used mmddyy, six-digit date storage from back in the day it doubled the centuries your dates could represent!

Constrained or not, the seven-digit date rears its ugly head occasionally with AVR programmers. It provides a minor challenge because .NET doesn't provide built-in help with that conversion. This article provides two AVR functions to convert a seven-digit numeric date to and from a System.DataTime data type.

This article isn't suggesting that you convert your data files to move away from the seven-digit date (although, if you do there may be prudent reasons for doing so). Rather, this article provides functions you can use to, after having read seven-digit dates from storage, convert those dates to and from a System.DataTime type in your programs.

The seven-digit storage scheme

The seven-digit date is a numeric type stored in this format:

cyymmdd

where

If c >= 1 if the century is 2000
If c = 0 if the century is 1900
yy = last two digits of the year
mm = leading zero month number
dd = leading zero day number

This article provides two routines:

  • SevenDateToDate. Converts a seven-digit numeric date to a System.DateTime date.
  • DateToSevenDate. Converts a System.DateTime date to a seven-digit numeric date.

Converting from the seven-digit date to a System.DateTime date

This function calculates the correct four-digit year and then returns a new System.DateTime value. A narrative of some of the important lines follows the code:

BegFunc SevenDateToDate Type(*Date) Access(*Public) Shared(*Yes)
    DclSrParm SourceDate Type(*Packed) Len(7, 0)   

    // Convert a seven-digit numeric date (ala JBA) in the format cyymmdd  
    // to a DateTime data type where if c >= 1 then the century is 2000,  
    // otherwise if c = 0 then the century is 1900.  
    // An exception is thrown if the date doesn't convert successfully.

    DclFld c          Type(*String)  
    DclFld yy         Type(*Integer4)  
    DclFld yyyy       Type(*Integer4)
    DclFld mm         Type(*Integer4)  
    DclFld dd         Type(*Integer4)  
    DclFld SevenDate  Type(*String)   

    SevenDate = SourceDate.ToString("0000000") // Get seven-digit string.

    If (NOT RegEx.Match(SevenDate, "^(0|1)\d{6}$").Success)
        Throw *New System.ArgumentOutOfRangeException("Date must be seven digits long and start with 0 or 1.") 
    EndIf 

    c  = SevenDate.SubString(0, 1)  // Get century.
    yy = SevenDate.SubString(1, 2)  // Get last two digits of year.
    mm = SevenDate.SubString(3, 2)  // Get the month
    dd = SevenDate.SubString(5, 2)  // Get the day. 

    yyyy = (c="1") ? 2000  : 1900
    yyyy += yy

    LeaveSr *New DateTime(yyyy, mm, dd)  
EndSr    
Line 16
Convert the incoming seven-digit numeric date to a string. The "0000000" mask for ToString() ensures the potential leading zero is preserved.
 
Lines 18-20

Use a regular expression to ensure the SevenDate variable is in the correct format of xnnnnnnn where n is either a 0 or 1 and n is a numeric digit. The regular expression has these four parts:

RegEx Description
^ Anchor the pattern at the beginning of the tested input
(0|1) The first digit must be 0 or 1
\d{7} Followed by six numeric digits
$ Anchor the pattern at the end of the tested input
Lines 22-25
Fetch date parts.
 
Line 27

This is AVR's relatively new conditional expression syntax. It performs an inline if/else test. The general syntax of a conditional expression is:

var = (Boolean test) ? True value : False value

In line 30 above, the variable yyyy is assigned 2000 if c equals 1 or yyyy is assigned 1900 otherwise. You can read more about AVR's conditional expressions in slides 8-23 in this ASNApalooza presentation.

Line 30
With the four-digit year calculated a new System.DateTime is returned.

Converting from a DateTime date to seven-digit date

This function returns a seven-digit numeric date from a given System.DateTime value. A narrative of some of the important lines follows the code:

BegFunc DateToSevenDate Type( *Integer4 ) Access(*Public) Shared(*Yes)
    DclSrParm DateIn Type( *Date )   

    // Convert a DateTime data type to a JBA numeric date in the format cyymmdd  
    // Where if c >= 1 then the century is 2000, otherwise 
    // if c = 0 then the century is 1900.  

    DclFld c                 Type(*Integer4)  
    DclFld yy                Type(*Integer4)  
    DclFld mm                Type(*Integer4)  
    DclFld dd                Type(*Integer4)   
    DclFld leadingYearDigits Type(*String)        

    LeadingYearDigits = DateIn.Year.ToString().Substring(0,2)        
    If (LeadingYearDigits <> "19" and LeadingYearDigits <> "20")
        Throw *New System.ArgumentOutOfRangeException("The date's year must either be 19 or 20.") 
    EndIf

    yy = DateIn.ToString("yy") // Get the last two digits of the year.         
    mm = DateIn.Month // Get the month.         
    dd = DateIn.Day // Get the day.     
    c = (DateIn.Year = 2000) ? 1 : 0 

    LeaveSr c.ToString() + yy.ToString("00") +  mm.ToString("00") + dd.ToString("00")   
EndFunc
Lines 14-17
Ensure that the first two digits of the year of the System.DateTime argument are either 19 or 20. If not, an exception is thrown.
 
Lines 19-22
Get the two-digit date values for year, month, and day from the incoming date and calculate the century value.
 
Line 24
Return the concatenated date values as the seven-digit value needed. Although this line concatenates a string from those values, AVR coerces this as the correct *Integer4 return type at runtime.

Example usage

Call the two functions like this:

DclFld SevenDate         Type(*Integer4)   
DclFld RealDate          Type(*Date) 

SevenDate = 1030630
RealDate = ASNA.Helpers.SevenDateToDate(SevenDate)
// RealDate value is '2003-06-30'

RealDate = D'2003-06-30'
SevenDate = DateToSevenDate(RealDate)
// SevenDate is 1030630

If you aren't globally checking for exception errors, you should probably do this:

RealDate = D'2003-06-30'
Try
    SevenDate = SevenDateToDate(RealDate)
Catch e Type(ArgumentOutOfRangeException)
    // Handle exception here.
Catch e Type(Exception)
    // Handle exception here. 
EndTry 

If you want to add a layer of sophistication to it all, wrap these two functions as static members of a utility class.