- Perl: Library for Date and Time

現在の時刻を求める

現在の時刻 $y$m$d$hr$min$sec秒を求める関数、getCurrentTime を考える。

sub getCurrentTime {
 my @currentTime = localtime(time);
 my %timeHash = ("y",0, "m",0, "d",0, "hr",0, "min",0, "sec",0);
 $timeHash{"y"} = $currentTime[5] + 1900;
 $timeHash{"m"} = sprintf("%02d", $currentTime[4]+1);
 $timeHash{"d"} = sprintf("%02d", $currentTime[3]);
 $timeHash{"hr"} = sprintf("%02d", $currentTime[2]);
 $timeHash{"min"} = sprintf("%02d", $currentTime[1]);
 $timeHash{"sec"} = sprintf("%02d", $currentTime[0]);
 return %timeHash;
}

後々の利用を考えて、各値を 0 で左詰めにしてハッシュ %timeHash に格納している。$y に 1900 が足されていること、$m に 1 が足されていることに注意する。プログラムに応じて、リストで書き出しても良いし、特定の様式に加工して書き出すのも良いだろう。

@currentTime には 7つめの項目として、$currentTime[6] も存在する。これは 0〜6 の値を取り、曜日を表す。それぞれの値の意味は「特定の日付の曜日を求める」を参照。

10日前、3時間後などの時刻も求めることができる。その場合は、求める時刻と現在の差分の秒数を time に加減してやる。

10日前なら、localtime(time - (10*24*60*60))

3時間後なら、localtime(time + (3*60*60))

引数に秒数を組み込んで、さらに汎用的な関数にするのも良いだろう。

文字列から日付のハッシュを求める

「1999年 12月 31日 (日)」であるとか、「2000/01/01 12:34:56」であるとか、日付の記述は様々である。これらの文字列 $date から、意味のある日付 $y$m$d$hr$min$sec秒を抜き出す関数、getTimeHash($date) を考える。

$date には、$y, $m, $d, $hr, $min, $sec がこの順番で含まれている必要がある。ある項目以下が全て欠けている場合 (「2004/06」など) は対応できるが、ある項目のみが欠け、以下の項目が詰まっている場合には機能しない。まァ、そんな変な日付の文字列はないだろうが。

sub getTimeHash {
 my ($date) = @_;
 $date =~ /^(\d{4})\D*(\d{0,2})\D*(\d{0,2})\D*(\d{0,2})\D*(\d{0,2})\D*(\d{0,2})/;
 my %timeHash = ("y",$1, "m",$2, "d",$3, "hr",$4, "min",$5, "sec",$6);
 return %timeHash;
}

$y は 4桁、$m 以下は 2桁である必要がある。したがって、「99年 2月 3日 4時 5分 6秒」という表示には対応していない。これは、「20060105」などという、年月日の間に区切り文字が入っていない文字列を処理するためである。

区切り文字があるならば、$y 以下の桁数に条件を付ける必要はなくなる。その場合は 3行目を以下のように書き換える。

$date =~ /^(\d*)\D+(\d*)\D+(\d*)\D+(\d*)\D+(\d*)\D+(\d*)/;

求めた値を 0 で左詰めにする必要があれば、「現在の時刻を求める」で処理したような形で、ハッシュ %timeHash の各値を整形してやれば良い。

特定の月の最大日数を求める

$y$m月の最大日数を求める関数、 getMaxDays を考える。基本的には、各月の最大日数のリストを用意し、それを当てはめるだけである。ただ、$m月が閏月であるかどうかを考慮する必要がでてくる。$y は 4桁である。

したがって、getMaxDays($y, $m) は、

sub getMaxDays {
 my ($y, $m) = @_;
 my @maxDays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
 if ((($y%4 == 0) && ($y%100 != 0)) || ($y%400 == 0)) { $maxDays[1] = 29; }
 return $maxDays[$m-1];
}

$m月が 2月の場合だけ閏年かどうかを計算すれば良さそうな気もするが、むしろそちらの方が処理は複雑になる。なので、何月であっても閏年の判別を行っている。

特定の日付の曜日を求める

$y$m$d日の曜日を求める関数、getWeekDay を考える。特定の日付の曜日を算出するには、ツェラーの公式 (Zeller's fomula) を利用する。$y年 1月は ($y - 1)年 13月、$y年 2月は ($y - 1)年 14月として計算する。$y は 4桁である。また、この公式の有効年は 1583〜3999年である。

$y + int($y/4) - int($y/100) + int($y/400) + int((13*$m+8)/5) + $d) % 7

この式の値が 0 なら日曜、1 なら月曜……、6 なら土曜である。したがって、getWeekDay($y, $m, $d) は、

sub getWeekDay {
 my ($y, $m, $d) = @_;
 if ($m < 3) { $y --; $m += 12; }
 my $day = ($y + int($y/4) - int($y/100) + int($y/400) + int((13*$m+8)/5) + $d) % 7;
 my @days = ("Sun.", "Mon.", "Tue.", "Wed.", "Thu.", "Fri.", "Sat.");
 return $days[$day];
}

好みに応じて、リスト @days の中身を変えれば良い。あるいは、$day の値だけを返して、実際の文字列に変換する関数を別に作っても良い。