†REXXのどろぬま†

日付と時刻の扱い

作成開始日
最終更新日

REXXでファイルの更新判断やソートを行う場合、タイムスタンプで新旧比較を行う事が多い。その場合必要なのは、二つの日付の新旧の判断だけで、日付計算は不要である。また、REXXでは文字列をそのまま大小比較可能(辞書順)なので、日時情報をわざわざ数値に変換する必要はない。ただし、当然だが、比較する日時情報のフォーマットは統一されていなければならない。REXXではこれが厄介なのだ。

標準形式 DATE('Ordered') TIME('Normal')(yy/mm/dd hh:mn:ss)

最も一般的な表記方法。可読性が高く、秒単位まで扱える。ファイルの更新日時の判断などには非常に便利。さらに、TIME('Long')であれば1/100秒まで取得できる。年表記が2桁なので2000年以前の日付に関しては細工が必要だが、一般的には最も扱い易い形式。すべてがこの形式で統一されていれば楽なのだが、残念ながら、直接この形式で日時情報が取得できる関数は他に存在しない。全部表記がバラバラなのだ…orz

と言うことで、プログラム内部では日時情報をさまざまな形式で処理することになるが、データとして外部ファイルに保存する場合は、この形式に統一することを推奨。可読性が高いのは大きなメリットだし、データの形式を統一しておくのは、汎用性の観点からも重要。各プログラムで読み込んでから任意の形式に変換する方が良い。

SysFileTreeのT形式(yy/mm/dd/hh/mn)

SysFileTreeはファイルの各種情報を取得することができるが、デフォルトのタイムスタンプは《日付はmm/dd/yy,時刻はam/pm表記》と比較には全く適さない形式。しかし、Tオプションを付けて表示させると、yy/mm/dd/hh/mnという、ソートに便利な形式で取得できる。

確かにこの表記は便利で、何等加工をしなくても、文字列としてそのままソート可能なのだが、何しろ可読性に難がある。時刻の区切りが「/」では混乱する。しかも、分単位までしか扱えないので、ファイルの更新判断の場合などにはけっこう不便を感じる。決定版にはならない。

このT形式を標準形式に近い形式(秒なし)に変換するには、次のようにする。T形式で取得した日時情報を表示するときは、この方が良いだろう。

dt='yy/mm/dd/hh/mn'
SAY LEFT(dt,8)' 'TRANSLATE(RIGHT(dt,5),':','/')
/* yy/mm/dd hh:mn 形式になる*/

STREAMのquery datetime形式(mm-dd-yy hh:mn:ss)

STREAM(file,'O','query datetime')で得られるタイムスタンプは標準形式に近く、しかも時刻が秒単位まで取得できる。日付の並びは米国式(mm-dd-yy)だが、数字の桁が揃っているので、比較的簡単に標準形式に変換できる。かなり扱いやすい形式と言える。

SysFileTreeのデフォルト形式のタイムスタンプは、月が一桁の場合に先行0が付かないため桁が揃わない。たとえば、「12/20/23」と「3/15/24」みたいな感じになる。

dt= STREAM('myfile.txt','c','query datetime')
PARSE VAR dt mm '-' dd '-' yy'  'tm
SAY  yy'/'mm'/'dd tm
/* 実行結果の例 25/05/06 19:11:16 */
ただし、このフォーマットには少し厄介な問題がある。日付と時刻の間にスペースが2つ入っているため、PARSE時にはyyとtmの間に明示的に'  '(空白2つ)を指定しなければならない。そうしないと、時刻の頭に余分なスペースが残ってしまう。

通常、ホワイトスペースはいくつあっても1つ扱いのはず。なので、わざわざ空白2個を明示的に指定する必要はないと思うのだが、実際には上記のようにしないと上手く機能しない。しかも、この現象には再現性がある。後述のDIRコマンドのファイル名取得の際にも発生する。

と言うことで、標準形式に最も近く、秒単位まで扱える便利な形式ではあるが、わざわざファイルごとにqueryしないと取得できないのは、生理的にちょっと抵抗がある。SysFileTreeやDIRコマンドのように、1コマンドで一括取得できる情報をわざわざ…気のせいだとは判っているが、細かくちまちまつつかれると、SSDの寿命が縮まるんじゃないか?的な…厭だ嫌いだはしょうがない(^^;

DIRコマンドのタイムスタンプ(yy-mm-dd hh:mn)

DIRコマンドを使えば、SysFileTreeと同じように、タイムスタンプ情報を一括取得できる。ただし、外部コマンドなのでコマンドラインで実行して、実行結果をファイルにリダイレクトする等の処理が必要。タイムスタンプの形式は「yy-mm-dd」と「hh:mn」と、標準形式にかなり近い。秒単位はないが、「-」を「/」に置換して、日付と時間を並べれば簡単に準標準形式になる。
st='25-05-05  23:05       8264           0  ftp.htm' /* DIRの実行結果の1行 */
PARSE VAR st dt tm sz ea fn
dt=TRANSLATE(dt,'/','-')
say dt tm
/* 25-05-05 23:05 と表示される*/
で、まあ、これはこれで良いのだが、当然このタイムスタンプがどのファイルのものか判らないと意味がないので、出力行からファイル名も取得する。ちなみに、出力行は「日付、時間、サイズ、EAサイズ、ファイル名」の順で並んでいる。なので、最後のファイル名を取り出せば問題ない…と思いきや、変なことが起きる。
st='25-05-05  23:05       8264           0  ftp.htm' /* DIRの実行結果の1行 */
PARSE VAR st dt tm sz ea fn
say dt		/*25-05-05*/
say tm		/*23:05*/
say sz		/*8264*/
say ea		/*0*/
say fn		/* ftp.htm ←ここだけなぜか先頭にスペースが…*/
ファイル名だけ頭に余分なスペースがくっついてしまう。query datetimeの時と同じ奇怪な現象である。これについては【別項参照】

通日と通秒 DATE('Basedate') TIME('Seconds')

今までは、単に日付の新旧判断をする場合を考えてきたが、これらのタイムスタンプは日付計算には使えない。たとえば、「今日から100日前は何月何日?」みたいなことは全く判らない。こうした日付計算に適しているのが通日と言う概念。REXXではDATE('Basedate')で紀元元年元日(0001/01/01)を起点とした通日を求めることができる。したがって、今から100日前が知りたければ、通日の値から100を引いて日付表記に戻せば良いのだが…

この紀元元年元日はグレゴリオ暦によったもの。したがって、1582年以前の歴史的な出来事に関しては、通日と日付の関係が狂う可能性が高い。

残念ながら、REXXには通日を日付に変換する関数は用意されていない。任意の日付を通日に変換することもできない(一時的に時計を狂わせると言う超邪道な方法もないではないが)。日付計算には月の大小があり、閏年があり、閏年の例外の年もあるので、単純な割り算・掛け算では解決しない。通日計算にはツェラーの公式と言うのがあるそうだから、それを利用して自前の関数を作ると言う方法もあるが…そんなに難しくはなさそうだが…

一方、午前0時を起点とした通秒の方は扱いが楽。時刻←→通秒の変換は、60で割ったり掛けたりすれば簡単にできる。今から95分後は何時何分、なんてのも割合簡単に求められる(まあ、閏秒なんてのもあるけど)。なお、現在の通秒はTIME('Seconds')で取得できる。


【REXXのどろぬま目次】 【ホーム】