About Dates But Never Dared To Ask, Part 1

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 1

RPGPGM.

COM - From AS400 to IBM i


Advice about programming, operations, communications, and anything else I can think of
This blog is about IBM i for Power

Home About this blog List of all posts IBM i user groups Legal notices Privacy policy Search this site

Tu e s d a y, O c t o b e r 2 0 , 2 0 1 5

Everything you wanted to know about dates but


never dared to ask, part 1
The Date data type was introduced in V2R3, but it was not until
the introduction of RPGLE in V3R1 could we finally make use of
it. Over those years I have used the Date data type extensively
in my RPG programs and DDS files. I am surprised to still find
developers who will not use them, or find others who do not
fully understand the functionality of this data type.

Rather than address each item I have found I thought it would be better to write about the
Date data type and how I can use them in my RPG code. This will be spread out over several
posts, as to cover all I want to do is too much for just one. Let’s start today with what I
consider the basics.

Date formats
The date is stored as YYYYMMDD in Date data type variables, fields, and columns. Date
formats are just the way the date is presented to us, much like an edit mask or edit code.
Some formats can be used when defining Date data types variables, fields, and columns. Then
there are others that are only used to move values in or out of Date data types. Contact Form

These are the formats that are used for defining variables, fields, and columns: Name

Form Layout (with Valid


at default separator Email *
name Description separator) s Range
/ - . , 01/01/(19)40 –
*MDY USA month/day/year MM/DD/YY Message *
& 12/31/(20)39
/ - . , 01/01/(19)40 –
*DMY European day/month/year DD/MM/YY
& 31/12/(20)39

/ - . , (19)40/01/01 –
*YMD Year/month/day YY/MM/DD
& (20)39/12/31
Send
/ - . , (19)40/001 –
*JUL Julian YY/DDD
& (20)39/365
International Standards 0001-01-01 – 9999-12-
*ISO YYYY-MM-DD -
Organization 31
01/01/0001 –
*USA USA standard MM/DD/YYYY /
12/31/9999
01.01.0001 –
*EUR European standard DD.MM.YYYY .
31.12.9999
0001-01-01 – 9999-12-
*JIS Japanese Industrial Standard YYYY-MM-DD -
31

Yes, *ISO and the *JIS are the same.

If you use "&" as a seperator a space is used.

These are the date formats that can only be used when moving value into or out of Date data
types:

Layout (with
Format default Valid Links to useful sites
name Description separator) separators Range

Century/year/month/da / - . , 000/01/01 – 1 Getting started with IBM i


*CYMD CYY/MM/DD
y & 999/12/311
001/01/00 – IBM Documentation for 7.5
Century/month/day/yea / - . , 2
*CMDY CMM/DD/YY
r & 912/31/991
3 IBM Documentation for 7.4
Century/day/month/yea / - . , 001/01/00 –
*CDMY CDD/MM/YY
r & 931/12/991
4 IBM Documentation for 7.3
*LONGJU / - . ,
Long Julian YYYY/DDD 0001/001 – 9999/365
L & IBM Documentation for 7.2
5

1 The range of dates expressed in conventional written format are January 1 1900 - December
6 IBM Documentation for 7.1
31 2899.

I have seen the *CYMD format most commonly used in older databases, designed in the days PowerSystems Redbooks (inc.
7
IBM i)
when the cost of disk was so expensive it was decided to use only one number for the century
to save disk space.

Personally, I think that the *CMMDDYY and *CDDMMYY are ridiculous and I cannot think of a reason Popular Posts: Last
to use them. 30 days

1 Modernize your CL too


Defining dates
2 Getting the SQL statement
I use Date data types in RPG, DDS files, and (SQL) DDL tables. Alas, Date data types are not out of Query/400
supported in IBM i 7.2 or earlier releases of CL. I am sure they can be used in COBOL, but we
Getting information about
don't talk about that programming language in this blog. 3
User Profiles using SQL

In all free RPG defining variables with these different date format could not be simpler as all I Defining variables in RPG all
4
have to do is define the variable as a date followed by the format I want to use: free

Handling variable length


5
01 dcl-s USA_date date(*usa) ; variables in CL

02 dcl-s ISO_date date(*iso) ;


03 dcl-s EUR_date date(*eur) ;
04 dcl-s JIS_date date(*jis) ; Popular Posts: All
Time
05 dcl-s YMD_date date(*ymd) ;
06 dcl-s DMY_date date(*dmy) ;
07 dcl-s MDY_date date(*mdy) ; Defining variables in RPG all
1
free

08 dcl-s JUL_date date(*jul) ; Example subfile program


2
using modern RPG
09 dcl-s Misc_date date ;
Run SQL statements in your
3
CL

For those of you who still have to use fixed format definitions I would define the variable with a Defining Procedures in RPG all
4
"D" in the data type column and the DATFMT in the keyword column: free

5 File definition in RPG all free


DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords++++
D USA_date S D datfmt(*usa) Email IFS files
6
D ISO_date S D datfmt(*iso)
D EUR_date S D datfmt(*eur)
Read, write, and update a file
D JIS_date S D datfmt(*jis) 7
in CL

D YMD_date S D datfmt(*ymd) Creating PDF files from spool


8
files
D DMY_date S D datfmt(*dmy)
D MDY_date S D datfmt(*mdy) 9
Getting information about
User Profiles using SQL

D JUL_date S D datfmt(*jul) Handling variable length


10
variables in CL
D Misc_date S D

But wait, for the date on line 9 I have not given a date format. In this case the default date Labels
format, *ISO, assumed.

I can change the default date format by using the DATFMT keyword in the Control options/H- 1 CL

specs. In the example below the default date format will be *EUR (DD.MM.YYYY).
DSPF
2
In all free RPG:
HISTORY
3
ctl-opt datfmt(*eur) ;
4 IFS

Or in fixed format.
5 LF

HKeywords+++++
6 NEW RELEASE
H datfmt(*eur)

NODE.JS
7
In DDS files, whether a Physical, Display, or Printer file, I define the date in the same way:

8 OPERATIONS

A..........T.Name++++++RLen++TDpB......Functions+++
A FLD_DATE L DATFMT(*USA) 9 PF

PROGRAMMING
To define it as a Date data type field I put "L" in the Data type column. "D" was already taken 10

when the Date data type was introduced, it used in Display files for an alphanumeric fields that
QUERY
can contain only numeric digits. As I am in USA I always use the DATFMT(*USA) so that when 11
the date is displayed in various tools, like Query, it will be shown in *USA format. If I did not
use the DATFMT the date would be displayed in *ISO format. 12 RPG

A date column can be coded in a (SQL) DDL table with the following statement: SHARED
13

CREATE TABLE QTEMP/TESTTABLE SQL


14
(DATE_COLUMN DATE NOT NULL
) USER GROUP
15

I have added the NOT NULL as I do not want the date to be null if it contains no value. 16 VIRTUAL USER GROUP

Moving values in and out of dates


Between dates Blog Archive
Moving values between Date data types is simple, all I have to do is to move the value in one
► 2022 (81)

Date data type variable to another, with one exception. Below is an example of how simple this
► 2021 (90)

is, I am using the Date data type variables I showed earlier:
► 2020 (92)

01 dcl-s USA_date date(*usa) inz(*sys) ; ► 2019 (89)



02 dcl-s EUR_date date(*eur) inz(d'2013-06-23') ; ► 2018 (70)

► 2017 (92)

03 YMD_date = %date() ;
► 2016 (85)

04 ISO_date = d'2015-10-30' ; ▼ 2015 (86)



05 DMY_date = ISO_date ; ► December (6)

06 Misc_date = DMY_date ;
► November (6)

07 JUL_date = Misc_date ;
▼ October (10)

08 USA_date = d'1939-09-03' ; Using SQL to get
information about Job
09 MDY_date = USA_date ;
Scheduled e...

10 clear DMY_date ; IBM i and Open Source


presentation
11 JIS_date = *loval ;
12 ISO_date = *hival ; Everything you wanted to
know about dates, FAQ
Everything you wanted to
Line 1: I am using the special value *SYS to initialize USA_date with the current system date. know about dates but
For more information on how to use these special values see the post Initializing variables with neve...

special values. Everything you wanted to


know about dates but
Line 2: I am initializing EUR_date with the date 2013-06-23. The "d" indicates that this is a neve...
date, followed by the number of the date enclosed in apostrophes ( ' ). Even though I am A better way of sending
moving a date into a *EUR date I have to give the date in *ISO format as I have not given the break messages
date type in the Control options. If I had then I would have to give the date in that format. Determining the last
Friday/first Monday in
Line 3: This is another way to initialize a date with today's date. the month
IBM i 7.2 TR3 / 7.1 TR11
Line 4: ISO_date = 2015-10-30. announcement video

Line 5: DMY_date = 30/10/15. IBM i 7.2 TR3 and 7.1 TR11


is announced
Line 6: As I have not given a date format in the control option Misc_date = 2015-10-30. IBM i 7.2 TR3 and 7.1 TR11
being announced
Line 7: JUL_date = 15/303. tomorrow?

Line 8: USA_date = 09/03/1939. ► September (10)


► August (7)

Line 9: As the date in USA_date is outside the allowed range for a *MDY formatted Date data
type the program errors with the following message: ► July (5)

► June (7)

Message ID . . . . . . : RNQ0114 Severity . . . . . . . : 99 ► May (5)



Message type . . . . . : Inquiry ► April (11)

Message . . . . : The year portion of a Date or Timestamp value is
► March (8)

not in the correct range (C G D F).
► February (5)

► January (6)

Date data types cannot be zero, as it is not a valid date. When I clear DMY_date, line 10, it will
contain the lowest date possible in that format, 01/01/40. Another way I can initialize a date to ► 2014 (94)

its lowest value is shown on line 11, using *LOVAL initializes JIS_date with 0001-01-01. Now
► 2013 (59)

the opposite, if I move *HIVAL to ISO_date I get 9999-12-31, line 12.

Other data types into and out of dates

When moving other data types to Date types I always need to be sure that they contain
something that represents a date. If it does not I am going to get an error. Too many times I
have been told by other developers "Even though it is a number that field always contain a
valid date". Then at 3 AM a program errors as there is a record with "date" value of zero. Make
it your rule to always validate the other data type's value before moving it to a Date data type.
I did a brief post about this called Validating dates in RPGLE, and I hope with a combination of
this post and that one I cover pretty much all aspects of date validation.

We are provided with the TEST(DE) operation for validating any incoming data type is a date. I
need to provide the format I wish to validate the incoming value with, and the variable
containing the "date". In the example below I am validating that NumericDate contains a value
that is compatible with the *USA format, if it does not then the %ERROR is set on:

test(de) *usa NumericDate ;


if (%error) ;

Before I give more detailed examples I need to introduce five new variables to our program:

01 dcl-s A_USA char(8) inz('09201995') ;


02 dcl-s A_YMD char(8) inz('93.01.21') ;
03 dcl-s A_ISO1 char(8) inz ;
04 dcl-s A_ISO2 char(10) inz('19501024') ;
05 dcl-s N_CYMD packed(7) inz(1150930) ;

Or in fixed format:

DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++
01 D A_USA S 8 inz('09201995')
02 D A_YMD S 10 inz('93.01.21')
03 D A_ISO1 S 8 inz
04 D A_ISO2 S 10 inz('19501024')
05 D N_CYMD S 7 0 inz(1150930)

The names of the variables describe the types of "dates" contained within them. Notice that
A_ISO1 is blank, therefore, it is not a valid "date". Below are examples of how I would move
these "dates" into Data date types of different types:

06 test(de) *usa0 A_USA ;


07 if not(%error) ;
08 YMD_date = %date(A_USA:*usa0) ;
09 endif ;

10 test(de) *ymd. A_YMD ;


11 if not(%error) ;
12 MDY_date = %date(A_YMD:*ymd.) ;
13 endif ;

14 test(de) *iso0 A_ISO1 ;


15 if (%error) ;
16 clear ISO_date ;
17 else ;
18 ISO_date = %date(A_ISO1:*iso0) ;
19 endif ;

20 test(de) *iso0 A_ISO2 ;


21 if not(%error) ;
22 JIS_date = %date(A_ISO2:*iso0) ;
23 endif ;

24 test(de) *cymd N_CYMD ;


25 if not(%error) ;
26 USA_date = %date(N_CYMD:*cymd) ;
27 endif ;

On line 6 I am testing if A_USA contains a *USA formatted "date", as this is an alphanumeric


variable I do need to give the separator character used. In this case no separator is used, so I
code a zero ( 0 ), which immediately follows the format (*USA0). As the date is valid the error
indicator is off, line 7. I can move the value in A_USA using the %DATE built in function into
YMD_date. The first parameter is the variable name, the second is the date format for the
value in the variable and if alphanumeric variables you also need to give the separator
character. In this case there is no separator so zero is used denote there is no separator
character. The result is that YMD_date contains 95/09/20.

In the next grouping, lines 10 – 13, I am validating a *YMD formatted date with a period (. ) as
the separator. As the "date" is valid means that MDY_date contains 01/21/93.

Line 14 – 19 is the best example as it shows what you should do if the "date" is invalid or
valid. As this variable is blank it is going to be invalid. The validation on line 14 checks for a
"date" in ISO format with no separator character (*ISO0). As this "date" fails the validation the
error indicator is on, line 15. I have chosen to clear the date variable, line 16. There are times
when I might want to move another "date" in, for example today's date, instead.

In the group lines 20 – 23 I validate A_ISO2 as an alphanumeric ISO date without a date
separator, and then move the value into a *JIS formatted date. The result is that JIS_date
contains 1950-10-24.

When validating and moving numeric variables I do not have to worry about a separator
character. In the example shown in lines 24 – 27 I am using a numeric representation of a
*CYMD formatted date. As I said earlier I cannot define a *CYMD Date data type, but I use this
format to move a value from a variable into a Date data type of another format. On lines 24
and 26 there is no separator character with the date format. The result is that USA_date
contains 09/30/2015.

To move a value from Date data type into other types I use the %CHAR or %DEC depending on
what the result variable is. The examples below take the values in the Date formats above and
move them into other types of variables:

28 A_USA = %char(JIS_date:*usa0) ;
29 A_YMD = %char(USA_date:*ymd&) ;
30 A_ISO1 = %char(MDY_date:*iso0) ;
31 A_ISO2 = %char(JIS_date:*cymd-) ;
32 N_CYMD = %dec(YMD_date:*cymd) ;

Line 28: The value in JIS_date is converted to a character data type using the %CHAR built in
function. The second parameter in the %CHAR is the format I want to output to the variable
A_USA, in this line it is *USA format with no separator. A_USA = '10241950'.

Line 29: The interesting thing about the line is the use of the "&" as the character separator,
this denotes that the separator will be a blank. A_YMD = '15 09 30'.

Line 30: With no date separator A_ISO1 = '19930121'.

Line 31: Using the *CYMD with the date separator character results in A_ISO2 = '090-10-24 '.

Line 32: I am using the %DEC built in function with a second parameter of the format I want the
result to contain. There is no date separator as I am moving this into a numeric field. N_CYMD
= 0950920.

Others in this series include:

Part 2: Calculations using dates, extracting parts of dates


Part 3: FAQ

You can learn more about these BIFs on the IBM website:

RPG built in function %CHAR


RPG built in function %DATE
RPG built in function %DEC
RPG operation code TEST

This article was written for IBM i 7.2, and it should work with earlier releases too.

Written by Simon Hutchinson at 7:00 AM

Labels: programming, rpg

5 comments:

Brian Rusch October 20, 2015 at 11:10 AM

Great post. Just a small correction, the ability to use %DEC with a second parameter to move a
date into a numeric variable is also allowed in 7.1.

Reply

Replies

Simon Hutchinson October 20, 2015 at 2:19 PM

I think that came in one of the more recent TRs. The IBM i I used for testing with
release 7.1 would not allow it.

Bruno October 20, 2015 at 2:58 PM

%dec to convert the date to numeric is usable since version 5.4

Simon Hutchinson October 20, 2015 at 6:44 PM

Correction made.

Thank you both for your input.

Reply

Ted Holt September 21, 2017 at 5:14 PM

I have always assumed (a dangerous practice) that CMDY and CDMY were there for shops that
stored their dates in 6-digit packed decimal fields using MDY or DMY format. Converting
applications to use the high-order nibble, which was not being used for anyting, to store the
year would be minimally disruptive. I can't think of any reason a shop would choose one of
these formats if other formats were feasible. I doubt it was any extra effort for IBM to support
these format.

Reply

To prevent "comment spam" all comments are moderated.


Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If
this is you feel free to use the Contact Form to send me the comment and I will post it for you, please
include the title of the post so I know which one to post the comment to.

Newer Post Home Older Post

Copyright © 2013-2022 Simon Hutchinson. All rights reserved.

View mobile site

Powered by Blogger.

You might also like