さまざまな形式の日付文字列を解析するには、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 )
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
モジュールは時刻差の計算において閏秒を利用しません
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
以上が主な関数だ。現在のバージョンで推奨されているオブジェクト指向型インターフェイスについてはまた別記事に書こう。