package DateTime::Time;
# $Id: Time.pm,v 1.11 2000/12/16 05:45:56 tom Exp $
################################################################

=head1 NAME

DateTime::Time - 時間クラス

=head1 SYNOPSIS

  my $t = new DateTime::Time;
  $t->Set(1999, 10, 22, 0, 0, 0);

=cut

use strict;
use DateTime::Date;
use DateTime::Zone;

use vars qw(@ISA $AUTOLOAD);
@ISA = qw(DateTime::Date);

attributes qw(hour min sec time);

use overload
    "+" => "Increment",
    "-" => "Decrement",
    "<=>"=> "Compare"
    ;

################################################################
# auto loading

use AutoLoader;

sub AUTOLOAD {
    $AutoLoader::AUTOLOAD = $AUTOLOAD;
    goto &AutoLoader::AUTOLOAD;
}

package DateTime::Time;
1;

################################################################
__END__

sub initialize($)
{
    my $self = shift;
    $self->hour(0) unless $self->hour;
    $self->min(0) unless $self->min;
    $self->sec(0) unless $self->sec;
    $self->SUPER::initialize;
}


=head2 $t->SetTime($tm, $TZ);

時刻 $tm にセットする。
$TZ は省略可

=cut

sub SetTime ($$;$)
{
    my ($self, $time, $tz) = @_;
    $tz = $ENV{'TZ'} unless defined $tz;    
    my ($min, $sec, $hour, $day, $mon, $year) = gmtime($time+tz_offset($tz));
    $self->Set($year+1900, $mon+1, $day, $hour, $sec, $min);
    $self->time($time);
}

=head2 $t->Set($year, $month, $day, $hour, $min, $sec, $tz);

年月日時分秒をセットする。

=cut

sub Set($$$$;$)
{
    my ($self, $y, $m, $d, $H, $M, $S, $TZ) = @_;

    $self->SUPER::Set($y, $m, $d);
    $self->hour($H);
    $self->min($M);
    $self->sec($S);
    $self;
}
sub Dup($)
{
    my $self = shift;

    return new DateTime::Time(year=>$self->year,
			      month=>$self->month,
			      day=>$self->day,
			      hour=>$self->hour,
			      min=>$self->min,
			      sec=>$self->sec
			      );
}

################################################################

=head2 $t->GetTime($TZ);

tm を得る

=cut


sub GetTime($;$)
{
    my ($self, $tz) = @_;
    $tz ||= 'GMT';
    if ($self->month < 1 || $self->month > 12){
	die "illegal month: ", $self->month;
    }
    require Time::Local;
    return Time::Local::timegm($self->sec, $self->min, $self->hour,
			       $self->day, $self->month-1, $self->year-1900)-
				   tz_offset($tz);

}

=head2 $t->IsValid;

時刻として正しいか

=cut

sub IsValid ($)
{
    my $self = shift;
    return 0 unless $self->SUPER::IsValid;
    return 0 if $self->hour < 0 || $self->hour >= 24;
    return 0 if $self->min < 0 || $self->month >= 60;
    return 0 if $self->sec < 0 || $self->sec >= 60;
    return 1;
}


=head2 $self->part;

旬を a, b, c で返す

=cut

sub part($)
{
    my $self = shift;
    my @tmp = qw(a b c c);
    return $tmp[int($self->day/10)];
}
################################################################

=head2 $t->Increment($q);

$q だけ増やす

=cut

sub Increment ($$)
{
    my ($self, $quantity) = @_;
    my ($num, $unit) = $quantity =~ /^(\d+)([hms])$/;
#    $num ||= 1;

    if ($quantity eq '1'){
	$num = 1;
	$unit = 's';
    }

#    print "time:$quantity\n";
    
    if ($unit eq 's'){
	my $sec = $self->sec;
	$sec += $num;
	my $min = int($sec / 60);
	$self->sec($sec % 60);
	$self->Increment("${min}m") if $min;
    } elsif ($unit eq 'm'){
	my $min = $self->min;
	$min += $num;
	my $hour = int($min / 60);
	$self->min($min % 60);
	$self->Increment("${hour}h") if $hour;
    } elsif ($unit eq 'h'){
	my $hour = $self->hour;
	$hour += $num;
	my $day = int($hour / 24);
	$self->hour($hour % 24);
	$self->SUPER::Increment("${day}D") if $day;
    } else {
	$self->SUPER::Increment($quantity);
    }
    return $self;
}

=head2 $t->Decrement($q);

$q だけ減らす

=cut

sub Decrement ($$)
{
    my ($self, $quantity) = @_;
    if (ref $quantity){                # blessed
	return $self->GetTime - $quantity->GetTime;
    }
    my ($num, $unit) = $quantity =~ /^(\d+)([hms])$/;
    if ($quantity eq '1'){
	$num = 1;
	$unit = 's';
    }
    if ($unit eq 's'){
	my $sec = $self->sec;
	$sec -= $num;
	if ($sec < 0){
	    my $min = int($sec*(-1)/60)+1;
#	    print "min:$min, sec: $sec\n";
	    $self->Decrement("${min}m");
	}
	$self->sec($sec % 60);
    } elsif ($unit eq 'm'){
	my $min = $self->min;
	$min -= $num;
	if ($min < 0){
	    my $hour = int($min*(-1)/60)+1;
	    $self->Decrement("${hour}h");
	}
	$self->min($min % 60);
    } elsif ($unit eq 'h'){
	my $hour = $self->hour;
	$hour -= $num;
	if ($hour < 0){
	    my $day = int($hour*(-1)/24)+1;
	    $self->SUPER::Decrement("${day}D");
	}
	$self->hour($hour%24);
    } else {
	$self->SUPER::Decrement($quantity);
    }
    $self;
}

=head2 $t->Compare($t2);

$t2 と比較する。

=cut

sub Compare ($$)
{
    my ($self, $obj) = @_;

    my $rtn = $self->SUPER::Compare($obj);

    if ($rtn == 0){
	if (ref $obj ne 'DateTime::Time'){
	    return 0;
	}
	my $s_hms = sprintf("%02d%02d%02d", $self->hour, $self->min, $self->sec);
	my $o_hms = sprintf("%02d%02d%02d", $obj->hour, $obj->min, $obj->sec);
	return $s_hms <=> $o_hms;
    } else {
	return $rtn;
    }
}

sub GetParams($$)
{
    my ($self, $params) = @_;

    $params = $self->SUPER::GetParams($params);

    $params->{'hh'} = sprintf("%02d", $self->hour);
    $params->{'mm'} = sprintf("%02d", $self->min);
    $params->{'ss'} = sprintf("%02d", $self->sec);
    return $params;
}
    
sub Dump($)
{
    my $self = shift;
    return sprintf("%s %02d:%02d:%02d",
		   $self->SUPER::Dump,
		   $self->hour, $self->min, $self->day);
}


1;
