日付系の処理を集約してくれるDate::Class(仮)を考えてみた

日付系の処理って用途によってモジュールが分かれてて非常に使いにくいと思うのんだけども。

基本的にはDateTime使えばいいんだろうけど例えばカレンダーとか作るときに休日とか年号とか色々処理したいときに、

 use DateTime;
 use Date::Japanese::Holiday;
 use Date::Japanese::Era;
 
 my $dt  = DateTime->new(year=>2007,month=>3,day=>13);
 my $djh = Date::Japanese::Holiday->new($dt->year,$dt->month,$dt->day);
 my $dje = Date::Japanese::Era->new($dt->year,$dt->month,$dt->day);
 
 if ( $djh->is_holiday ) {
     print $dje->name;
 }

こんな感じで別のオブジェクトから呼び出さないといけないのがなんともイケてないんだよね。

なんつーかもうDateTimeからすべて制御できたらとってもスッキリするのになぁとか、

 use DateTime;
 
 my $dt  = DateTime->new(year=>2007,month=>3,day=>13);

 if ( $dt->is_holiday ) {
     print $dt->era_name;
 }

日付に関する処理って他にも色々あって干支だったり星座だったり年齢だったりユリウス暦だったり会計年度だったり(だんだんマニアックになってきた)そーゆーのが全部DateTimeオブジェクトから操作できたらスッキリ綺麗に処理が書けていいのにね、

 my $dt = DateTime->new(year=>1980,month=>12,day=>22);
 printf
     qq{私の年齢は%s歳です。星座は%sで、干支は%sです。},
     $dt->age,           # 年齢
     $dt->zodiac_name,   # 星座
     $dt->year_zyunishi; # 干支

ただし、日付に関する処理ってさっきも少し挙げたけどめちゃくちゃ沢山あるのでそれら全部が全部DateTimeに入れてしまったら物凄い巨大なクラスになってしまう。ただでさえDateTimeはメソッドの数が多いのに。

かもたまにしか使わないようなメソッドが量産されてしまうんで初めからメソッド定義しておくのは微妙。かといってCGIクラスみたいな実装もどうかと思うのでじゃあどうすんだよと。

そこでだ。

DBIx::Classみたいに追加したい処理をコンポーネント化しといて使いたいときにMIXINさせるやり方はどうだろう。

というわけでDBIx::ClassをもじってDate::Classというのを考えてみました。

 package MyDateClass;
 use strict;
 use warnings;
 use base qw/Date::Class/;
 __PACKAGE__->load_components(qw/
     Core
     Age
     Japanese::Holiday
     Japanese::Era
     Japanese::Eto
     Zodiac
 / );
 
 my $dt = MyDateClass->new(year=>1980,month=>12,day=>22);
 
 if ( $dt->is_holiday ) {
     print $dt->era_name;
 }
 printf
     qq{私の年齢は%s歳です。星座は%sで、干支は%sです。},
     $dt->age,           # 年齢
     $dt->zodiac_name,   # 星座
     $dt->year_zyunishi; # 干支

こういう感じでコンポーネント化しとけば使用頻度は少ないけどあったら便利なメソッドとかもじゃんじゃん作れて良いんじゃないかと。例えば曜日の判定とかも、

 package Date::Class::Component::IsWeek;
 use strict;
 use warnings;
 
 our $VERSION = '0.01';
 
 sub is_monday    { shift->day_of_week == 1 }
 sub is_tuesday   { shift->day_of_week == 2 }
 sub is_wednesday { shift->day_of_week == 3 }
 sub is_thursday  { shift->day_of_week == 4 }
 sub is_friday    { shift->day_of_week == 5 } 
 sub is_saturday  { shift->day_of_week == 6 }
 sub is_sunday    { shift->day_of_week == 7 }
 
 1;

こーゆーコンポーネント作って

 package MyDateClass;
 use base qw/Date::Class/;
 __PACKAGE__->load_components(qw/Core IsWeek/);
 
 my $dt = MyDateClass->new(year=>2007,month=>3,day=>11);
 if ( $dt->is_sunday ) {
     print "日曜日です";
 }

こー書いたほうがカッコよくない?

はたまたIteratorコンポーネントみたいなの作って、ある期間の間の処理を簡潔にわかりやすく書くとか、

 ### 普通のやり方
 my $dt   = DateTime->new(year=>1999,month=>1,day=>1);
 my $dt_c = $dt->clone;
 my $end  = DateTime->new(year=>1999,month=>12,day=>31);
 while( $dt_c->ymd <= $end->ymd ){
     # 1999/01/01〜1999/12/31まで5日ずつループ処理
     print $dt_c->ymd;
     $dt_c->add( days => 5 );
 }
 
 ### Iteratorを使ったやり方
 package MyDateClass;
 use base qw/Date::Class/;
 __PACKAGE__->load_components(qw/Core Iterator/);
 
 my $dt   = MyDateClass->new(year=>1999,month=>1,day=>1);
 my $iter = $dt->iterator(year=>1999,month=>12,day=>31)->engine( days => 5 );
 while( my $dt_c = $iter->next ) {
     # 1999/01/01〜1999/12/31まで5日ずつループ処理
     print $dt_c->ymd;
 }

ってな感じなわけで。

とまぁ、あまり調査もせずにここまで勢いで書いてみたんだけどどうだろ。

# 実は今までは自作の日付クラス使ってたからDateTimeどんなメソッドがあるとかあまりよくわかってなかったりしますorz

ってかもう既に同じような思想のモジュールとか別の方法とかがあったらさぶいな・・・。それPlaggerでできるよとか言われたらどうしようw

あと現状としてはCoreクラスをDateTimeベースで考えてたんだけどDateTime自体が結構でかいクラスなんでもっと最小限のことしかできない小さな日付クラスとかをCoreクラスにしたほうが良いかもねぇ。

とりあえず適当にサクッと作ってみたサンプルプログラムを以下に。

Date-Class-v0.01.tar.gz