こんにちは!メインフレームの底知れぬパワーと、そこに眠るレガシーなコードたちを愛してやまないシステムアーキテクトです。
普段はJavaやC#、あるいは今風のモダンな言語でバリバリ開発をしているあなたにとって、IBMメインフレームの世界、そしてそこで動く「PL/I(ピーエルアイ)」という言語は、どこかタイムカプセルのような、少し近寄りがたい雰囲気を放っているかもしれません。
「変数名にルールはあるの?」
「データ型の宣言がなんだか呪文みたい……」
大丈夫です、怖くありませんよ。JavaやCOBOLといった他の言語を触ったことがある方なら、頭の切り替えさえできればすぐに仲良くなれます。今回は、基幹システムの改修やマイグレーション案件で必ずと言っていいほど直面する「日付」をテーマに、PL/Iの世界へご案内します。
世紀またぎのトラップから、現代的な日付関数の使い方、そしてメインフレームならではの時間の扱い方まで、一緒に優しく紐解いていきましょう!
—
1. PL/Iの変数名って自由すぎる?「予約語を持たない」という哲学
他の言語を学んできた人がPL/Iのソースコードを最初に見たとき、こう驚きます。
「あれ、IFとかDATEとかって、変数名に使っても怒られないの?」
そうなんです。これがPL/Iの最大の特徴の一つであり、初心者をちょっと混乱させるポイントなのですが、PL/Iには厳密な意味での「予約語(Keyword)」がほとんどありません。
例えば、Javaなら `int if = 1;` なんて書こうものならコンパイルエラーで即座に叱られますよね。しかし、PL/Iではコンパイラが前後の文脈(コンテキスト)を空気のように読み取ってくれます。
1
/ 変数名として DATE を使っても、コンパイラは文脈で判断します /
DCL DATE CHAR(8);
DATE = ‘20231024’;
「なんて柔軟なんだ!」と感動する一方で、これをやるとコードを読む人間(特に後任のプログラマ)が混乱するため、実務では「予約語っぽく見える単語を変数名にするのはやめましょうね」というお約束のコーディング規約が存在します。ルールがないからこそ、優しさが求められる――大人の言語ですね。
—
2. タイムトラベラーズ・ハイ!レガシーな `DATE` 関数の罠
さて、本題の日付処理です。基幹システムにおいて「今日の日付を取る」というのは、すべてのバッチ処理の基本中の基本ですよね。
Javaで言えば `LocalDate.now()`、COBOLなら `ACCEPT … FROM DATE` です。では、PL/Iの最も古い世代の関数 `DATE` を見てみましょう。
1
DCL TODAY_OLD CHAR(6);
TODAY_OLD = DATE(); / システムから日付を取得 /
PUT SKIP LIST (‘レガシーDATEの値:’ || TODAY_OLD);
このコードを実行すると、もし今日が2023年10月24日であれば、コンソールにはこう出力されます。
`231024`
……おや? 気付きましたか?
そう、西暦の下2桁しか返ってこないのです。これが、世のエンジニアを震え上がらせた、かの有名な「2000年問題(Y2K)」のレガシーな名残です。
もしこの `231024` をそのまま使って、「1999年10月24日」と比較したらどうなるでしょう? システムは「991024(1999年)」の方が「231024(2023年)」より未来だと勘違いしてしまいます。
JavaやCOBOLの経験がある方なら「いまどき下2桁なんて信じられない!」と思われるかもしれませんが、何十年も動き続けているメインフレームの古いジョブストリームの中には、いまだにこの `DATE` 関数が現役で息を潜めているケースがあります。マイグレーション調査の際は、まずここを疑うのがアーキテクトの第一歩です。
—
3. 救世主登場!現代的な `DATETIME` 関数とピクチャー句
「じゃあ、4桁の西暦が欲しいときはどうすればいいの?」
ご安心ください。モダンなPL/I(Enterprise PL/Iなど)には、しっかりと現代的な関数が用意されています。それが `DATETIME` 関数です。
これを使うと、世紀を含む正確な日時を取得できます。さらに、PL/I特有の強力な武器である「ピクチャー(PICTURE)句」を組み合わせることで、日付のフォーマットを自由自在に操ることができます。
まずは実際のコードを見てみましょう。
1
/ ————————————————– /
/ 現代的なDATETIME関数とピクチャー句を使った日付取得 /
/ ————————————————– /
DCL W_DATETIME CHAR(21); / タイムスタンプ受取用 /
DCL W_FORMATTED_DATE CHAR(10); / 編集後日付 (YYYY-MM-DD) /
/ 1. YYYYMMDDHHMISS999… 形式で現在日時を取得 /
W_DATETIME = DATETIME(‘YYYYMMDDHHMISS99) ;
/ 2. ピクチャー句を使って、データを見やすい形に整形する /
Dcl EDIT_DATE Pic ‘9999-99-99’;
EDIT_DATE = SUBSTR(W_DATETIME, 1, 8); / 年月日部分を切り出し /
PUT SKIP LIST (‘現代風DATETIME(生):’ || W_DATETIME);
PUT SKIP LIST (‘整形された日付:’ || EDIT_DATE);
ここがポイント:ピクチャー(PICTURE)句とは?
COBOLを触ったことがある方なら「あ、あれね!」とピンと来るはずです。PL/Iのピクチャー句は、変数の「見た目の形(型とレイアウト)」を定義するものです。
`Pic ‘9999-99-99’` と書くことで、数値文字が自動的にハイフン区切りの日付文字列として扱われます。データ定義とレイアウトが一体化している、PL/Iの非常にエレガントな機能です。
—
4. メインフレームの奥深い闇?「タイムゾーン」の考慮事項
日付と時刻を扱う上で、もう一つ忘れてはならないのがタイムゾーンです。
Javaなどで開発していると、JVMが自動的にOSのタイムゾーンを解釈してくれたり、`ZonedDateTime` でスマートに世界協定時(UTC)とローカル時間を変換できたりしますよね。
しかし、堅牢なIBMメインフレームの世界では、少し勝手が違います。
メインフレーム(z/OS)のシステム時計は、基本的には基幹系が設置されている地域の時刻(日本であればJSTなど)を刻んでいますが、グローバル展開するシステムや、海外拠点とデータを連携するバッチ処理では、以下の点に注意が必要です。
1. ハードウェアの時刻(ETOD: Extended Time-Of-Day クロック)
メインフレームのハードウェアレベルでは、時間はUTC(協定世界時)ベースのハードウェア・タイマーで厳密に管理されています。
2. PL/Iの `DATETIME` 関数の挙動
PL/Iから呼び出す関数は、基本的にはOS(z/OS)のローカルタイムを返します。しかし、もしLE(Language Environment)のランタイムオプションや、TZ(タイムゾーン)環境変数が適切に設定されていない場合、意図せずUTCの時間が取れてしまったり、サマータイム(DST)の計算で思わぬズレが生じたりすることがあります。
マルチテナント環境やクラウド連携(AWSやAzureへメインフレームのデータを飛ばす近代化プロジェクトなど)において、「あれ? ログのタイムスタンプが9時間ズレているぞ?」というトラブルは、このタイムゾーンの解釈違いからよく起こります。
日付データを他システムへ連携する設計をする際は、必ず「いま取得しているのはローカル時間なのか、それともUTCなのか」をチーム全体で共通認識として持つようにしてくださいね。
—
まとめ:レガシーな世界も、基本を押さえれば怖くない!
今回は、PL/Iにおける日付の扱いと世紀またぎ問題、そしてシステム時刻の取得について解説しました。
- 予約語を持たない自由な構文に最初は戸惑うかもしれませんが、文脈を意識すれば大丈夫。
- 古い `DATE` 関数は西暦下2桁しか返さないので、レガシーコード改修時は要チェック!
- 現代的な `DATETIME` 関数と ピクチャー句 を使えば、安全かつスマートに年月日を扱える。
- 異システム連携では、タイムゾーン(ローカル vs UTC)の意識を忘れずに。
他の言語の経験があるあなたなら、PL/Iの独特な文法も「方言」のようなものだとすぐに気づくはずです。一つひとつの仕様を紐解いていけば、メインフレームは決してブラックボックスではありません。
怖がらずに、一歩ずつモダンなレガシーライフを楽しんでいきましょう!次回の解説もお楽しみに。
