This
chapter shows you how you can use DATE, TIME, and DATETIME fields
in assignments, ACCEPT
(validation), PRINT
and SEND statements (Display).
DATE,
TIME, and DATETIME Fields
Initialising
DATE fields with VALUE
Initialising
TIME fields with VALUE
A field may
be defined with type DATE, TIME, or DATETIME: -
DEFINE
W DATA(
Date1 DATE DPIC 'dd-MMM-yy',
Date2 DATE DPIC 'dd.MMMM.yyyy',
Date3 DATE DPIC 'dd/MM/yy',
Date5 DATE DPIC 'yyyy.ddd',
Date6 DATE DPIC 'ddd/yy',
SpecialDate
DATE VALUE 19631122);
Time1 TIME DPIC ‘hh:mm PM’,
DateTime1 DATETIME );
By defining
fields like this you make it clear to others, and to Jazz, the type of data
that is in these fields: what it is used for, what values are valid, and how it
should be displayed. Jazz can ensure
that it is set validly: for example
DEFINE
X DATA(Date5 CHAR(9));
ACCEPT(W.Date5 =
X.Date5) MESSAGE(Err.Msg);
will report
an error if the CHAR(9) value in X.Date5 is
not a valid date in format 'yyyy.ddd', such as 2016.060.
DATE and TIME fields
are internally the same as INTEGER fields, in COBOL PIC S9(9)
COMP, with initial value ZERO which means “No DATE (or TIME) value” A DATETIME field
is a combination: you can think of it as either a BIGINT
field, or a GROUP
containing a DATE and a TIME
field.
Whatever
the DPIC
(Display PICture), data in a DATE field
is always stored as 8 digits: four year digits, two
month, and two day. Thus with value
2016.060 in X.Date5, W.Date5
is set to 20160229 (2016 is a leap year).
Refer to JazzLRM_Data.#Display_Options for information about DPIC.
TIME data is be stored as 9 digits: 2 for hour (00 to 23), 2 for minute (00 to 59), 2 for seconds (00 to 59), and 3 for milliseconds.
A DATE field may be set from a constant, another DATE field, or (subject to various rules) a numeric or string field. Date fields can also be set in Date Arithmetic.
If you set a DATE field from a constant, then Jazz will interpret and validate the value when your program is checked, according to these rules: -
· Numeric constants, without any separator characters. For example: -
W.Date1
= 20160228;
The value is in order year, month, and day no matter what DPIC is used.
· Numeric constants, with single separator character “.” or “/”. If there is only a single separator character then the value is interpreted as an ordinal date, with the part before the separator being interpreted as year, and the part after the separator being interpreted as the day value from 1 to 366 (366 if a leap year). If the year is less than 100, then the current century is added, as below. The separator can be either “.” or “/”. Thus
W.Date1 = 18.300;
#568 I Date Value is 27 Oct 2018
W.Date1 = 2018/300;
#568 I Date Value is 27 Oct 2018
The ordinal date is interpreted as a string, so leading zeros don’t have to be given: 18.25 is interpreted as day 25, not as day 250.
W.Date2 = 18.25;
#568 I Date
Value is 25 Jan 2018
· Numeric constants with two separator characters. For example
W.Date1 = 04/3/2018;
#568 I Date Value is 04 Mar 2018
W.Date1 = 4/3/18;
#568 I Date Value is 04 Mar 2018
To resolve ambiguity between year and month the field’s DPIC is used. W.Date1 .has no explicit DPIC and so has the default DPIC 'dd mmm yy', so day comes before month. W.Date2 has American-style DPIC 'mm dd yy' and so 4/3 is interpreted as 3rd April, not 4th March.
W.Date2= 4/3/18;
#568 I Date Value is 03 Apr 2018
Note that the DPIC is used only to get the order of the sections Day, Month, and Year. Even with DPIC 'dd mmm yy' you can give the month as a number, while with DPIC 'dd mm yy' you could give the month as “Jan” etc. You can give more or fewer spaces, you do not have to give exactly the layout of the DPIC.
The date value is validated: for example: -
W.Date1 = 20170229; [Not a leap year
#568 E Date
Value invalid: Day Invalid
· To reset a date to its initial value, or to zero indicating that the field contains no date, use
W.Date1 =
JAZZ.$Init;
or
W.Date1
= JAZZ.$Null;
(You’ll simply write $Init or $Null, leaving it to Jazz to insert the qualification)
· String constants. With a string value the value is checked as follows: -
1. The string is split into parts, based on the separator character. Jazz scans the string for characters {"/", "-", ".", " "} and the first of these characters found is used as the separator character. With value
Date1
DATE DPIC 'dd-MMM-yy', …
W.Date1
= '27 May 17';
the blank between 27 and May is used as a separator, even though the DPIC uses “-”. Both separator characters must be the same, so
W.Date1
= '27 May-17';
is invalid.
2.
If there is no separator character the string is
interpreted as an eight-digit number, as described above.
3. If there are only two parts, then the date is interpreted as an ordinal date, i.e. a year number followed by day-of-year. Thus 2018.300 => 27th October, 2018. The same rules as above for numeric constants are followed: the century may be omitted, and leading zeros for the day part are not necessary: '18.25' is interpreted as day 25, not day 250, so this means 25th January 2018.
4. The DPIC is used to determine the order, in cases where this is ambiguous. Thus with a value
W.Date1
= '4 3 17';
does this mean the 4th of March 2017 (British), or the 3rd of April 2017 (American), or the 17th of March 2004 (possibly Japanese)? The DPIC resolves this ambiguity as described for numeric constants with separators.
You can set DATE fields from
· another DATE field,
· an INTEGER field
· a DECIMAL field with at least 8 digits and no decimal fraction
· a DECIMAL field defined DECIMAL (7,3) or DECIMAL(5,3), or
· a CHAR or VARCHAR field.
If the source type is not DATE
then it is recommended that data is assigned using an ACCEPT statement: for example
ACCEPT (W.Date1
= W.DecimalField);
instead of using a simple assignment
W.Date1 = W.DecimalField;
In both cases Jazz will check the input, but errors may not be reported unless ACCEPT is used.
Rules are as described above for assignments BUT you need to be careful to ensure that DECIMAL fractions are handled as you want. For example: -
W.Date1 = '18.25'; means the 25th day of [20]18.
W.Date1 = 18.25; interprets “18.25” as a string, and gives the same value.
Both assign the 25th day of January to the date. However DECIMAL fractions are handled according to the normal rules of maths. Thus
DNbr2
DECIMAL(7,3),
W.DNbr2 =
2018.25;
If you follow this with an assignment to a
date field: -
W.Date1
= W.DNbr2;
the date is not the 25th January, it is some time in September.
The first assignment is a normal arithmetic assignment: there’s nothing
in it to say that this value is intended to be a DATE, so it assigns 2018 and 25/100 to W.DNbr2, and is identical in value to
W.DNbr2 =
2018.250;
To have the fractional part interpreted as days-of-the-year you must write leading zeros and avoid fractions larger than 366.
W.DNbr2 = 2018.025; [25th Jan
W.Date1
= W.DNbr2;
· Date values can also be set with an expression of the form Date1 = Date2 +/- value, for example
W.Date1
= W.Date2
+ 30;
See Date Arithmetic below for more information.
Like other fields a DATE field can be defined with an initial value: -
DEFINE
W DATA(
Date1 DATE DPIC 'dd MMM yy' VALUE 20180304,
#568 I Date
Value is 04 Mar 2018
Initial values may be an eight-digit number, in format yyyymmdd, as above, or a decimal number which will be interpreted as an ordinal date: -
DateG
DATE VALUE 18.300,
#568 I Date
Value is 27 Oct 2018
or a string value giving a date as if printed with the field’s DPIC: -
DateB
DATE DPIC 'MM dd yy' VALUE '4 3 18',
#568 I Date
Value is 03 Apr 2018
In all cases message #568 will tell you how the date is interpreted, to avoid confusion between American and other date forms, and the COBOL fields will be an integer (COMP S9(9) VALUE 20180304 if the date is 4th March 2018).
When a DATE field is printed, or
displayed on a screen, then it is formatted according to its DPIC. Refer to JazzLRM_Data.#Display_Options for information about DPIC.
A DATE field may be assigned to
·
Another DATE field
· A numeric variable
Assigning a DATE to a number will assign an eight-digit numeric value such as 20180124. A message will be produced if the numeric variable has fewer than 8 digits. If the target number has a suitable DPIC then the value will be easy to interpret: for example
DEFINE
X DATA(Number INTEGER DPIC '9999,99,99');
DEFINE
W DATA(Date1 DATE DPIC 'dd-MMM-yy');
X.Number
= W.Date1;
PRINT
(W.Date1, X.Number);
resulting in output like this: -
*-Date1-* *-Number-*
29-Feb-16 2016,02,29
· A CHAR or VARCHAR variable. The date field will be formatted to a string according to its DPIC. For example: -
DEFINE
W DATA(
Date2 DATE DPIC 'dd.MMMM.yyyy');
DEFINE
X DATA(
Date2 CHAR(17);
W.Date2
= 20171217;
X.Date2
= W.Date2;
assigns value '17.December.2017' to X.Date2.
Refer to JazzLRM_Data.#Display_Options for information about DPIC.
When variable data is assigned to a DATE field it is recommended that you use ACCEPT, rather than a simple assignment, as this will validate that the assigned data is actually a date, setting $Error and producing messages when the source field cannot be converted to a valid date. Refer to JazzLRM_Accept for more information about ACCEPT. As with any other ACCEPT statement, several validations and assignments may be combined into one statement: -
ACCEPT(W.Date1 =
X.Date1, W.Date5 = X.Date5) MESSAGE(Err.Msg);
and assignment targets (W.Date1, W.Date5 in the example above) may be omitted if the target is implied or there’s no assignment because a DATE field is being validated in place.
If the source fields (X.Date1, X.Date5 in the example above) have type CHAR or VARCHAR then if possible their value is converted into a date with these rules: -
1. Firstly Jazz looks for possible separator characters: “/”, “-”, “.”, or “ ”. The first of these to be found is chosen as the separator character, even if it is not the leftmost. Thus if the string contains “12 3/4” then Jazz will use “/” as separator because it looks for this first.
2. The string value is then broken into parts using the separator character. Thus the string above would be broken into two parts, “12 3” and “4”. More realistically,
“12345678bbbb” => 1
part, “12345678”
“18.155” => 2
parts, “18”, “155”
“1/2/3” => 3
parts, “1”, “2”, “3”
3.
If one part, then the string is interpreted as
an eight digit number in format yyyymmdd. 12345678 is invalid: 56 is greater than 12,
and no month has 78 days. However a value 20180325 would be valid.
4.
If two parts the string value is interpreted as
an ordinal date. For year values less
than 100 the century added, thus 10 becomes 2018. The day-of-year must be a number from 1 to
366. Thus 18:155 is the 155th day of
2018, i.e. 4th June 2018.
5.
If three parts, then the DPIC of
the target field is used to give the relative order of day, month, and
year. Thus with
DPIC
'dd MMM yyyy' : “1/2/3”
means 1st Feb, 2003
DPIC
'MM dd yy' : “1/2/3” means 2nd January 2003.
Note that the Jazz interprets the date as flexibly as possible, so that you
don’t can give a numeric month when the DPIC specifies an alphabetic abbreviation or the
full month name, and vice versa. Day
must be a number from 1 to the maximum for the month, month must be a number
from 1 to 12 or one of the valid month abbreviations or names, and year must be
a number from 100 to 9999. If the year
is < 100, the current century is added.
None of the numeric values may be negative.
6. If there are four or more parts, then the value is invalid.
A number is first range-checked: it must not be less than 10000101 (1 Jan 1000) or greater than 99991231 (31st December 9999). If it survives this check, then the value of Month (digits 5-6) and Day (7-8) are checked as above.
You can write simple date arithmetic assignments of the form
Date1 = Date2 operator Value;
for example to add 30 days
W.Date1
= W.Date2
+ 30;
To increase or decrease a date you can use the assignment operators += and -=. Thus
W.Date1 += 30;
is equivalent to
W.Date1
= W.Date1
+ 30;
The operator may be + or -, but not multiplication (*) or division (/).
The difference value may be a constant or a field. Fields must have one of the types valid for assignment to a date. Difference values are interpreted as described above for date assignment, being broken into parts by the separator character, except that any (or all) of the year, month, and day sections may be zero. Thus
W.Date1
= W.Date2
+ 2.300;
adds 2 years and 300 days to W.Date2. With three-part dates DPIC resolves the ambiguity between British-form dates (Day, Month, Year) and American-form dates (Month, Day, Year), so that if W.Date2 has DPIC 'mm dd yy' then
W.Date1
= W.Date2
+ 2/3/0;
adds two months and three days to W.Date2.
The assignment may only have a single operator and may not use parenthesis. Thus
W.Date1
= W.Date2
+ W.NBR * 2;
is not valid.
The logic
of Date arithmetic is
1.
The
Date Difference value is converted to a GROUP with four SMALLINT
values representing the number of years, months, days-of-month and days-of-year
to be added or subtracted. This value is
like a DATE field
except that any or all parts may be zero.
2.
Logic
is as follows.
a. Add/Subtract years
b. Add/Subtract months
If months < 1 (-) then
Subtract 1 from years, add 12 to months
elseif Months > 12 (+) then
Add 1 to years, subtract 12 from months
c. Set days-in-Feb to 29 if a leap
year, else 28
d. Add/Subtract Days
If Days < 1 (-) then
Subtract 1 from months
If months < 1 then
Subtract 1 from years, add 12 to months
Add days-in-month to Days
Elseif Days > days-in-month (+) then
Subtract days-in-month from Days
Add 1 to months
If months > 12 then
Add 1 to years, subtract 12 from months
3.
If
the date difference was given as a simple number, or an ordinal date, then the
days part will be in the days-of-year field.
With date assignment,
W.Date1 =
18.155;
W.Date1 is set to
04 Jun 2018, but adding 155 days to a date is not the same as adding 4 days and 6
months, as the result of date arithmetic depends on the starting date to which
the difference is added or subtracted.
DATE fields can be used in comparisons. All the usual comparison operators =, <>, >, >=, <, <=, are available.
TIME data will be stored as 9 digits: 2 for hour (00 to 23), 2 for minute (00 to 59), 2 for seconds (00 to 59), and 3 for milliseconds. Within COBOL and Jazz, it is stored as a 4 byte field like an INTEGER, but in SQL the last three digits (milliseconds) are not carried in DB2 (and presumably Oracle), and the value will be rounded up if milliseconds is > 500.
A TIME field may be set from a constant, another TIME field, or (subject to various rules) a numeric or string field. TIME fields can also be set in Date Arithmetic.
Constants
are written in order Hours, minutes, seconds, and milliseconds. Whatever form you write – time, number,
string – it will be converted to a time constant. For example, if you write
W.T1 = 154512345;
it will be changed to
W.T1 = 15:45:12.345;
when [Check] is clicked, except for value 0 which stays as 0, assigning a time of 00:00:00.000.
The constant must be a valid time. If not, there will be one or more messages, and the invalid sections of the time will be set to zero. For example
W.T1 = 127862789;
becomes
W.T1 = 12:00:00.789;
#734 E Minutes < 0 or > 60
#734 E Seconds < 0 or > 60
Click [Check] again if you want to accept the 00
values, or enter the correct values.
You can set TIME fields from another TIME field, or
· a compatible numeric field: INTEGER, DECIMAL(9), PIC ‘999999999’
· From a CHAR or VARCHAR field, which is interpreted as a number.
The value assigned to a TIME field must be a valid time: all parts must be numeric, and value in the range
Hour: 0 to 23,
Minute: 0 to 59
Second: 0 to 59
Millisecond: 0 to 999.
An assignment of a compatible field to a TIME is checked to ensure that the compatible field actually contains a TIME value. For example, with
DEFINE W DATA(
T1 TIME,
Int INTEGER,
…
this assignment produces message #737: -
W.T1 = W.Int;
#737 W W.Int may be invalid.
Check JZTMVS.Error after this assignment
If you are not confident that W.Int will always have a valid time value, then you should follow this statement with some logic starting with
IF JZTMVS.Error
THEN;
…
END IF;
Assignments from compatible numeric fields may contain
values like 123456789 and from CHAR or VARCHAR
fields values like ’123456789’. In addition,
assignment from CHAR or VARCHAR This form of assignment may include DPIC, in which case the character string is interpreted according
to the DPIC of the target TIME field,
e.g.
W.T5 = W.CH DPIC;
interprets the value in W.CH according to W.T5’s DPIC.
When you use VALUE with a TIME field, the value is validated and
converted to a time constant. For
example, you might write
DEFINE W DATA(
T1 TIME VALUE 12:34:56.789,
T2 TIME DPIC 'hh:mm
PM' VALUE 123456789,
T3 TIME VALUE '123456789',
T4 TIME VALUE '12:34:56.789',
T5 TIME DPIC 'hh:mm'
VALUE '12:34',
Last INTEGER);
[Check] changes this to
DEFINE W DATA(
T1 TIME VALUE 12:34:56.789,
T2 TIME DPIC 'hh:mm
PM' VALUE 12:34:56.789,
T3 TIME VALUE 12:34:56.789,
T4 TIME VALUE 12:34:56.789,
T5 TIME DPIC 'hh:mm'
VALUE 12:34:00.000,
Last INTEGER);
If the value is not a valid Time then there
will be a message, and the invalid part will be set to 00.
For TIME fields, DPIC uses these symbols
hh Represents the hour, using the 24 hour clock, e.g. 13 = 1 PM. Values 0 to 23.
mm Minutes. Values 0 to 59.
ss Seconds. Values 0 to 59.
mmm Milliseconds. Values 0 to 999. Depending on the system clock, this value may be approximate.
PM Will print hours as 0-12, and display AM or PM depending on whether the hour is >= 12. Hours 00 to 09 will omit the leading zero.
mm and mmm must be lower case to distinguish it from Month values in a DATE value, and hh and ss must also be lower case. PM must be upper case.
Blank (“ “
or B), period (.), and colon (:) are separator characters. The default DPIC
is ‘hh:mm:ss.mmm’
For example: -
DEFINE W DATA(
T1 TIME,
T2 TIME DPIC 'hh:mm:ss.mmm',
T3 TIME DPIC 'hh:mm PM',
T5 TIME DPIC 'hh:mm PM',
T6 TIME DPIC 'hh:mm:ss.mmmBPM',
CH CHAR(12),
INT INTEGER;
W.T1 = 15:45:12.345;
W.T2 = W.T1;
W.T3 = W.T1;
W.T5 = 08:15:30.123;
W.T6 = W.T1;
PRINT (W.T1, W.T2, W.T3, W.T5, W.T6) FIELDTABLE;
Prints: -
Field VALUE
W.T1 15:45:12.345
W.T2 15:45:12.345
W.T3 3:45
PM
W.T5 8:15
AM
W.T6 3:45:12.345
PM
In all cases the actual value handled by COBOL is a simple integer, e.g.
002890 MOVE 154512345 TO T1 OF W. ATP
If the DPIC does not include mmm to display milliseconds, then the value is rounded up if milliseconds is greater than 500. Thus with
DEFINE W DATA(
T1 TIME DPIC 'hh:mm:ss',
W.T1 = 15:45:12.745;
prints as
W.T1 15:45:13
This would also be the value stored in DB2 if W.T1 is assigned to a field in the DB record and this record is written to DB2. However in your Jazz and COBOL program, the data retains its full 9 digit precision, including 745 milliseconds.
TIME fields can be assigned to INTEGER, DECIMAL(9), PIC ‘999999999’ fields, and to CHAR and VARCHAR fields. For example
W.INT = W.T1;
would assign value 154512345 to W.INT. For DECIMAL and PIC the target field must have 9 digits, and no decimal fraction.
For assignment to CHAR and VARCHAR fields the TIME field’s DPIC determines the format of the output.
TIME values can be added and subtracted, with statements like
W.TR1 = W.T1 + W.T2;
W.TR2 = W.T2 - W.T1;
W.TR3 = W.T1 - W.T2;
W.TR4 = W.T1 + 12:34:56.789;
If a
constant is added or subtracted, it must be written as a Time Constant, and be
the 2nd operand.
The shorthand operators += and -= can be used to add or subtract a value from a TIME. These are equivalent: -
W.T1 = W.T1 + W.T2;
W.T1 += W.T2;
So are
these: -
W.T1 = W.T1 + 12:34:56.789;
W.T1 += 12:34:56.789;
Addition may result in a value greater than 23:59:59:999, and subtraction may result in a value less than 00:00:00.000. If so, the Hour part is set to the value MOD 24, and a non-zero overflow value results. You may choose to test the overflow field JZTMAR.OFlow, this will be -1 if value would have been less than 00:00:00.000, and +1 if the value exceeded 23:59:59:999
TIME fields can be used in comparisons. All the usual comparison operators =, <>, >, >=, <, <=, are available.
Not yet implemented, except for
definition. A DATETIME field is like a GROUP defined
DTGroup
GROUP,
DatePart DATE,
TimePart TIME,
end GROUP);
except that adding or subtracting
time from Timepart does not affect Datepart, whereas adding or subtracting time from a DATETIME field can change the date
part of the DATETIME field.
Assignment between DATETIME and
DATE or TIME fields is permitted: -
·
Datetime = Date; Datepart set, Timepart is set to
00:00:00:000
·
Datetime = Time; Date set to 0,
Timepart set.
E-level message
·
Date = Datetime; Datepart is
assigned, Timepart ignored
·
Time = Datetime; Timepart is
assigned, Datepart ignored.
·
For arithmetic with
DATETIME fields and a TIME field the overflow is rolled
into DATE part of the calculation,
resulting in a day being added or subtracted to the date, and no message is
produced.
Some material is like this, written in the normal text, but coloured green. This is material that hasn’t been implemented yet. As this material is implemented the text is reviewed, changed as necessary, and changed to normal text colour.