【Perl】Date::Manip モジュールで日付文字列を解析する(関数型インターフェイス編)


さまざまな形式の日付文字列を解析するには、DateTime::Manip モジュールが使いやすい。これは口語で時間・時刻を表す様々な文字列を、いとも簡単に解析してくれる優れもの。英語に限られるのが残念ではあるが、簡単なスクリプトを書くときには重宝する。

ただ、このモジュールは Ver.6 以降、従来の関数型インターフェイスとは別にオブジェクト指向型インターフェイスがサポートされて、使い方ががらりと変わってしまった。今回は古くから使われている関数型インターフェイスについて書いてみる。

Date::Manip – search.cpan.org
http://search.cpan.org/~sbeck/Date-Manip-6.11/lib/Date/Manip.pod

testDM.pl

#!/usr/bin/perl
use strict;
use warnings;
use feature qw! say !;
use Date::Manip;

my @d_s = (
    "2010/7/9",
    "now",                  # 今
    "next Wednesday",       # 次の水曜日
    "+3:48",                # 3分48秒後
    "-10 business days",    # 10営業日前
);

for ( @d_s ) {
    say $_;
    # Date::Manipの関数型インターフェイスを使う
    say "  => " . UnixDate( $_, "%c" );
}

出力例

2010/7/9
  => Fri Jul  9 00:00:00 2010
now
  => Fri Jul  9 18:38:37 2010
next Wednesday
  => Wed Jul 14 00:00:00 2010
+3:48
  => Fri Jul  9 18:42:25 2010
-10 business days
  => Mon Jun 28 08:00:00 2010

以下、代表的な関数について。

Date_Init( "TZ=JST" )

必須のおまじないだったはずだが、現バージョンでは必要ないのかな。前はこれがないと時刻がすべて GMT になってしまっていた。

UnixDate( $date, @format )

一番使用頻度が高いのがこの関数。時刻を表す文字列を好きな形式で表すことができる。@format に複数の要素を与えると返値はリストになる。

my ( $year, $month, $day ) = UnixDate( "now", "%Y", "%m", "%d" )

その他、表せる形式の一覧は次の通り。

Date::Manip::Date – PRINTF DIRECTIVES
http://search.cpan.org/~sbeck/Date-Manip-6.11/lib/Date/Manip/Date.pod#PRINTF_DIRECTIVES

ParseDate( $date )
ParseDate( \@args )

時刻を解析して一定の形式で返す。これだけでは余り使い道がないので、ほかの関数と組み合わせて使う。

say UnixDate( ParseDate( [ $y, $m, $d ] ), "%Y年%f月%e日" )
# 「2010年 7月11日」などと表示される

Date_Cmp( $d1, $d2 )

二つの時刻を比較して -1$d1 < $d2 の場合)、0$d1 == $d2 の場合)、1$d1 > $d2 の場合)といった値を返す。

# 今年8月最後の月曜日と金曜日ではどちらが後か。
say Date_Cmp( "last Monday in August", "last Friday in August" )
# 「1」と表示される。つまり、月曜日の方が後である。

もちろんこれは、sort 関数での使用を意図したものだ。

my @dates = (
    "2010/07/12",           # 今日
    "Sunday",               # 7月18日
    "Wednesday",            # 7月14日
    "-3 business day",      # 7月8日
    "2nd Friday in July",   # 7月9日
);
say $_ for sort { Date_Cmp( $a, $b ) } @dates;

DateCalc( $d1, $d2 )

引数が共に時刻を表す文字列だった場合はその差を求める。

say DateCalc( "9:00:00", "10:12:34" );
# 「+0:0:0:0:1:12:34」などと表示される
say DateCalc( "now", "next Sunday" );
# 「+0:0:+0:5:13:2:14」などと表示される

引数が時刻と時刻の差だった場合は、計算して時刻文字列を返す。

say DateCalc( "9:00:00", "+0:0:0:0:1:12:34" );
# 「2010071210:12:34」などと表示される
say DateCalc( "9:00:00", "-0:0:0:0:2:34:56" );
# 「2010071206:25:04」などと表示される

引数が共に時刻の差を表す文字列だった場合は、単純に足し引きの結果を返す。

say DateCalc( "+0:0:0:0:5:20:30", "-0:0:0:0:2:30:10" );
# 「+0:0:+0:0:2:50:20」などと表示される

Delta_Format( $delta, [$dec,] @in )

時刻差文字列を指定した形式に変換して返す関数。

$delta
時刻差文字列。上述の DateCalc 関数の返値である。
$dec
小数の桁数。省略可。
@in
表示形式。
say Delta_Format( DateCalc( "9:00:00", "10:12:34" ), "%hv時間%mv分%sv秒" );
# 「1時間12分34秒」などと表示される
say Delta_Format( DateCalc( "2010/7/13 10:12:34", "next Sunday" ), "%yv年%Mvヶ月%dv日%hv時間%mv分%sv秒" );
# 「0年0ヶ月4日13時間47分26秒」などと表示される
say Delta_Format( DateCalc( "2010/7/13 10:12:34", "next Sunday" ), 2, "%hv %hd %hh %ht" );
# 「13 13.79 109.00 109.79」などと表示される

“表示形式”に指定できる文字列は、前述の UnixDate の項で説明した文字列に“v”、“d”、“h”、“t”のいずれかを指定したものとなる。上の例で使った "%hv %hd %hh %ht" について説明すると、

%hv
時刻差「4 日 13 時間 47 分 26 秒」のうち「13 時間」を返す。
%hh
“時間”より小さい単位を合わせて返す。つまりこの場合、「13 時間 47 分 26 秒」を“時間”単位で表した数値、「13.79 時間」を返す。
%hd
“時間”より大きい単位を合わせて返す。つまりこの場合、「4 日 13 時間」を“時間”単位で表した数値、「109.00 時間」を返す。
%ht
全ての値を“時間”単位で返す。つまりこの場合、「4 日 13 時間 47 分 26 秒」を“時間”単位で表した数値、「109.79 時間」を返す。

このようになる。他にも、秒単位で全ての値を表すなら「%st」、日単位でそれより小さい単位を合わせて返すなら「%dh」などとなるわけだ。

# 2009年元日を秒で表す
say Delta_Format( DateCalc( "2009/1/1", "2009/1/2" ), 0, "%st 秒" )

上記の例では「86400 秒」などと表示される。さすがに閏秒には対応できていないようだ。この日は「2009 年 1 月 1 日 8 時 59 分 60 秒」という閏秒があったはずなので、正しくは「86401 秒」と表示しなくてはならない。

閏秒 – Wikipedia
http://ja.wikipedia.org/wiki/%E9%96%8F%E7%A7%92

報道発表(お知らせ)「うるう秒」挿入のお知らせ ~ 来年の元日はいつもより「1秒」長い1日です ~
http://www2.nict.go.jp/pub/whatsnew/press/h20/080912/080912-1.html

Date::Manip does NOT make use of the leap seconds in calculating time intervals

Date::Manip モジュールは時刻差の計算において閏秒を利用しません

http://search.cpan.org/~sbeck/Date-Manip-6.11/lib/Date/Manip/Misc.pod#WHAT_DATES_ARE_DATE::MANIP_USEFUL_FOR?

ParseRecur( $string, $base, $start, $end )

条件を満たす複数の日付・時刻文字列の配列を返す関数。“条件”を事細かに指定することによって相当複雑なことができるようだ。便利と言えば便利だが、どういう場合に必要なのか……。

# 今年の7月から9月までの、月最後の土曜日の朝4時半を表す
say $_ for ParseRecur( "0:*-1:6:4:30:0", "", "2010/7/1", "2010/9/30" );
# 2010073104:30:00
# 2010082804:30:00
# 2010092504:30:00

以上が主な関数だ。現在のバージョンで推奨されているオブジェクト指向型インターフェイスについてはまた別記事に書こう。

コメントを残す