本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはコルクことkolukuさんで、
業務でコードを書くうえで時間は重要な要素です。日付の管理や特定の形式への変換、
本稿は、
Perlにおける時間の基本
本節では、
UNIX時間による時間管理
まずは、
Perlではtime
関数で現在時間を取得できます。
say time; # 1609426800
このときに得られる時間はUNIX時間です。UNIX時間とは、
UNIX時間は、$^T
でも取得できます。
say $^T; # 1609426800
2100年以降での不具合の可能性
UNIX時間は、
閏年は、
- ❶ 西暦が400で割り切れる年は閏年
- ❷ 西暦が100で割り切れる年は平年
- ❸ 西暦が4で割り切れる年は閏年
そのため、
ですが、
2038年問題による不具合の可能性
time
関数で取得しているUNIX時間は、time_
型から取得しています。time_
型は、
これにより、
時刻の表現
ここまで、
time
関数はUNIX時間で返されるため、localtime
関数を用いると、
localtime
関数は、time
関数が返す時刻を、
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday,
$isdst) = localtime;
say $year + 1900; # 2021
localtime
関数をスカラコンテキストのときに扱うと、
say scalar localtime; # Fri Jan 1 00:00:00 2021
また、localtime
関数は引数にUNIX時間を与えることで、time
関数の値を上書きせず、localtime
関数はもとの時間を返します。
# Fri Jan 1 00:00:00 2021
say scalar localtime(1609426800);
タイムゾーンの変換
前述したようにlocaltime
関数は自身のマシンのタイムゾーンの時刻になるため、
ほかのタイムゾーンの時刻に変換するには、
そこで、gmtime
関数があります。この関数は、localtime
関数と同じAPIで扱えます。gmtime
関数をリストコンテキストのときに扱うと、
# 2021-08-01 12:00:00に実行
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday,
$isdst) = gmtime;
say $isdst; # 常に0
指定した秒数の実行停止
指定した秒数の間実行を停止するには、sleep
関数を使います。
say scalar localtime; # Fri Jan 1 00:00:00 2021
sleep 10; # 10秒間、プログラムを実行停止
say scalar localtime; # Fri Jan 1 00:00:10 2021
時間の標準モジュール
localtime
関数やgmtime
関数で時間をある程度は自由に表現できるものの、
本節では、
Time::Piece ──高速な時間演算モジュール
localtime
関数は時間を任意の単位で取り出せるため加工するのに便利ですが、
Time::Piece
モジュールlocaltime
関数で得られていた分や日など単体の時間はもちろん、localtime
関数とgmtime
関数をオーバーライドして、Time::Piece
オブジェクトとして結果が返却されます。
use Time::Piece;
my $t = localtime;
# $t->ymd 2021-01-01 # 年月日
# $t->mdy 01-01-2021 # 月日年
# $t->mdy("/") 01/01/2021
# $t->dmy 01-01-2021 # 日月年
# $t->datetime 2021-01-01T00:00:00 (ISO 8601)
# $t->cdate Fri Jan 01 00:00:00 2021
Time::Piece
モジュールではUNIX時間から任意の時間を得るのではなく、Time::Piece
オブジェクトに変換できます。
use Time::Piece;
my $parse_time = Time::Piece->strptime('2021-01-01T00:00
:00', '%Y-%m-%dT%H:%M:%S');
say $parse_time->ymd; # 2021-01-01
localtime
関数では、
my $t = time;
# 1週間分の秒数を減らす
my $one_week_ago = localtime($t - 60 * 60 * 24 * 7);
say $one_week_ago;
それに対してTime::Piece
モジュールでは、Time::Piece
オブジェクトに対してTime::Seconds
オブジェクトを加算や減算できます。このとき得られるのはTime::Piece
オブジェクトです。
use Time::Piece;
use Time::Seconds;
my $now = localtime;
# ONE_DAYはTime::Secondsで定義されている1日の定数
my $tomorrow = $now + ONE_DAY;
say $tomorrow->ymd; # 2021-01-02
また、Time::Piece
オブジェクトどうしを減算することで時間の差を得られます。このとき得られるのはTime::Seconds
オブジェクトです。
use Time::Piece;
use Time::Seconds;
my $now = localtime;
my $tomorrow = $now + ONE_DAY;
my $diff = $now - $tomorrow;
say $diff->days; # -1
さらに、Time::Piece
オブジェクトどうしで比較できます。
use Time::Piece;
use Time::Seconds;
my $now = localtime;
my $next_year = $now + ONE_YEAR;
if ($now < $next_year) {
say '$next_yearのほうが未来です';
}
Time::Secondsモジュールの定数の罠
Time::Seconds
モジュールでは、
- ONE_
YEAR:365. 24225日 - ONE_
MONTH:ONE_ YEAR / 12
ONE_
は365日と定義されていません。これは、ONE_
は30日よりも10時間ほど多い値になっています。
閏年を考慮せず常に30日として扱いたい場合はONE_
を、LEAP_
を、NON_
を使います。
Time::HiRes ──ミリ秒まで時間を扱うモジュール
UNIX時間は秒数を単位にしているため、
Time::HiRes
モジュールgettimeofday
関数を使うと、
use Time::HiRes qw(gettimeofday);
my ($now, $micro) = gettimeofday;
say scalar gettimeofday; # 1609426800.76897
マイクロ秒までの時間の差は、tv_
関数にgettimeofday
関数で得られた時間を渡すことで得られます。第二引数を省略した場合は現在時間を参照します。
use Time::HiRes qw(gettimeofday tv_interval);
my $t0 = [gettimeofday];
sleep 1;
my $diff = tv_interval($t0);
say scalar $diff; # 1.001211
sleep
関数では秒数で実行を停止しましたが、Time::HiRes
モジュールではusleep
関数にマイクロ秒を渡すことで浮動小数点の秒数でプログラムの実行を停止できます。
use Time::HiRes qw(usleep);
usleep 2_550_000; # 2.55秒停止
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、 NFT