【実務・中級編】DATEおよびDATETIME組み込み関数の西暦2000年問題と推奨代替関数 – PL/Iの基本構文とデータ制御実践ガイド

やあ。今日もまた、古いオンライン画面のチューニングや、夜間バッチのログとにらめっこご苦労様。
メインフレームの心臓部で稼働するPL/Iプログラム、その独特の渋い構文にもだいぶ慣れてきた頃じゃないか?

今日はな、PL/Iにおける「時限爆弾」の話をしよう。
テーマは`DATE`および`DATETIME`組み込み関数の西暦2000年問題(Y2K)、そしてLE/370(Language Environment)準拠の推奨代替関数への移行指針だ。

「おいおい、今更Y2Kかよ。2000年はもうとっくに過ぎたぞ」と思ったそこの君。甘い。
世の中の新規システムはクラウド全盛期かもしれないが、我々が守るべき基幹系メインフレームの深部には、昭和の終わりや平成の初めに書かれた「生きた化石」のようなソースコードが、今なお何百万ステップも眠っているんだ。そして、その一部はいまだに「下2桁の呪縛」を抱えたまま、だましだまし動き続けている。

今回は、現場のベテランとして、なぜ古い`DATE`関数が危険なのか、そして現代のPL/Iプログラマとしてどうコードを書き換えるべきか、実践的なコードを交えて徹底的に叩き込んでやる。心して聞いてくれ。

1. なぜ旧来の `DATE` 関数は「地雷」なのか?

PL/Iの歴史は古い。その中で、システム日付を取得するために長年愛用されてきたのが、組み込み関数の `DATE` だ。

1
DCL TODAY CHAR(6);
TODAY = DATE(); / 結果: “231024” のようなYYMMDD形式 /

何が問題かわかるか?
そう、世紀(CC)が含まれていない点だ。この関数が返す文字列は `YYMMDD` の6桁。
2023年10月24日であれば `”231024″` だが、1923年10月24日であっても同じ `”231024″` が返ってくる。

「いやいや、うちはもう21世紀なんだから、西暦2050年問題を見据えてそろそろやばいのでは…」なんて悠長なことを言っている場合じゃない。金融機関の勘定系や、数十年のスパンで履歴を保持するマスター維持バッチにおいて、この `YYMMDD` をそのままキーにしたり、大小比較や減算(経過日数の計算)に使ったりしているコードを見かけたら、それは今すぐ排除すべき重大な潜在バグ(不具合の爆弾)だ。

コンパイラはエラーを出してくれない。PL/Iは「識別子や予約語の概念が緩やか(キーワードの文脈依存)」な言語だが、組み込み関数についてもプログラマの意図通りに黙々と動いてしまう。だからこそ、我々アーキテクトが手動でコードを浄化していかなければならないんだ。

2. 救世主:Language Environment (LE/370) 準拠の `CEEDATE`

IBMメインフレームのz/OS環境において、この問題を根本から解決するのが LE/370のCallable Services(呼び出し可能サービス)、およびPL/Iに統合された拡張日付/時刻組み込み関数だ。

その中でも、実務で最も堅牢かつ標準的なアプローチが、Lilian日付(1582年10月14日からの経過日数を表す整数値)をベースにした変換処理、あるいは `YYYYMMDD` や `YYYY-MM-DD` といった完全な世紀を含む形式への変換である。

今回は、LE/370のサービスをラップし、PL/Iからスマートに呼び出せる `CEEDATE`(Lilian整数を文字列表現に変換する)や、現在の正確な世紀付き日付を取得するモダンな手法を、実際のバッチプログラムの文脈で見ていこう。

3. 実践:VSAM入出力とONユニットを伴う安全な日付処理プログラム

百聞は一見にしかずだ。
以下に、VSAM(KSDS)のマスターファイルからレコードを読み込み、旧来の `DATE` 関数を排除して、LE/370の機能(または推奨される組み込み関数・ロジック)を用いて安全に日付をハンドリングする、実務標準のPL/Iサンプルコードを示す。

大文字ベースの記述、適切なインデント、そして例外処理を行う `ON` ユニットの組み込み方に注目してほしい。

1
/ ———————————————— /
/ Program Name: DATEMIGM /
/ Description : Y2K対応日付変換およびVSAM更新処理 /
/ ———————————————— /
DATEMIGM: PROC OPTIONS(MAIN);

/ — 宣言部 — /
DCL 1 CUST-REC, / VSAMレコード構造体 /
5 CUST-ID CHAR(8), / 顧客ID /
5 CUST-NAME CHAR(30), / 顧客名 /
5 LAST-UPD-DT CHAR(8), / 最終更新日(YYYYMMDD) /
5 FILLER CHAR(54); / 予備領域 /

DCL MYSKIP COND; / 例外処理用条件名 /
DCL EOF-FLAG CHAR(1) INIT(‘OFF’); / 終了フラグ /

/ LE/370日付計算用変数の定義 /
DCL LILIAN-DATE FIXED BIN(31); / 1582年からの経過日数 /
DCL 01 VDATE-STRUCT, / CEEDATE用引数構造体 /
03 VDATE-LEN FIXED BIN(16) INIT(8),
03 VDATE-STR CHAR(8);
DCL 01 FEEDBACK, / LEフィードバックコード /
03 FB-SEV FIXED BIN(15),
03 FB-MSG-NO FIXED BIN(15),
03 FB-FLAGS CHAR(1),
03 FB-IC FIXED BIN(15);

/ VSAMファイルの定義 (KSDS) /
DCL CUSTFILE FILE RECORD SEQUENTIAL
ENV(VSAM ORGANIZATION(KEYED));

/ — 例外処理(ONユニット)の定義 — /
ON ENDFILE(CUSTFILE)
BEGIN;
EOF-FLAG = ‘ON’;
END;

ON UNDEFINEDFILE(CUSTFILE)
BEGIN;
DISPLAY(‘ ERROR: VSAMファイルが開けません。JCLを確認してください。’);
SIGNAL ERROR;
END;

/ — 処理開始 — /
DISPLAY(‘ DATEMIGM 処理開始 ‘);

/ VSAMファイルを更新モード(UPDATE)でオープン /
OPEN FILE(CUSTFILE) UPDATE;

/ メインループ:全件走査 /
READ FILE(CUSTFILE) INTO(CUST-REC);

DO WHILE (EOF-FLAG = ‘OFF’);

/ 【重要】旧来のDATE()は使用せず、LEサービスで現在日付を取得する /
/ CEELOCT はカレントの日付・時刻をLilian形式等で取得する汎用サービス /
CALL CEELOCT(LILIAN-DATE, , , FEEDBACK);

IF FB-SEV > 0 THEN DO;
DISPLAY(‘ WARNING: 日付取得に失敗しました。フィードバックコード確認’);
/ エラー処理のルーチンへ /
END;
ELSE DO;
/ Lilian日付を ‘YYYYMMDD’ 形式の文字に変換する (Picture ‘YYYYMMDD’) /
/ CEEDATE(Lilian日付, 変換書式ピクチャ, 結果文字列, フィードバック) /
CALL CEEDATE(LILIAN-DATE, ‘YYYYMMDD’, VDATE-STRUCT, FEEDBACK);

IF FB-SEV = 0 THEN DO;
/ 取得した世紀付き日付でレコードの更新日を上書き /
CUST-REC.LAST-UPD-DT = VDATE-STR;

/ レコードの書き換え(REWRITE) /
REWRITE FILE(CUSTFILE) FROM(CUST-REC);
END;
END;

/ 次のレコードを読み込み /
READ FILE(CUSTFILE) INTO(CUST-REC);
END;

/ — 終了処理 — /
CLOSE FILE(CUSTFILE);
DISPLAY(‘ DATEMIGM 正常終了 ‘);

RETURN;

END DATEMIGM;

4. ベテランからのデバッグのコツと実務での移行指針

ソースコードを見てもらったところで、現場で活きる「生きた知見」をいくつか授けよう。

① 既存ソースの全数調査にはクロスリファレンスを使え

まずはコンパイラのクロスリファレンス(XREF)リストを出力させ、ソースコード中のどこで `DATE` という文字列が使われているかを機械的に洗い出せ。
ただし、単なる変数名として `DCL DATE CHAR(6);` のように定義されているケースもある。構文解析の文脈を読み解きながら、組み込み関数の `DATE` を呼び出している箇所を特定するのが第一歩だ。

② ピクチャー変数(PICTURE)の桁数あふれに注意せよ

PL/Iの強みであり恐ろしいところは、`PICTURE` 属性による厳密なデータ編集だ。
`DCL DT_OLD PIC ‘999999’;` から `DCL DT_NEW PIC ‘99999999’;` へ移行する際、これを格納するレコードレイアウト(構造体のオフセット)が変わる。
VSAMのKSDSやマスタファイルで、もし日付項目をキーの一部にしている場合、フィールドの拡張はインデックスの再構築(REPRO等による再ロード)を伴う大規模な影響範囲調査が必要になる。甘く見てはいけない。

③ ON-UNIT と LEフィードバックの組み合わせ

モダンなPL/Iバッチでは、異常系をいかにエレガントに拾うかがプログラマの腕の見せ所だ。
上記のサンプルでも示した通り、LEの呼び出し可能サービスは戻り値ではなく `FEEDBACK`(構造体)でエラーを返す。これを無視して進めると、不正な日付データがマスターを汚染することになる。`ON ERROR` などの条件制御と組み合わせ、異常検知時は即座にジョブを異常終了(U0000や意図的なabend)させる設計を徹底してほしい。

おわりに

レガシーシステムのモダナイゼーションや保守において、「動いているから触らない」というのは最悪の言い訳だ。時限爆弾は、忘れた頃にやってくる(あるいは監査で指摘されて青ざめる)。

今日解説した `CEEDATE` への移行や、世紀を意識したデータ制御は、君が守るべき基幹システムの寿命をさらに10年、20年と延ばすための必須スキルだ。
プライドを持って、美しい、安全なPL/Iコードを書き続けてくれ。期待しているぞ!

タイトルとURLをコピーしました