モダンPerlの世界へようこそ

第15回 DateTime:APIの標準化をめざして

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

DateTimeプロジェクト

そのような混乱にいちおうの終止符を打ったのが,デイヴ・ロルスキー(Dave Rolsky)氏が2003年初頭に始めたDateTimeプロジェクトでした。

このプロジェクトについては2006年のYAPC::Asiaでも紹介がありましたし(当時のスライド⁠,さまざまなところで推薦されてきたのですでに使われている方も多いと思いますが,氏は,話し合うだけでは現状の解決にはならないので,既存モジュールの作者を無理に説得するのはやめて,DateTimeというしがらみのない新しい名前空間を計画的に使おうと呼びかけたうえで,前述のTime::Pieceや,不完全ながらもタイムゾーンのサポートを始めていたData::ICalのコードを下地に,やりとりの基本となる(日時を保存しておく)ベースクラスのAPIを固め,その後,既存のコードを大いに参考にしながら,各種のカレンダーや書式,計算といった派生モジュールの対応を行っていく方針を打ち出します。

また,過去や未来のカレンダーにも対応する必要性から,従来データ交換に使われてきたOSのエポック時の制約からの脱却が計られたほか,Time::Pieceと同時期にコア入りしたTime::HiResなどの存在も考慮して,1秒以下の時間への対応なども打ち出されます。

基本的な使い方

とはいえ,ごく表面的なレベルでは,DateTimeの使い方はTime::Pieceなどと大差ありません。

use strict;
use warnings;
use DateTime;

my $dt = DateTime->now;
print $dt->year, "\n";
print $dt->strftime('%Y-%m-%d %H:%M:%S'), "\n";

明示的にタイムゾーンを指定すれば(ふつうは指定します)そのタイムゾーンにあわせた時刻が表示されます。

print DateTime->now(time_zone => 'Asia/Tokyo'), "\n";

特定のエポック時を指定したり,別のDateTimeオブジェクトを利用して初期化することもできます。

my $dt     = DateTime->from_epoch(epoch => time + 3000);
my $new_dt = DateTime->from_object(object => $dt);

後者の例は,単独では価値がわかりにくいですが,DateTime系のモジュールは種類によって特定のAPIを実装することが求められているため,このような汎用的なAPIを覚えておくと,拡張モジュールを利用してほかの表記をしたくなったときに便利です。

use strict;
use warnings;
use DateTime;
use DateTime::Calendar::Japanese;

my $dt = DateTime->new(year => 1876);
my $ja = DateTime::Calendar::Japanese->from_object(object => $dt);
printf "%s (%d-%d)\n",
    $ja->era->id, $ja->era->start->year, $ja->era->end->year;

もっとも,かならずしもすべてがハッシュ渡しではなく,場合によっては文字列やオブジェクト単独で渡す場合もあります。

use strict;
use warnings;
use DateTime;
use DateTime::Format::MySQL;

my $dt = DateTime->now(time_zone => 'Asia/Tokyo');
print DateTime::Format::MySQL->format_datetime($dt);

カレンダーを出力するときに便利なイテレータもあります。

use strict;
use warnings;
use DateTime;
use DateTime::Event::Recurrence;

my $recur = DateTime::Event::Recurrence->daily;

my $dt = DateTime->new(year => 2009, month => 9);
while ($dt->month == 9) {
    print $dt->date, "\n";
    $dt = $recur->next($dt);
}

なお,渡せるキー・値の組み合わせには制約がついている場合があります。ありえない値を渡した場合などはエラーで死にますので,必要に応じてエラーをトラップしなければなりません(詳しくはそれぞれのPODをご確認ください⁠⁠。

# 万一$monthの値が13などになった場合はエラーで死にます
my $dt = eval { DateTime->new(year => $year, month => $month) };
$dt = DateTime->now if $@;

同梱のDateTime::Durationの助けを借りれば加減算を行うこともできます。演算子を使う計算だけでなく,メソッドを使ってより明示的な計算を行うこともできるようになっています。

my $yapc  = DateTime->new(year => 2009, month => 9, day => 10);
my $today = DateTime->today;
my $delta = $yapc - $today;
print $delta->delta_days, "\n";

print DateTime->now->add(days => 5), "\n";

DateTimeプロジェクトに対する反応

長らく待ち望まれてきた共通APIへの反応はおおむね好意的なものでした。2003年だけで19人の作者が50個の関連モジュールをリリースしていますし,既存の日時関連モジュールの中には,2003年のDateTimeリリース以降,更新が止まっているものも少なくありません。DateTimeモジュール群は事実上の業界標準モジュールとして,世界各地で利用されています。

ただし,DateTimeモジュール群にも泣き所がないわけではありません。日時処理は非常駐環境で使われる場合も少なくないだけに,そのロード時間の長さはいささか気になるところですし,モジュール群の大きさやタイムゾーン・データベースの更新頻度などの都合もあってPerlのコアモジュールにはしづらいというのも悩みの種でした。いくらすばらしいモジュールであっても,インストール不要なコアモジュールでなければ使いづらい環境というのはたしかにあります。そのようなギャップを埋める何かが必要でした。

その隙間を埋める試みとしては,まずアダム・ケネディ(Adam Kennedy)氏が2006年の8月末から9月頭にかけてリリースしたDate::TinyTime::Tinyという2つの::Tinyモジュールがあげられます。

これはログファイルの処理など,複雑な計算は必要ない用途向けにコアを軽量化し,必要があれば本家のDateTimeに処理を委譲しようというものだったのですが,氏が提唱した::Tinyというカテゴリーそのものに対する疑問や,それぞれ日付のみ,時刻のみしか扱えないという大きな制約などから,多くの関心を集めるには至りませんでした。

同時期に起こったより重要な出来事としては,一度はコアから外れたTime::Pieceが,2006年11月にふたたびPerl 5.9/5.10系列のコアに入ったことがあげられます。DateTimeに比べれば不完全な部分があるとはいえ,大きさや機能,ロード時間といったコストパフォーマンスを考えればTime::Pieceは十分に現実的な解決策といえます。その仕様が固められた時期や経緯を考慮すると,これもまた未来(=Perl6)からの贈り物とみなしうるのも大きな理由になりました。

また,ごく最近の例としては牧大輔氏によるDateTimeX::Liteの例をあげることもできます。これはまだ開発版の域を出ていませんが,もう少し整備が進めば興味深いものになるかもしれません。

2038年問題

こういった軽量化の話とは別に,2008年にはエポック時から解放されたはずのDateTimeにもまだ2038年問題は残っているという指摘が行われました。この問題については最終的にマイケル・シュワーン(Michael Schwern)氏がPerl Foundationからの助成金を受けて,Perl本体の日時関数の修正を行うことで解決が計られましたが,最新版のPerlを使っていない方は,Time::y2038というモジュールをインストールしておくと,DateTimeのほうでよきに計らってくれるようになります。

どちらを使うにせよ

機能豊富なDateTimeを使うか,軽量なTime::Pieceを使うかは,必要な機能や環境,好みの問題になりますが,少なくとも最初に紹介したような素のlocaltimeやgmtimeを使うやり方は,モダンとはいえません。日時系のモジュールのインストールにはコンパイラが必要になることが多いので,なかなか移行しづらい面もありますが,これは10年ほど前までのPerl/CGIの時代に比べて長足の進歩をとげた分野でもありますので,機会があればぜひモダンな代替品に移行していただければと思います。

著者プロフィール

石垣憲一(いしがきけんいち)

あるときは翻訳家。あるときはPerlプログラマ。先日『カクテルホントのうんちく話』(柴田書店)を上梓。最新刊は『ガリア戦記』(平凡社ライブラリー)。

URLhttp://d.hatena.ne.jp/charsbar/