メニューボタン
IBMi海外記事2026.07.22

DateTimeに関する経験則

Ted Holt 著

私は優れたプログラマーではありません。私はそこそこのプログラマーであるに過ぎず、トラブルに巻き込まれないようにする方法を見つけてきただけです。私はちょっとした経験則を数多く活用しており、私や私の顧客が都合の悪いタイミングでギョッとさせられるのをそれらが防いでくれています。ここでは、これまでの業務で役に立ったdatetimeデータに関する経験則を紹介しようと思います。

私がRPGプログラムで初めて使用した日付フィールドは、MMDDYYフォーマットの6桁のゾーン10進数値でした。これは、昔、私が地域の職業技術センターで受講していたクラスのプログラミング課題の一部でした。私のデータ処理に関する初期段階の理解では、日付は、このようにしてファイル システムに保存されるものなのだという程度でした。

仕事に就いて数年後、この日付フォーマットが絶えず引き起こす問題にうんざりするようになりました。特に、#GSORTシステム ユーティリティを使用してS/36ファイルからレコードを選択およびソートするときでした。私は、YYMMDDフォーマットで日付を保存する方法に気付きます(Y2Kは、まだ数年先のことでした)。

その後の数十年間で、私はdatetimeデータが数多くのフォーマットで保存されているのを目にしてきましたが、私に言わせれば、最近のトランザクショナルなdatetimeデータを保存するための唯一の適切な方法は、SQLで定義される表のDATEおよびTIMESTAMP列にあります。

「TIMEデータ タイプについてはどうでしょうか」と、皆さんが尋ねる声が聞こえてきそうです。私が承知している、単独のTIME列の唯一の適切な活用例は、たとえば列車または航空便の時刻表など、定期的なスケジュールの場合です。もちろん、他にも用途があるでしょう。私はこれまで、本番作業向けの表定義でTIME列を定義したことはなかったと思います。

時刻と日付が関連付けられている場合は、別々のDATEおよびTIME列ではなく、TIMESTAMP列をお勧めしています。日付と時刻が一緒になって歴史上のある1つの時点を表すので、単一のTIMESTAMP列を定義するのが合理的だと思いますが、また、TIMESTAMPは、行選択の手助けもしてくれます。

たとえば、ある日の午後6時00分から翌日の午前6時00分までのすべての行を選択したいとします。日付と時刻が別々の列にある場合、次のように、TIMESTAMP関数を使用してそれらを結合する必要があります。

select * from SomeTable as t
 where timestamp(t.DateCreated, t.TimeCreated) 
 between '2026-07-04-18.00.00' and '2026-07-05-06.00.00';

TIMESTAMPの日付と時刻を結合する機能、すなわち、私がRPGの%TIMESTAMP関数にもあったらよいのにと思う機能に私は敬服しています。しかし、私は行選択式の左側に関数を置かないようにしています。それがパフォーマンスを低下させることが多いからです。そもそもDateCreatedとTimeCreatedが1つのWhenCreated列であったとしたら、TIMESTAMP関数を必要としないのですが。

select * from SomeTable as t
 where t.WhenCreated
 between '2026-07-04-18.00.00' and '2026-07-05-06.00.00'; 

そのため、ほとんどのアプリケーションで使用するのは、DATEおよびTIMESTAMPデータ タイプということになります。通常はDATEで十分です。医者や税務署、銀行や他の金利の高い金融機関、私の子供たち、さらには世界中のサイバーテロリストまでも、皆、私の誕生日を知っていますが、これまでに誰一人として私が何時に生まれたのか尋ねてきたことはありません。時刻がどうでもよいときは、DATE列の使用をお勧めします。

あとはTIMESTAMPです。デフォルトでは、TIMESTAMP(0)をお勧めしています。通常は、秒未満の端数は必要とされないからです。引数なしのTIMESTAMPは、小数6桁(マイクロ秒)で列を定義します。必要な場合は、最大で12桁の小数を定義することができます。

秒未満の端数は、本当に必要であれば使用すればよいのですが、そこにある理由が、誰かにそれらを省こうとするだけの配慮がなかっただけという場合には、照会が失敗する原因になることもあります。たとえば、次のようなデータ セットがあるとします。

Key   WhenCreated
---   --------------------------
1     2026-07-04 08:00:00.123456
2     2026-07-04 08:00:35.222446
3     2026-07-04 08:01:10.306599
4     2026-07-04 08:23:59.000000
5     2026-07-04 08:23:59.227751
6     2026-07-04 08:23:59.999999

次の照会によってどの行が返されるでしょうか。

select * from SomeTable
 where WhenCreated 
  between '2026-07-04-08.01.00' and '2026-07-04-08.23.59'

答えはもちろん、この照会は行3と行4を返すということになります。マイクロ秒があることで、行5および行6が除外されました。このような照会が不正確な結果セットを返す可能性は低いですが、私はどんなことも運任せにしないように最善を尽くします。

秒未満の端数を含むタイムスタンプの照会で行き詰まった場合、対処方法は少なくとも2つあります。次のように、式の右側に秒未満の端数を含めることができます。

select * from SomeTable
 where WhenCreated
  between '2026-07-04-08.01.00.000000'
      and '2026-07-04-08.23.59.999999';

あるいは、式の左側で、タイムスタンプ切り捨て関数(TRUNC_TIMESTAMP)を使用することもできます。

select * from SomeTable
 where trunc_timestamp(WhenCreated, 'SS')
  between '2026-07-04-08.01.00'
      and '2026-07-04-08.23.59';

確かに、ここでは式の左側に関数を置いています。なるべく避けるようにしているのですが、必要なときはそうします。

TRUNC_TIMESTAMPを使用すると、任意のレベルの精度を選択することができます。前述の例では、秒数(「SS」)未満をすべて切り捨てました。以下は、1か月間のすべての注文を取得するための1つの方法です。このケースでは2026年7月です。

select * from qtemp.temp01
 where trunc_timestamp(WhenCreated, 'MM') = '2026-07-01';

TRUNC_TIMESTAMPは、指定した精度に満たないすべての値をそれより小さい値に設定します。つまり、月および日の場合は01となり、時刻の場合は、秒未満の端数を含め、すべての部分で0となります。datetimeリテラル値の時刻部分を指定する必要はありませんでした。タイムスタンプの時刻部分のデフォルトは、すべて0であるからです。

よく似た関数であるROUND_TIMESTAMPは、必要とされる精度に従って、最も近い単位に丸めます。たとえば、分の単位へ丸める場合、30秒以上の値は、その次の分数の一部として処理されます。

必要な場合は、行選択式の左側で他の関数を使用することもできます。

select . . . where date(WhenCreated) = '2026-07-04';
select . . . where time(WhenCreated) = '08.00.00';
select . . . where year(WhenCreated) = 2026;
select . . . where month(WhenCreated) = 7;
select . . . where day(WhenCreated) = 5;
select . . . where hour(WhenCreated) = 12;
select . . . where minute(WhenCreated) = 23;
select . . . where second(WhenCreated) = 59;
select . . . where microsecond(WhenCreated) = 227751;

これらの経験則は、次のようにまとめることができるかもしれません。

  1. DDSではなく、SQLを使用して物理ファイルを定義する。
  2. 時刻がどうでもよい場合は、DATEを使用する。
  3. 単一の時点には、別々のDATEおよびTIME列ではなく、1つのTIMESTAMP列を使用する。
  4. デフォルトで、タイムスタンプをTIMESTAMP(0)として定義する。秒未満の端数は必要な場合にのみ使用する。

私は今もなお、英字および数字フィールドに日付と時刻を格納するデータベース ファイルで作業していますが、私の好みは断然、datetimeデータ タイプを使用することです。主な理由として、システムが無効な日付および時刻値を拒絶するからです。強力なデータ タイプは、データベースの完全性を向上させます。

あわせて読みたい記事

PAGE TOP