Perl Hackers Hub

第66回 モジュールによる時間の多様な取り扱い(2)

この記事を読むのに必要な時間:およそ 4 分

特定の時刻フォーマットの読み込みと変換を行うモジュール

時間は,年月日や時刻だけでなく,曜日やフォーマットなども自由に表現できるため,読み込みや変換が独自の実装だけでは足りなくなりがちです。ここでは個々のケースに対応するモジュールを見ていきます。

POSIX::strftime ─⁠─任意の時刻フォーマットへの変換モジュール

単に現在時間を任意の時刻フォーマットに変換するためにTime::PieceモジュールやDatetimeモジュールをuseすることは,目的に対してモジュールが大きすぎます。

POSIX::strftime(執筆時点のバージョンは1.94)に変換したいフォーマットとlocaltime関数を渡すことで,任意の時刻文字列に変換できます。localtime関数のリストで返される結果と同じ順で直接指定することでも,任意の変換が行えます。

use POSIX qw(strftime);
my $now_string = strftime "%a %b %e %H:%M:%S %Y", localt
ime;

Time::Local::timelocal_posix ─⁠─UNIX時間に戻すモジュール

同様に,Time::PieceモジュールやDatetimeモジュールは,単に任意の時間をUNIX時間に変換する目的では大げさです。

Time::Local::timelocal_posix(執筆時点のバージョンは1.30)に変換したい時間のリストを渡すことで,UNIX時間に変換できます。$month$yearは,localtime関数と同じ0~11の範囲と1900を引いた値をTime::Local::timelocal_posixに渡します。

use Time::Local qw(timelocal_posix);
my ($sec, $min, $hour, $mday, $month, $year) = (0, 0, 0, 1, 0, 121);
my $epoch = timelocal_posix($sec, $min, $hour, $mday, $month, $year);

HTTP::Date::parse_date ─⁠─ 日付変換ルーチンモジュール

文字列の日付は,HTTPフォーマットやスカラコンテキストのlocaltime関数などさまざまな種類があり,変換が複雑になりがちです。

HTTP::Date::parse_date(執筆時点のバージョンは6.05)に変換したい日付文字列を渡すことで,時間情報をリストに変換できます。また,スカラコンテキストの場合はタイムゾーンの情報を含めた「YYYY-MMDD hh:mm:ss TZ」形式で返されます。

use HTTP::Date qw(parse_date);
my ($year, $month, $day, $hour, $min, $sec, $tz) = parse_date('Fri, 01 Jan 2021 00:00:00 GMT');
my $parsed_time = parse_date('Fri, 01 Jan 2021 00:00:00 GMT');
say $parsed_time;

同類のモジュールとして,変換結果をDateTimeオブジェクトにするDateTime::Format::HTTPモジュールなどもあります。

Time::Piece::MySQL ─⁠─Time::Piece用MySQL日付モジュール

MySQLで時間を扱うのは少々大変です。たとえば,DATETIME型とTIMESTAMP型は,同じフォーマットでもサポートされている時間の範囲が異なります。また,DATE型やDATETIME型,TIMESTAMP型は,無効な値を入力された場合は年月日に00を含むゼロ値に変換されます。

Time::Pieceモジュールの代わりにTime::Piece::MySQLモジュール(執筆時点のバージョンは0.06)useすることで,Time::PieceオブジェクトとしてMySQLの時刻フォーマットの相互変換がメソッドで可能になります。

use Time::Piece::MySQL;
my $now = localtime;
# DATETIME型への変換
say $now->mysql_datetime;
my $mysql_datetime = '2021-01-01 00:00:00';
# DATETIME型からの変換
my $time = Time::Piece->from_mysql_datetime($mysql_datetime);

DateTime::Format::MySQL ─⁠─DateTime用MySQL日付モジュール

DateTimeモジュールでもMySQLの相互変換が行えます。DateTimeモジュールの代わりにDateTime::Format::MySQLモジュール(執筆時点のバージョンは0.06)useすることで利用できます。

use DateTime::Format::MySQL;
# DATETIME型からの変換
my $dt = DateTime::Format::MySQL->parse_datetime('2021-01-01 00:00:00');
# DATETIME型への変換
say DateTime::Format::MySQL->format_datetime($dt);

Test::MockTime ─⁠─ユニットテストでの時間固定モジュール

ユニットテストでプログラムの実行日時を変えたいときがあります。たとえば特定の時間になるとタイムセールが始まるコードがマシンの時間に依存していた場合,テストをするたびにマシンの時間を変えるわけにもいきません。Test::MockTimeモジュール(執筆時点のバージョンは0.17)を使うと,time関数が返すUNIX時間を修正してテストを行えます。

set_fixed_time関数にUNIX時間を渡すと,time関数の戻り値が固定化されます。日時を文字列で渡す場合でも,フォーマットを指定することでその時間に固定化されます。もとの時間に戻す場合にはrestore_time関数を呼び出すだけです。

use Test::MockTime qw(set_fixed_time restore_time);
set_fixed_time(1609426800);
set_fixed_time('2021-01-01 00:00:00', '%Y-%m-%d %H:%M:%S');
restore_time();

逆に,経過時間を記録するなどテストの実行中は時間が動き続けてほしいケースもあります。この場合は,set_absolute_time関数に値を渡すか,set_relative_time関数で現在時間から見た秒数を指定して,過去や未来に時間を移動できます。

use Test::MockTime qw(set_absolute_time set_relative_time restore_time);
set_absolute_time(0);
restore_time();
set_relative_time(-600); # 600秒前に時間を戻す
restore_time();

まとめ

本稿では,時間を取り扱うさまざまなPerlモジュールを見てきました。もしPerlでの時間の取り扱いに悩んでいる場合は,本稿がモジュール選択の指針になれば幸いです。

さて,次回の執筆者は菅井茂樹さんで,テーマは「GitHub APIを使ってみよう」です。お楽しみに。

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.125

2021年10月23日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-12435-9

  • 特集1
    作って学ぶプログラミング言語のしくみ
    インタプリタ,構文解析器,文法
  • 特集2
    GraphQL完全ガイド
    RESTの先へ! フロントエンドに最適化されたAPI
  • 特集3
    速習DynamoDB
    AWSフルマネージドNoSQLの探求

著者プロフィール

koluku

北海道生まれ。

2020年に面白法人カヤックに入社し,現在はサーバーサイドアプリケーションを開発している。Webとゲームが好きなひよっこエンジニア。

Twitter:@koluku
URL:https://koluku.com