package Time::Piece::Over24;

use strict;
use warnings;
use vars qw/$VERSION/;
use Time::Piece;

$VERSION = "0.008";
my $OVER24_OFFSET = '00:00:00';

sub import { shift; @_ = ( "Time::Piece", @_ ); goto &Time::Piece::import }

package Time::Piece;

sub over24 {
    my ( $self, $time ) = @_;
    return $self->from_over24($time) if ($time);
    return $self->over24_datetime;
}

sub over24_time {
    my ( $self, $time ) = @_;
    return $self->from_over24_time($time) if ($time);

    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;
    $hour = sprintf( "%02d", $hour );
    return $self->strftime("$hour:%M:%S");
}

sub over24_year {
    my ($self) = @_;

    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;
    return $self->year;
}

sub over24_mon {
    my ($self) = @_;

    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;
    return $self->mon;
}

sub over24_mday {
    my ($self) = @_;

    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;
    return $self->mday;
}

sub over24_hour {
    my ($self) = @_;
    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;
    return $hour;
}

sub over24_ymd {
    my ($self) = @_;
    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;

    #    return $self->strftime("%Y-%m-%d $hour:%M:%S");
    return $self->ymd;
}

sub over24_datetime {
    my ( $self, $datetime ) = @_;
    return $self->from_over24_time($datetime) if ($datetime);

    my $hour;
    ( $self, $hour ) = $self->_over24_offset_pattern;
    $hour = sprintf( "%02d", $hour );
    return $self->strftime("%Y-%m-%d $hour:%M:%S");
}

sub from_over24 {
    my ( $self, $time ) = @_;
    if ( $time =~ /^\d\d:\d\d(?:\:\d\d)?$/ ) {
        return $self->from_over24_time($time);
    }
    else {
        return $self->from_over24_datetime($time);
    }
}

sub from_over24_time {
    my ( $self, $time ) = @_;
    my $datetime = $self->ymd . " $time";
    return $self->_from_over24($datetime);
}

sub from_over24_datetime {
    my ( $self, $datetime ) = @_;
    return $self->_from_over24($datetime);
}

sub over24_offset {
    my ( $self, $offset ) = @_;
    if ($offset) {

        #just time
        if ( $offset =~ /^\d\d?$/ ) {
            $offset = sprintf( "%02d:00:00", $offset );
        }
        elsif ( $offset =~ /^\d\d?:\d\d$/ ) {
            $offset .= ":00";
        }
        $OVER24_OFFSET = $offset;
    }
    return $OVER24_OFFSET;
}

sub is_during {
    my $self       = shift;
    my $start_time = shift || die "need start_time";
    my $end_time   = shift || die "need end_time";
    my $check_time = shift || $self;

    unless ( ( ref($start_time) eq "Time::Piece" ) ==
        ( ref($end_time) eq "Time::Piece" ) )
    {
        die "start_time and end_time is different object";
    }

    unless ( length($start_time) == length($end_time) ) {
        die "start_time and end_time is different format";
    }

    unless ( ref($check_time) eq "Time::Piece" ) {
        $check_time = $self->over24($check_time);
    }

    my ( $start_t, $end_t );
    if ( ref($start_time) eq "Time::Piece" ) {
        $start_t = $start_time;
        $end_t   = $end_time;
    }
    else {
        my $t   = $check_time;
        my $a_t = $t->over24($start_time);
        my $b_t = $t->over24( $t->over24($end_time)->hms );
        $t -= 86400 if ( ( $t < $a_t ) && ( $t < $b_t ) );

        $start_t = $t->over24($start_time);
        $end_t   = $t->over24($end_time);
    }

    return $start_t <= $check_time && $check_time < $end_t ? '1' : undef;
}

sub _over24_offset_object {
    my ($self) = @_;
    return $self->strptime( $self->ymd . " " . $self->over24_offset,
        '%Y-%m-%d %H:%M:%S' );
}

sub _over24_offset_pattern {
    my ($self) = @_;
    my $hour = $self->hour;
    if ( $self < $self->_over24_offset_object ) {
        $self -= 86400;
        $hour += 24;
    }
    return ( $self, $hour );
}

sub _from_over24 {
    my ( $self, $datetime ) = @_;
    my @hms = split /[\s:]/, $datetime;
    my $data = shift @hms;

    my $add_day = 0;
    if ( $hms[0] >= 24 ) {
        $add_day = int( $hms[0] / 24 );
        $hms[0] = sprintf( "%02d", $hms[0] - 24 * $add_day );
    }
    my $time = join ":", @hms;
    return $self->strptime( $data . " $time", "%Y-%m-%d %T" ) +
      $add_day * 86400;
}

1;
__END__

=head1 NAME

Time::Piece::Over24 - Adds over 24:00:00 methods to Time::Piece

=head1 SYNOPSIS

  use Time::Piece::Over24;

  my $t = localtime;

  #e.g. 2011-01-01 04:00:00 now
  $t->over24_offset("05:00:00");

  #method changed!! time >>> datetime
  print $t->over24_datetime;
  >2010-12-31 28:00:00

  print $t->over24_time;
  >28:00:00

  print $t->over24_year;
  >2010

  print $t->over24_mon;
  >12

  print $t->over24_day;
  >31

  print $t->over24_hour;
  >28;

  #e.g. today is 2011-01-01
  #retun Time::Piece object
  my $over_time = $t->from_over24_time("26:00:00");
  my $over_datetime = $t->from_over24_datetime("2011-01-01 26:00:00");

  print $over_time->datetime;
  print $over_datetime->datetime;
  >2011-01-02 02:00:00
  
  #over24 is alias over24_time and from_over24_time and from_over24_datetime
  $t->over24(); #call over24_time;
  $t->over24("26:00:00"); #call from_over24_time
  $t->over24("2011-01-01 26:00:00"); #call from_over24_datetime

  print $t->is_during("2010-12-31 23:00:00","2011-01-01 10:00:00");
  >1

  print $t->is_during("2011-01-01 05:00:00","2011-01-01 10:00:00");
  >Use of uninitialized value in print ...   = retrun value is undef!

=head1 METHODS

=over 4

=item over24_offset

start hour a day.

default "00:00:00".

value is undef, return offset time.

print localtime->over24_offset();

>00:00:00

not undef is set offset time.

value format is "5" or "05" or "05:00" or "05:00:00", every format is ok.

but set value range is [00:00:00 - 23:59:59]

e.g. over24_offset("05:00:00"); #hour range is [05:00:00 - 28:59:59]

e.g. over24_offset("11:00:00"); #hour range is [11:00:00 - 34:59:59]

=item over24_time

get datetime in offset time

=item from_over24_time, from_voer24_datetime

return a Time::Piece object. 

*not null over24_time e.g.over24_time("26:00:00") is alias, from_over24_time

*not null over24_datetime e.g.over24_datetime("26:00:00") is alias, from_over24_datetime

=item is_during

return 1 or undef

check a time

is_during("start_time","end_time","check_time");

check_time is option.default is now.

start_time and end_time format is datetime,time and timepiece object.

plz start and end is same format.

=back

=head1 AUTHOR

Synsuke Fujishiro <i47.rozary at gmail.com>

=head1 COPYRIGHT & LICENSE

(c) 2011, Synsuke Fujishiro.All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

=head1 SEE ALSO

L<Time::Piece>, L<Time::Piece::MySQL>

=cut
