時間

■ 現在の日付・時刻を得る(time, gmtime, localtime)

◆ 現在の日付・時刻を得る

time() は、グリニッジ標準時 1970年1月1日0時0分0秒を 0 とした現時刻の秒数を返します。gmtime()localtime() はこの時刻を年、月、日、時、分、秒に分解します。gmtime() はグリニッジ標準時における値を、localtime() は現地時刻の値を返します。

$ENV{'TZ'} = "JST-9";
@wdays = ( "日", "月", "火", "水", "木", "金", "土");

$time = time();
($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime($time);
printf("%04d/%02d/%02d %02d:%02d:%02d(%s)\n",
    $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $wdays[$wday]);

実行結果は次のようになります。

2002/02/11 01:13:59(月)
◆ 年と月の注意

$year には 1900 年からの差分が返されます。また、$mon には月から 1 を引いた値(0~11)が返されますので注意してください。

◆ タイムゾーン

プログラムの最初の方で $ENV{'TZ'} を設定しておかないと、時間帯が狂うことがあります。"JST-9" は「日本時間、標準時から -9 時間のずれ」を意味しています。例えば、アメリカ東部標準時間(EST)での時刻を求めるには次のようにします。

$ENV{'TZ'} = "EST+5";
@wdays = ( "日", "月", "火", "水", "木", "金", "土");

$time = time();
($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime($time);
printf("%04d/%02d/%02d %02d:%02d:%02d(%s)\n",
    $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $wdays[$wday]);

ただし、システムによっては、環境変数 TZ の値を参照しないものもあるようです。

■ ファイルの最終更新時刻を得る

◆ ファイルの作成日付を得る

stat()lstat() を用いて、ファイルの最終更新時刻を得ることができます。返される時間は 1970年1月1日からの秒数なので、localtime() を用いて年月日時分秒に分解します。

$ENV{'TZ'} = "JST-9";
@wdays = ( "日", "月", "火", "水", "木", "金", "土");

$mtime = (stat("file.txt"))[9];
($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime($mtime);
printf("%04d/%02d/%02d %02d:%02d:%02d(%s)\n",
    $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $wdays[$wday]);

実行結果は次のようになります。

2002/02/11 01:16:50(月)

■ 閏年かどうか調べる

◆ 閏年かどうか調べる

閏年(うるうどし)は西暦が4で割りきれる年です。ただし、100で割りきれる年は閏年ではありません。ただし、400で割りきれるときは閏年となります。

sub IsLeapYear {
    local($year) = @_;
    if (($year % 400) == 0) {
        return 1;
    }
    if (($year % 100) == 0) {
        return 0;
    }
    if (($year % 4) == 0) {
        return 1;
    }
    return 0;
}

for ($year = 1998; $year < 2002; $year++) {
    if (IsLeapYear($year)) {
        print "$year年は閏年です。\n";
    } else {
        print "$year年は閏年ではありません。\n";
    }
}

実行結果は次のようになります。2000年は 100でも割り切れますが、400でも割り切れるので閏年となります。

1998年は閏年ではありません。
1999年は閏年ではありません。
2000年は閏年です。
2001年は閏年ではありません。

■ 1970年1月1日からの秒数を求める

◆ Time::Local の timelocal を用いる例

localtime() とは逆に、現地時刻の年、月、日、時、分、秒から、グリニッジ標準時 1970年1月1日0時0分0秒からの秒数を求めるには、Time::Local モジュールの timelocal() を用います。

use Time::Local;

$year = 1999; $mon = 12; $mday = 31;      # 1999年12月31日
$hour = 23; $min = 59; $sec = 59;         # 23時59分59秒

$time = timelocal($sec, $min, $hour, $mday, $mon - 1, $year - 1900);
print "$time\n";

結果は次のようになります。

946652399

$year は 1900年からの差分、$mon は 0~11 の値で指定するので注意してください。

◆ POSIX の mktime を用いる例

POSIX モジュールの mktime() を用いても同じことができます。

use POSIX;

$year = 1999; $mon = 12; $mday = 31;      # 1999年12月31日
$hour = 23; $min = 59; $sec = 59;         # 23時59分59秒

$time = mktime($sec, $min, $hour, $mday, $mon - 1, $year - 1900);
print "$time\n";

結果は次のようになります。

946652399

■ 時刻の差分を求める

◆ 時刻の差分を求める

1992年4月11日12時34分56秒は、2002年12月31日19時45分12秒の何日と何時間前でしょうか。このような時刻の差分を計算する場合、両者を一度1970年1月1日からの秒数に変換し、この差分を求めることで計算できます。

use Time::Local;

# 両者を一度 1970/01/01 00:00:00 からの秒数に変換する
$t1 = timelocal(56, 34, 12, 11, 4 - 1, 1992 - 1900);
$t2 = timelocal(12, 45, 19, 31, 12 - 1, 2002 - 1900);

$diff = $t2 - $t1;                  # 秒数の差分を求める

$days = int($diff / (3600 * 24));   # 日数を求める
$diff %= (3600 * 24);
$hours = int($diff / 3600);         # 時間を求める
$diff %= 3600;
$minutes = int($diff / 60);         # 分を求める
$diff %= 60;
$secs = $diff;                      # 秒数を求める

print "$days日$hours時間$minutes分$secs秒前です。\n";

結果は次のようになります。

3916日7時間10分16秒前です。

■ 指定した日付の曜日を求める

◆ 指定した日付の曜日を求める

Zellerの公式 を用いて、指定した日付の曜日を求めます。0 が日曜日、6 が土曜日になります。これは、1583年~3999年までの間で有効な公式だそうです。前出の mktime() や timelocal() を用いて1970年からの秒数を求め、これを localtime() にかけても算出できます。

@wdays = ( "日", "月", "火", "水", "木", "金", "土");

sub GetDate {
    local($yy, $mm, $dd) = @_;
    if (($mm == 1) || ($mm == 2)) { $yy--; $mm += 12; }
    return ($yy + int($yy / 4) - int($yy / 100) + int($yy / 400)
               + int(2.6 * $mm + 1.6) + $dd) % 7;
}

print "2001/12/31 は" . $wdays[GetDate(2001, 12, 31)] . "曜日です\n";

■ 指定した時間待つ(sleep, select)

◆ sleep()を用いる方法

sleep() は指定した秒数だけ処理をスリープ(中断)して待ちます。例では、"A" を 1秒毎に10個書き出します。

for ($i = 0; $i < 10; $i++) {
    print "A\n";
    sleep(1);
}
◆ select()を用いる方法

select() を用いることにより、待ち時間をミリ秒の単位で指定することができます。例では、"B" を 0.5秒毎に 10個書き出します。

for ($i = 0; $i < 10; $i++) {
    print "B\n";
    select(undef, undef, undef, 0.5);
}

■ プロセスが消費した時間を調べる(times)

◆ プロセスが消費した時間を調べる

times() は、プロセスや子プロセスが消費したユーザCPU時間とシステムCPU時間を秒単位で返します。

($user1, $system1, $c_user1, $c_system1) = times();
for ($i = 0; $i < 10; $i++) {
    system("netstat -a");
}
($user2, $system2, $c_user2, $c_system2) = times();
printf("ユーザ時間:%5.3f秒\n", $user2 - $user1);
printf("システム時間:%5.3f秒\n", $system2 - $system1);
printf("子プロセスユーザ時間:%5.3f秒\n", $c_user2 - $c_user1);
printf("子プロセスシステム時間:%5.3f秒\n", $c_system2 - $c_system1);

実行結果は次のようになります。(結果表示部分のみ)

ユーザ時間:0.016秒
システム時間:0.016秒
子プロセスユーザ時間:0.000秒
子プロセスシステム時間:0.000秒

この値を測定することにより、プロセスがどのくらい、CPU に負荷をかけているかを計測することが可能になり、システム高速化検討の目安となります。

■ パフォーマンスを調べる

◆ パフォーマンスを調べる

stat() と lstat() はどちらの方がどのくらいパフォーマンスがよいでしょうか。stat() や lstat() の処理時間を測定できればよいのですが、処理の前後で time() を取得して差分をとっても、大抵が 0 秒となってうまく計測できません。この場合、処理を繰り返して差分が発生する確率を求めることにより、パフォーマンスの推定を可能にする方法が考えられます。

open(IN, "data.txt");
for ($i = 0; $i < 10000; $i++) {

    $t1 = time();
    lstat(IN);            # 処理1
    $t2 = time();
    stat("stat.txt");     # 処理2
    $t3 = time();

    $tt1 += ($t2 - $t1);  # 処理1に要した時間の合計
    $tt2 += ($t3 - $t2);  # 処理2に要した時間の合計
}
close(IN);

print "処理1 = $tt1\n";
print "処理2 = $tt2\n";

実行結果の例は例えば次のようになります。

処理1 = 2
処理2 = 16

結果からは、処理1(lstat)よりも処理2(stat)の方が、8倍も遅いことが分ります。結果に差異が現れない場合は、ループの回数(例では 10000)を増やしてみてください。


Copyright (C) 2002 杜甫々