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 forToString()
ensures the potential leading zero is preserved. - Lines 18-20
-
Use a regular expression to ensure the
SevenDate
variable is in the correct format ofxnnnnnnn
wheren
is either a0
or1
andn
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 either19
or20
. 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.