Practice of Programming

プログラム とか Linuxとかの話題

DateTime系のモジュールで...

ここで、DateTime系って言ってるのは、DateTimeモジュールのことではないです。
自作する場合(wrapperでもなんでも)、みたいな話。

例えば、Time::Pieceのように

$d += DAY * 5;
$d += MONTH * 3;

なんてことをしたい。
ただ、Time::PieceのTime::Secondsに定義されているMONTHとかは、単純に秒数なので、1/31 + 1 * MONTH = 2/28 みたいなことはしてくれない。

で、次のようなものを考えた。

package Hoge::DateTime::Unit;

use strict;
use Carp ();
use Readonly;
use overload '""'   => sub { shift };
use overload '*'    => sub { my $self = shift; return $self->_multiply(@_) };
use overload 'bool' => sub { my $self = shift; return $$self };

# new is used to create constant value. so $unit shouldn't be modified.
sub new {
  my $class = shift;
  Readonly my $unit => 1;
  return bless \$unit, $class;
}

sub _multiply {
  my $class = ref(my $self = shift);
  my $unit  = $$self * shift;
  return bless \$unit, $class;
}

package Hoge::DateTime::Month;
use base qw/Hoge::DateTime::Unit/;

package Hoge::DateTime::Year;
use base qw/Hoge::DateTime::Unit/;

package Hoge::DateTime::Weekday;
use base qw/Hoge::DateTime::Unit/;

1;

とかしてやって、DateTime系のモジュールで、 次のようにconstant定義する。

package Hoge::DateTime;

use constant {
  YEAR    => Hoge::DateTime::YEAR->new,
  MONTH   => Hoge::DateTime::Month->new,
  WEEKDAY => Hoge::DateTime::Weekday->new, # 営業日
  DAY     => 60 * 60 * 24,
  HOUR    => 60 * 60,
  MINUTE  => 60,
  SECOND  => 1,
};

オブジェクトに +/- されるときに、classを調べて、挙動を変えてあげると、

$d = Hoge::DateTime->new("2007-01-31");
$d += MONTH * 1;   # 2007-02-28
$d += WEEKDAY * 7; # 2007-03-09
$d += YEAR * 1;    # 2008-03-09

みたいなのが出来るなぁとか思った。それだけ。