The Worksheet | Fixing Date Problems |
This chapter explains some of the basic year 2000 problems and the principles for fixing them using the windowing technique. It is equally suitable as background information whether you are verifying or remediating applications.
The COBOL constructions that can cause a year 2000 problem are:
In addition, although CALL statements do not in themselves cause a problem, if you pass year data items as parameters to the called programs, you will need to check and fix those programs as necessary.
To see sample code showing these and more complex problems, see the gotchas.cbl sample program within the sfplus\revolve\sample directory.
SmartFix generates fixes based on the year type of the suspect data items and applies the fixes according to the type of suspect statement, as follows:
SmartFix provides a fix for only some types of statements, and sets the fix status of the statement accordingly, as follows:
Note: By default, SmartFix inserts COBOL code that conforms to the ANSI '85 standard. You can for fix code that conforms to the ANSI '74 standard. This fix code is in the ANS74 macro library and is available from the Fix Options tab of the worksheet Options.
SmartFix provides fixes for:
SmartFix does not provide fixes for:
Comparisons can occur in EVALUATE, IF, PERFORM and SEARCH statements. SmartFix does not handle SEARCH statements at all.
The following statement compares two years. If the two years are in different centuries, the wrong result is produced. For example:
if yy1 > yy2 * if 05 > 98 *> wrong result * if 2005 > 1998 *> correct result
Note that if the comparison is comparing for equality or non-equality, there is not a logic problem at the century boundary.
You now need to search for the PERFORM, EVALUATE and SEARCH statements in much the same way.
* if yy1 > yy2 *> original IF statement 1 expand yy1 into ccyy1 *> ready to compare in line 3 2 expand yy2 into ccyy2 *> ready to compare in line 3 3 if ccyy1 > ccyy2 *> compare expanded data items
Lines 1 and 2 | The two date data items need expanding ready for the comparison in the IF statement in line 3. |
Line 3 | The IF statement needs to use the expanded versions of the two date data items. |
SmartFix fixes any comparison that has an operand with a year type allocated. SmartFix uses the macros for each operand's year type and handles the statement as follows:
SmartFix does not do anything with the expanded date data items after the statement, since the operands were not changed in the statement and the originals are still valid.
For example, when comparing a group item with a numeric item, the group item is treated as a non-numeric item and so the alphanumeric equivalent of the numeric item is successfully compared with it.
01 display-date PIC 9(6). 01 yymmdd 03 yy PIC 99. 03 mm PIC 99. 03 dd PIC 99. . . . if display-date > yymmdd
The PERFORM statement header can involve a comparison in its UNTIL clause, and an arithmetic modification in its VARYING clause. Both the comparison and the modification can cause logic problems if the data items involved represent dates.
SmartFix automatically generates a fix for the PERFORM statement header so that the UNTIL condition and the VARYING clauses are evaluated correctly the first time through the PERFORM loop. The compared data items are expanded before the header, and the varied operand is expanded before the header and contracted after it.
However a complication arises in fixing PERFORM statements because the date data items in the PERFORM statement header might be modified during the performed code and so they need fixing every time through the PERFORM loop. In these situations, you need to manually add code at the end of the PERFORM loop to ensure the date data items are up to date when they tested and varied again in the UNTIL and VARYING clauses of the PERFORM statement header.
In a PERFORM UNTIL statement, two operands are compared, and one or other of them will be modified within the performed code before they are compared again.
perform until yy1 > yy2 . . . performed code . . . end-perform
The initial comparison can be fixed like any other comparison, by expanding the date data items before the comparison. The complication in a PERFORM statement is that the date data items might change each time through the loop and so they need re-expanding every time.
1 expand yy1 into ccyy1 *> ready to compare in line 3 2 expand yy2 into ccyy2 *> ready to compare in line 3 3 perform until ccyy1 > ccyy2 *> compare expanded dates . . . performed code . . . 11 expand yy1 into ccyy1 *> in case yy1 has changed 12 expand yy2 into ccyy2 *> in case yy2 has changed 13 end-perform
Lines 1 and 2 | The two date data items need expanding ready for the first comparison in the UNTIL clause in line 3. |
Line 3 | The UNTIL clause needs to use the expanded versions of the two date data items. |
Lines 11 and 12 | The date data items might have been modified in the performed code since the PERFORM statement header. In case they have, they need re-expanding, ready for the next comparison in the UNTIL clause. |
SmartFix automatically generates a fix that:
You need to manually edit the code to expand the date data items at the end of the performed code, immediately before the END-PERFORM statement. If the performed code modifies only one of the date data items, you need expand that one only. See lines 11 and 12 in the fix above.
In a PERFORM VARYING . . . statement, an operand is assigned an initial value at the start of the loop and is varied every time through the loop. This section describes the case where the varied operand is not involved in the comparison in the UNTIL clause.
perform varying yy1 from yy2 by 1 until yy3 > yy4 . . . performed code . . . end-perform
This VARYING clause assigns the date data item, yy1
, with
an initial value and then modifies that date data item each time through
the loop. The initial assignment does not cause a problem. The
modification, however, might involve problematic arithmetic and so needs
fixing.
1 expand yy2 into ccyy2 *> ready to assign in line 4 2 expand yy3 into ccyy3 *> ready to compare in line 4 3 expand yy4 into ccyy4 *> ready to compare in line 4 4 perform varying ccyy1 from ccyy2 by 1 until ccyy3 > ccyy4 5 contract ccyy1 into yy1 *>since ccyy1 has changed . . . performed code . . . 11 expand yy1 into ccyy1 *> in case yy1 has changed 12 expand yy2 into ccyy2 *> in case yy2 has changed 13 expand yy3 into ccyy3 *> in case yy3 has changed 14 end-perform 15 contract ccyy1 into yy1 *> since ccyy1 has changed
Line 1 | In theory, this date data item does not need expanding.
However, for practical purposes it does, because it is used to assign an
expanded date to the date data item ccyy1 . Note that the
date data item, yy1 , does not need expanding initially,
since it is assigned its value in line 4. |
Lines 2 and 3 | The two compared date data items need expanding ready for the first comparison in the UNTIL clause in line 4. |
Line 4 | Although, the expanded ccyy1 is not needed for the
assignment in the first execution of the PERFORM statement, it is needed
in subsequent executions, where modifications occur. Since the expanded
ccyy1 is needed, the expanded ccyy2 is also
needed for the initial assignment. As described in the previous section,
the two expanded date data items, ccyy3 and ccyy4 ,
need to be used in the UNTIL clause. |
Line 5 | The varied date data item, ccyy1 , needs to be
contracted back after being varied, in case the varied value is used in
the performed code. |
Lines 11, 12 and 13 | The date data items might have been modified in the performed code since the PERFORM statement header. In case they have, they need re-expanding, ready for the next variation and the next comparison in line 4. |
Line 15 | In theory, the varied date data item, ccyy1 , needs to
be contracted back after dropping out of the PERFORM loop, since the
last action in the PERFORM loop was to vary it. In practice, this needs
to happen only if the unexpanded version of the date data item, yy1
is used subsequently. |
SmartFix automatically generates a fix that:
ccyy1
immediately
after the PERFORM statement header. See line 5 in the fix above. You need to manually edit the code to:
ccyy1
immediately
after dropping out of the PERFORM loop, after the END-PERFORM statement.
You do not need to do this if the date data item, yy1
, is
not used in the subsequent code. Also, you do not need to contract back
any operands that are not varied, since their contracted versions are
still valid. See line 14 in the fix above.In a PERFORM VARYING ... statement, an operand, yy
, is
varied until it meets the condition in the UNTIL clause.
perform varying yy1 from yy2 by 1 until yy1 > yy3 . . . performed code . . . end-perform
The varied operand, yy1
, might be used during the
performed code and it might be modified before it is varied and tested
again. An up-to-date version of this operand needs to be available to the
condition every time it is tested.
1 expand yy2 into ccyy2 *> ready for assignment in line 3 2 expand yy3 into ccyy3 *> ready for comparing in line 3 3 perform varying ccyy1 from ccyy2 by 1 until ccyy1 > ccyy3 4 contract ccyy1 into yy1 *> since ccyy1 has changed . . . performed code . . . 11 expand yy1 into ccyy1 *> in case yy1 has changed 12 expand yy3 into ccyy3 *> in case yy3 has changed 13 end-perform 14 contract ccyy1 into yy1 *> since ccyy1 has changed
Line 1 | In theory, this date data item, yy2 does
not need expanding. However, for practical purposes it does, because it
used to assign an expanded date to the date data item ccyy1
in line 3. Consequently, the date data item, ccyy1 , does
not need expanding initially, since it is assigned its value in line 3
from ccyy2 . |
Line 2 | The other date data item to be compared, ccyy3 , needs
expanding ready for the comparison in the until clause on line 3. |
Line 3 | Although an expanded ccyy1 is not needed for the
assignment in the first execution of the PERFORM statement, it is needed
in subsequent executions where a modification occurs. Since the expanded
ccyy1 is needed, the expanded ccyy2 is also
needed for the initial assignment. As described in the previous section,
the PERFORM statement needs to use the expanded versions of the two
compared date data items, ccyy1 and ccyy3 ,
in its UNTIL clause. |
Line 4 | The varied date data item, ccyy1 needs to be
contracted back after being varied, in case the varied value is used in
the performed code. |
Lines 11 and 12 | The date data items might have been modified in the performed code
since the PERFORM statement header. In case they have, they need
re-expanding, ready for the next variation and the next comparison in
line 3. The date data item, ccyy2 , that was used for the
initial assignment is not used in the PERFORM statement again and so
does not need re-expanding. |
Line 14 | In theory, the varied date data item, ccyy1 , needs to
be contracted back after dropping out of the PERFORM loop, since the
last action in the PERFORM loop was to vary it. In practice, this needs
to happen only if the unexpanded version of the date data item, yy1
is used subsequently. |
SmartFix automatically generates a fix that:
ccyy1
immediately
after the PERFORM statement header. See line 4 in the fix above. You need to manually edit the code to:
ccyy1
immediately
after dropping out of the PERFORM loop, after the END-PERFORM statement.
You do not need to do this if the date data item, yy1
, is
not used in the subsequent code. See line 14 in the fix above. When using the WITH TEST AFTER clause, the condition is tested after the PERFORM code has been executed, and so affects which operands need to be expanded when.
perform with test after varying yy1 from yy2 by 1 until yy1 > yy3 . . . performed code . . . end-perform
In this statement, the varied operand, yy1
, is not varied
until the end of the performed code. Since it is possible that yy1
was modified during the performed code and it needs expanding at the end
of the performed code, just before it is varied and tested.
1 expand yy2 into ccyy2 *> ready for assigning in line 2 2 perform test after varying ccyy1 from ccyy2 by 1 until ccyy1 > ccyy3 3 contract ccyy1 into yy1 *> because ccyy1 has changed . . . performed code . . . 11 expand yy1 into ccyy1 *> in case yy1 has changed 12 expand yy3 into ccyy3 *> in case yy3 has changed 13 end-perform
Line 1 | The date data item ccyy2 needs expanding
ready for the initial assignment. |
Lines 11 and 12 | These date data items ccyy1 and ccyy3
need expanding ready for the test at the end of the performed code. Note
that they did not expanding before the PERFORM statement header. |
SmartFix automatically generates a fix that:
ccyy2
, ready for assigning
to ccyy1
in the PERFORM statement header. See line 1 in
the fix above.
ccyy1
immediately
after the PERFORM statement header. See line 3 in the fix above. You need to manually edit the code to:
ccyy1
and ccyy3
at the end of the performed code, immediately before the END-PERFORM
statement. If the performed code does not modify all the date data
items, you need expand only the ones that are modified. See lines 11 and
12 in the fix above.
Note that the automatically generated fix expands these date data
items, ccyy1
and ccyy3
before the PERFORM
statement header. This expansion does not need before the header,
although it does no harm. SmartFix includes these expansions so that
you can easily copy them from before the header to the end of the
PERFORM loop.
When the PERFORM loop finishes, no action is needed, since the test is done without varying any expanded operands. The expanded and contracted versions of all the operands are in step.
In this statement an out-of-line routine is performed.
perform routine varying yy1 from yy2 by 1 until yy1 > yy3
The operands need attention every time through PERFORM loop, and yet you cannot apparently fix them without changing the performed routine itself.
1 expand yy2 into ccyy2 *> ready to assign in line 3 2 expand yy3 into ccyy3 *> ready to compare in line 3 3 perform varying ccyy1 from ccyy2 by 1 until ccyy1 > ccyy3 4 contract ccyy1 into yy1 perform routine 11 expand yy1 into ccyy1 *> ready to compare in line 3 12 expand yy3 into ccyy3 *> ready to compare in line 3 13 end-perform 14 contract ccyy1 into yy1
Line 1 | In theory this date data item, yy2 does not need
expanding. However, for practical purposes it does, because it used to
assign an expanded date to the date data item ccyy1 in
line 3. Consequently, the date data item, ccyy1 , does not
need expanding initially, since it is assigned its value in line 3 from
ccyy2 . |
Line 2 | The date data item, ccyy3 , needs
expanding ready for the comparison in the until clause on line 3. |
Line 3 | Although an expanded ccyy1 is not needed for the
assignment in the first execution of the PERFORM statement, it is needed
in subsequent executions where a modification occurs. Since the expanded
ccyy1 is needed, the expanded ccyy2 is also
needed for the initial assignment. As described in the previous section,
the PERFORM statement needs to use the expanded versions of the two
compared date data items, ccyy1 and ccyy3 ,
in its UNTIL clause. |
Line 5 | The varied date data item, ccyy1 needs to be
contracted back after being varied, in case the varied value is used in
the performed code. |
Lines 11 and 12 | The date data items might have been modified in the performed code.
In case they have, they need re-expanding, ready for the next variation
and the next comparison in line 3. The date data item, ccyy2 ,
which was used for the initial assignment, is not used in the PERFORM
statement again and so does not need re-expanding. |
Line 14 | In theory, the varied date data item, ccyy1 , needs to
be contracted back after dropping out of the PERFORM loop, since the
last action in the PERFORM loop was to vary it. In practice, this needs
to happen only if the unexpanded version of the date data item, yy1
is used subsequently. |
To avoid changing the performed routine, SmartFix adds an in-line PERFORM to test the condition and retains the out-of-line PERFORM without a condition. All the necessary expansions and contractions are done within the new in-line performed code, either before it is called or on return from it.
SmartFix automatically generates a fix that:
ccyy1
immediately
after the PERFORM statement header. See line 4 in the fix above.
You need to manually edit the code to:
ccyy1
immediately
after dropping out of the PERFORM loop, after the end PERFORM statement.
You do not need to do this if the date data item, yy1
, is
not used in the subsequent code. See line 14 in the fix above. Exiting from performed code in the middle does not cause a problem. You can exit using:
Wherever there is an EXIT PERFORM within inline performed code, no action is needed. The contracted versions of the varied operand and those tested in the UNTIL condition are up to date, ready for use in the rest of the program.
This is a relatively new construct in the COBOL language and so you probably will not have to handle it. After an EXIT CYCLE statement, control passes to the END-PERFORM statement and the PERFORM cycle continues.
If there is a GOTO within the performed code, both contracted data items are up to date. The GOTO statement passes control to the procedure-name, from which control does not return.
Until |
Test after until |
Varying until |
Test after varying |
|
---|---|---|---|---|
Before PERFORM statement: |
||||
Expand operands in FROM and BY |
- |
- |
y |
y |
Expand operands compared in UNTIL |
y |
- |
y |
- |
After PERFORM statement: |
||||
Contract the varied operand |
- |
- |
y |
y |
After performed code: |
||||
Expand varied operand, if changed |
- |
- |
y |
y |
Expand operands compared in UNTIL, if changed |
y |
y |
y |
y |
After END-PERFORM statement: |
||||
Contract the varied operand |
- |
y |
- |
EVALUATE statements comprise comparisons and potentially arithmetic expressions, both of which can cause problems at the century boundary. For example:
Evaluate 05 - 95 *> fails. Should be 10 not -90. When 05 > 95 *> fails. Should be true not false.
The following EVALUATE statement involves a conditional expression and so needs fixing:
evaluate yy1 > yy2 when true . . . when false . . . end-evaluate
The following EVALUATE statement involves an arithmetic expression and so needs fixing:
evaluate yy1 - yy2 when tmp-1 . . . when tmp-2 . . . end-evaluate
SmartFix handles EVALUATE statement headers that contain conditional or arithmetic expressions, but not any of the WHEN clauses.
1 expand yy1 into ccyy1 2 expand yy2 into ccyy2 3 evaluate yy1 > yy2 when true . . . when false . . . end-evaluate
Lines 1 and 2 | The year operands, yy1 and
yy2 , need expanding before the EVALUATE statement in line
3. |
Line 3 | The EVALUATE statement needs to use expanded versions of the year operands. |
Use SmartFix to expand the year data items as described above to fix the EVALUATE statement header.
For other constructions of EVALUATE statements, you need to edit the WHEN clauses manually.
CALL statements do not in themselves cause a problem, but if you pass year data items as parameters to the called programs, you will need to check and fix those programs as necessary.
For example, in the following CALL statement, two year date data items are passed to the called program:
call program using yy1 yy2
SmartFix fixes simple CALL statements only, fixing the year data items in the USING clause, but not if other phrases all included in the statement, such as BY VALUE.
1 expand yy1 into ccyy1 2 expand yy2 into ccyy2 3 call program using ccyy1 ccyy2
Lines 1 and 2 | The year data items, yy1 and
yy2 , need expanding before the CALL statement in line 3.
|
Line 3 | The CALL statement needs to use expanded versions of the year data items. |
Use SmartFix to handle the year data items as described above.
You also need to fix the called program to expect the expanded parameters. For example, if you are calling a COBOL program. you might need to update the PROCEDURE DIVISION USING statement in the called program and its Linkage section.
SmartFix does not currently handle the following components of CALL statements:
In addition SmartFix does not provide any fixes for the called program or any data declarations for it.
If the operands input to the subtraction represent dates, SmartFix expands them before the operation. If the result is a date, it is contracted back after the operation.
Interestingly the result of a subtraction might be a year or the difference between some dates. Be wary of a non-date result that is stored in an operand that potentially holds a date. Similarly a date might be stored in a non-date data item.
The following statement subtracts one year from another, giving the difference between them, and not a year. If the two year operands are in different centuries, the wrong result is produced. For example:
subtract yy1 from yy2 giving difference 05 - 95 = - 90 *> wrong result 2005 - 1995 = 10 *> correct result
Having studied the SUBTRACT statements, you need to examine statements with arithmetic expressions that might contain subtractions. To do this:
1 expand yy1 into ccyy1 2 expand yy2 into ccyy2 3 subtract yy1 from yy2 giving difference
Lines 1 and 2 | The two year operands, ccyy1
and ccyy2 , need expanding before the subtraction in line
3. |
Line 3 | The subtraction needs to use the two expanded year operands. In
this situation, the result, difference is not a year and
so does not need expanding or contracting in any way. |
Use SmartFix to handle the year operands as described above, expanding them as necessary, according to their year types.
The following statement subtracts a value from a year, giving a new year. If the subtraction causes the century boundary to be crossed, the wrong result is produced:
subtract temp from yy1 giving yy2 03 - 5 = - 2 *> wrong result 2003 - 5 = 1998 *> correct result
Find the subtractions in the same way as described in the section, Subtracting One Year from Another
1 expand yy1 into ccyy1 2 subtract temp from ccyy1 giving ccyy2 3 contract ccyy2 into yy2
Line 1 | The year operand, yy1 , needs expanding
before the subtraction in line 2. However, the year operand, yy2 ,
does not need expanding, since it is generated by the subtraction in
line 2. The operand temp is not a date and so does not
need expanding. |
Line 2 | The subtraction needs to use expanded year operand, ccyy1 ,
and to store the result in the expanded operand, ccyy2 .
|
Line 3 | The result operand ccyy2 needs to be contracted ready
for use in subsequent code. |
Use SmartFix to handle the year operands as described above, expanding them as necessary, according to their year types.
In the following statement, the result is not a year and yet is stored in a data item that is allocated a year type. This causes an additional problem for you when fixing, because the year type allocated to the result operand is no longer appropriate after the operation.
subtract yy1 from yy2 05 - 95 = - 90 *> wrong result 2005 - 1995 = 8 *> correct result
Find the subtractions in the same way as described in the section, Subtracting One Year from Another
1 expand yy1 into ccyy1 2 expand yy2 into ccyy2 3 subtract ccyy1 from ccyy2 3 contract ccyy2 into yy2
Lines 1 and 2 | The two year operands, yy1 and yy2 ,
need expanding before the subtraction in line 3. |
Line 3 | The subtraction needs to use the two expanded year operands. |
Line 4 | The result operand ccyy2 needs to be contracted ready
for use in subsequent code. |
Use SmartFix to handle the year operands as described above, expanding them as necessary, according to their year types.
In this situation, the result, ccyy2
, is not a year and
yet is stored in a operand that is allocated a year type. This potentially
causes you a problem when fixing subsequent lines. SmartFix will expect to
map the operand according to its year type in subsequent suspect lines,
giving the wrong result.
To avoid this, you need to examine subsequent uses of the operand and when it does not contain a date, disable any automatic mapping of the operand to an expanded version. To disable operand mapping in one statement, go to the operand table in the SmartFix dialog and right-click the operand. You can then click Leave operand alone.
An addition might hide a subtraction, if one of the data items is negative or if a negative literal is used. This statement then has the same problems as a subtraction, and needs fixing in a similar way.
The negative additions might already be identified in the worksheet as a result of running Verify all research. In which case the negative additions are categorized as R-AddNegLtil. To find them:
If the negative additions are not categorized in the worksheet, you can run the tool, Find negative additions to do this. This tool searches the worksheet and identifies any ADD and COMPUTE statements in the worksheet that involve a negative literal or a signed data item. The tool then categorizes those statements and their data items as R-AddNegLitl.
1 expand yy1 into ccyy1 2 add temp to ccyy1 3 contract ccyy1 into yy1
Line 1 | The year operand, yy1 , needs expanding
before the addition in line 2. The operand temp is not a
date and so does not need expanding. |
Line 2 | The addition needs to use expanded year operand, ccyy1 ,
in which the result is also stored. |
Line 3 | The result operand ccyy1 needs to be contracted ready
for use in subsequent code. |
Use SmartFix to handle the year operands as described above, expanding them as necessary, according to their year types. Regardless of whether the operands are positive or negative, the correct result will be produced.
Addition of two-digit years does not always cause a problem. When numeric data items (such as PIC 99) are added, they are successfully truncated:
01 yy PIC 99. 0 1 yymmdd PIC 9(6). . . . Add temp to yy * 05 + 98 = 03 (correct) Add temp to yymmdd * 050000 + 981122 = 031122 (correct)
Addition of positive two-digit years causes a problem if the result is not truncated and is allowed to extend beyond the two digits. This can happen when the result operand can hold a three-digit result. For example:
01 yyyy PIC 9(4). 0 1 ddmmyy PIC 9(6). 0 1 yy-comp PIC 99 COMP. . . . Add temp to yyyy * 05 + 98 = 0103 (wrong) * 05 + 1998 = 2003 (correct) Add temp to ddmmyy * 05 + 070998 = 071003 (wrong) * 05 + 07091998 = 07092003 (correct) Add temp to yy-comp * 05 + 98 = 103 (wrong)
Data items defined as COMPUTATIONAL do not hold the data as decimal digits. This means that a data item, such as PIC 99 COMP, is normally assigned one byte, which can hold values up to 255.
These additions can be a problem if the programs are compiled with the NOTRUNC Compiler directive, which prevents the results being truncated.
The additions without truncation might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-Add3DigitYr or R-UseNOTRUNC. To find them:
If the additions without truncation are not categorized in the worksheet, you can run the tool, Find add to 3-digit year, to do this. This tool searches the worksheet for additions where the data item holding the result can hold more than 2 digits, and optionally binary items of 1 and 2 bytes. The tool then categorizes those statements and their data items as R-Add3DigitYr.
Alternatively, you can use Find potential NOTRUNC problems, which finds the additions involving COMP and COMP-4 data items of 2 bytes. The tool then categorizes those statements and their data items as R-UseNOTRUNC.
1 expand yy1 into ccyy1 2 add temp to ccyy1 3 contract ccyy1 into yy1
Line 1 | The year operand, yy1 , needs expanding
before the addition in line 2. The operand temp is not a
date and so does not need expanding. |
Line 2 | The addition needs to use expanded year operand, ccyy1 ,
in which the result is also stored. |
Line 3 | The result operand ccyy1 needs to be contracted ready
for use in subsequent code. |
Use SmartFix to handle the year operands as described above, expanding them as necessary, according to their year types.
If a date data item is truncated by two or four digits, it is possible that the two digits representing the century are lost and this could cause a problem in subsequent statements that use the truncated item. For example:
Move ccyy to yy * Move 1956 to 56 (could cause a problem)
If the target is alphanumeric or a group item, the least significant two digits of the year are lost and the statement is probably not of interest. For example:
Move ccyy to yy * Move 1956 to 19 (not a problem)
Situations where the century might be lost through truncation are where a:
The date truncations might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-DateTrunc. To find them:
If the date truncations are not categorized in the worksheet, you can run the tool, Find date truncation, to do this. This tool searches the worksheet for MOVE statements that truncate the values of the data items being moved. A value might be truncated because it is being moved to a data item of a smaller size, or because it is being moved from a data item of COMP format into one of Character or Display format. COMPUTATIONAL data items can hold values greater than implied by their COBOL PICTURE, which can lead to truncation. The tool then categorizes those statements and their data items as R-DateTrunc.
The statements are not a problem in their own right and so do not need fixing.
Multiplication and division are not frequently used on dates. However, multiplication can be used to multiply the years by 12 to calculate the number of months since 1900, or by 52 to calculate the weeks, and so on, and this might lead to problems
Multiplication and division do not cause a problem if the purpose is to convert a year into a different representation, since the century boundary is not crossed.
multiply yy 10000 giving tmp-yyyyyy * 69 * 1000 = 690000
Division is similar to multiplication. The added danger is where the divisor is zero when it represents the two-digit form of the year 2000.
The multiplication or division statements might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-YYMultDiv. To find them:
If the multiplication or division statements are not categorized in the worksheet, you can run the tool, Find YY multiply, divide, which identifies year data items used in multiplication or division statements. This tool searches the worksheet for the statements involving 2-digit numeric data items in multiplication or division, which includes MULTIPLY, DIVIDE and COMPUTE statements. The tool then categorizes those statements and their data items as R-YYMultDiv
Problems:
Division can cause a problem if the divisor is 00, representing the year 2000.
Find these divisions in the same way as described in the previous section.
If the division is included in a COMPUTE statement or arithmetic expression, SmartFix applies the appropriate macros to handle any data items that have a year type. SmartFix uses the macros for the relevant year types and handles the statement as follows:
If the suspect statement uses the DIVIDE verb, SmartFix does not provide a fix, or allow you to specify a fix. If you need to fix the statement, you have to edit the code manually.
In the following statement, the year operand, yy
, is
multiplied by 12 to calculate the months since 1900. If the year is in the
2000s the wrong result is produced. For example, to calculate the months
for June 2005:
compute total-mm = (yy * 12) + mm * 66 = (05 * 12) + 6 (wrong) * 1266 = (05 * 12) + (100 *12) + 6 (correct)
Unlike in subtractions, expanding the year operand does not solve the problem, since the computation produces the months since the year 0000, and not the months since 1900 as required. Also the result might be too large for the receiving data item. For example:
compute total-mm = (ccyy * 12) + mm * 24066 = (2005*12) + 6 (wrong, mm since 0000) * 1266 = ((2005*12) + 6) - (1900*12) (correct, mm since 1900)
These date calculations might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-UseOfLiterals. To find them:
If the date calculations are not categorized in the worksheet, you can run the tool, Find multiply, divide by 4, 12, 365, which identifies calculations that might operate on date values, such as leap-year calculations or time-duration calculations. The tool searches the worksheet for data items initialized to or used with the values 4, 12, 365 or 366 and then finds any statements involving those data items in multiplication or division, which includes MULTIPLY, DIVIDE and COMPUTE statements. The tool then categorizes those statements and their data items as R-UseOfLiterals
1 expand yy1 into ccyy1 2 subtract 1900 from ccyy1 3 compute total-mm = (ccyy1 * 12) + mm
Line 1 | The year operand, yy1 , needs expanding
before the computation in line 3. The operand mm is not a
year and so does not need expanding. |
Line 2 | The computation in line 3 is expecting the years since 1900, not
since the year 0000, so 1900 needs to be subtracted from the expanded
year operand, ccyy1 . Although you can subtract an
equivalent value after the computation, there is a danger that the
result operand total-mm is not large enough, so it is
safer to subtract before the computation in line 3. |
Line 3 | The COMPUTE statement needs to use expanded year operand, ccyy1 ,
which now hold the years since 1900. |
There are broadly two places where you can fix this problem. You can either fix the statements where the month value is used in a comparison, subtraction or addition. This is the usual windowing approach. However, you can fix the month value where it is calculated, where the multiplication occurs, which can be simpler and incur less change to the program.
To fix the statement where the month operand is calculated:
yy1
, using the
appropriate macro for that operand's year type. See line 1 in the fix
above.
yy1
. See line 2 in the fix
above.
To generate fixes for the statements that operate on the month operand:
This means the result does not represent the months since 1900 as originally intended.
If the suspect statement uses the MULTIPLY verb, SmartFix does not provide a fix, or allow you to specify a fix. If you need to fix the statement, you have to edit the code manually.
Literals representing the century or years in some form or another can cause problems. For example specific literals representing the century, like 19 and 1900, might need fixing.
add 1900 yy giving ccyy * 1900 + 03 = 1903 (wrong) * 2000 + 03 = 2003 (correct)
The expansion that SmartFix generates does not take account of literals that represent years or centuries. SmartFix expands the year operands correctly and then proceeds to add 1900 as well. For example:
add 1900 ccyy-1 giving ccyy-2 * 1900 + 1903 = 3803 (wrong) * 1900 + 2003 = 3903 (wrong)
These embedded centuries might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-19/20Litl. To find them:
If these embedded centuries are not categorized in the worksheet, you can run the tool, Find embedded century literals, which finds literal strings that might have a century value embedded in them, such as "July 24, 1998". This tool searches the worksheet for data items initialized to the values "* 19" and "* 20", that is 19 or 20 preceded by a space. The tools also searches for the statements that use those strings. The tool then categorizes those statements and their data items as R-19/20Litl
1 expand yy1 into ccyy1 2 move ccyy1 into ccyy2
Line 1 | The year operand, yy1 , needs expanding,
as intended in the original statement. |
Line 2 | The expanded year operand ccyy1 needs moving into the
result operand, ccyy2 , as intended in the original
statement. |
ccyy2
.
Sentinel processing is the process where a date data item is used to hold a non-date value to flag some condition. Although this is not a problem in its own right, if the data item is fixed assuming that it holds a date, a problem might occur.
These sentinels might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-SentinelValue. To find them:
If these sentinels are not categorized in the worksheet, you can run the tool, Find sentinel values: 000000 999999, which searches the worksheet for data items that might contain a value indicating a special condition, such as start or end-of-file, rather than containing a legitimate date value. The tool then categorizes those data items and their containing statements as R-SentinelValue
Date data items might contain values in complement form. Typically, these fields are used to order date-values for SORT statements or in indexed file keys. The year 2000 fix might have to be adjusted to handle these values correctly.
These date values in complement form might already be identified in the worksheet as a result of running Verify all research. In which case they are categorized as R-1Complement, R-2Complement and so on. To find them:
If these date values in complement form are not categorized in the worksheet, you can run the tool, Find 1s complement, Find 2s complement and so on, which search for data items that might contain date values in complement form. The Find 1s complement tool finds binary data items (COMP or COMP-4) that are initialized with high-values or hexadecimal FF and that are used in SUBTRACT statements. The tool then categorizes those statements and their data items as R-1Complement
A date field that holds the century is often redefined with a subfield that omits the century. The date is set in the subfield with the assumption that the century is constant and is implicitly set.
There are two typical cases, one in which an 8-digit date of the form CCYYMMDD has a 6-digit subfield that omits the century and the other in which a 7-digit date of the form CCYYDDD has a 5-digit subfield that omits the century. In both cases, a MOVE statement that sets the subfield might yield the wrong date because the century is not adjusted.
These MOVEs to subfields might already be identified in the worksheet as a result of running Verify all research. In which case they have a category such as R-5-7Digit. To find them:
If these MOVEs to subfields are not categorized in the worksheet, you can run a tool such as, Find 5- to 7-byte subfield (moves to window), which searches the worksheet for the MOVE statements where a 5-digit data item is moved either to a 7-digit elementary item or to a 5-digit item subordinate to a 7-digit group item. The tool then categorizes those statements and their data items as R-5-7Digit.
Similarly, use Find 4- to 6-byte subfield (moves to window) and so on to search for the MOVEs to subfields
Handling group items that contain elementary items representing the year is in principle the same as handling those elementary items. Since group items are not used in arithmetic, only comparisons offer a potential problem. When group items are compared, if the year is significant the result might be wrong.
if yymmdd-1 >= yymmdd-2 051228 >= 971228 wrong 20051228 >= 19971228 correct
There is an additional problem in that group items are treated as character items and not numeric items. This means the macros handling group items cannot perform arithmetic, such as adding the century onto the year.
Since group items are not numeric, the macros to handle them use MOVE statements rather than arithmetic to determine the century. Consequently the year type that you allocate to a group item has macros designed for handling non-numeric items.
If any subordinate items to a group item are defined as numeric non-display, such as PIC 9(4) COMPUTATIONAL, you cannot treat the group item as PIC X. In this case, you can edit the statement manually, or define a year type for group items like this and write the corresponding macros to handle them.
Example:
In this example, the two compared group items are treated as PIC X(6) even though their subordinate items are numeric (PIC 99).
01 yymmdd-1 03 yy-1 pic 99. 03 mm-1 pic 99. 03 dd-1 pic 99. 0 1 yymmdd-2 03 yy-2 pic 99. 03 mm-2 pic 99. 03 dd-2 pic 99. . . . * If yymmdd-1 > yymmdd-2 *> original code * begin fix move yymmdd-1 to ccyymmdd-1 of ccyymmdd-1 *> expand yymmdd-1 if ccyy-1 of ccyymmdd-1 > 60 move 19 to cc1 of ccyymmdd-1 else move 20 to cc1 of ccyymmdd-1 end-if move yymmdd-2 to ccyymmdd-2 of ccyymmdd-2 *> expand yymmdd-2 if ccyy-2 of ccyymmdd-2 > 60 move 19 to cc2 of ccyymmdd-2 else move 20 to cc2 of ccyymmdd-2 end-if If ccyymmdd-1 > ccyymmdd-2 *> compare group items * end fix
If a subscript is a year and is used for accessing a table of data, problems occur when:
move a(yy-70) to b.
SmartFix handles arithmetic expressions in subscripts in the same way as COMPUTE statements, but does not handle negative or zero subscripts.
To fix zero subscripts, manually edit the code to adjust the subscript to run in sequence from 1 through 99. For example, for the window 1960-2059: for dates in the 1900s, subtract 59 from the subscript, and for dates in the 2000s, add 60 to the subscript.
Year Subscript |
Array of Data |
---|---|
60 - 59 = 1 |
data |
|
|
99 - 59 = 40 |
data |
00 +60 = 60 |
data |
59+ 60 = 99 |
data |
|
|
Problems:
Reference modified operands that hold dates have the same problems as any other date operands when involved in comparisons or arithmetic. They can be fixed in the same ways.
move record-yy-date(8:2) to account-year
However, it is possible to use a date data item as one of the modifiers, although this is a rare construct:
move record(yy: data) to this-year-data move record(start: yy) to accumulated-data
If the reference modifier is a two-digit year, the code fails for the year 2000, as neither modifier can be zero. The COBOL compiler does not fail, but the logic is wrong.
In a similar way to fixing subscripts, adjust the offending reference modifier to run in sequence from 1 through 99. For example, for the window 1960-2059: for dates in the 1900s, subtract 59 from the modifier, and for dates in the 2000s, add 60 to the modifier.
SmartFix does not address any constructs that use keys, which might be dates and whose sequence might be significant. These constructs include:
Copyright © 1999 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names
used herein are protected by international law.
The Worksheet | Fixing Date Problems |