package Time::Local;
require 5.000;
require Exporter;
use Carp;

@ISA		= qw( Exporter );
@EXPORT		= qw( timegm timelocal );
@EXPORT_OK	= qw( timegm_nocheck timelocal_nocheck );

# Set up constants
    $SEC  = 1;
    $MIN  = 60 * $SEC;
    $HR   = 60 * $MIN;
    $DAY  = 24 * $HR;
# Determine breakpoint for rolling century
    my $thisYear = (localtime())[5];
    $nextCentury = int($thisYear / 100) * 100;
    $breakpoint = ($thisYear + 50) % 100;
    $nextCentury += 100 if $breakpoint < 50;

my %options;

sub timegm {
    my (@date) = @_;
    if ($date[5] > 999) {
        $date[5] -= 1900;
    }
    elsif ($date[5] >= 0 && $date[5] < 100) {
        $date[5] -= 100 if $date[5] > $breakpoint;
        $date[5] += $nextCentury;
    }
    $ym = pack(C2, @date[5,4]);
    $cheat = $cheat{$ym} || &cheat(@date);
    $cheat
    + $date[0] * $SEC
    + $date[1] * $MIN
    + $date[2] * $HR
    + ($date[3]-1) * $DAY;
}

sub timegm_nocheck {
    local $options{no_range_check} = 1;
    &timegm;
}

sub timelocal {
    my $t = &timegm;
    my $tt = $t;

    my (@lt) = localtime($t);
    my (@gt) = gmtime($t);
    if ($t < $DAY and ($lt[5] >= 70 or $gt[5] >= 70 )) {
	# Wrap error, too early a date
	# Try a safer date
	$tt += $DAY;
	@lt = localtime($tt);
	@gt = gmtime($tt);
    }

    my $tzsec = ($gt[1] - $lt[1]) * $MIN + ($gt[2] - $lt[2]) * $HR;

    if($lt[5] > $gt[5]) {
	$tzsec -= $DAY;
    }
    elsif($gt[5] > $lt[5]) {
	$tzsec += $DAY;
    }
    else {
	$tzsec += ($gt[7] - $lt[7]) * $DAY;
    }

    $tzsec += $HR if($lt[8]);
    
    $time = $t + $tzsec;
    @test = localtime($time + ($tt - $t));
    $time -= $HR if $test[2] != $_[2];
    $time;
}

sub timelocal_nocheck {
    local $options{no_range_check} = 1;
    &timelocal;
}

sub cheat {
    $year = $_[5];
    $month = $_[4];
    unless ($options{no_range_check}) {
	croak "Month '$month' out of range 0..11" if $month > 11 || $month < 0;
	croak "Day '$_[3]' out of range 1..31"	  if $_[3] > 31 || $_[3] < 1;
	croak "Hour '$_[2]' out of range 0..23"	  if $_[2] > 23 || $_[2] < 0;
	croak "Minute '$_[1]' out of range 0..59" if $_[1] > 59 || $_[1] < 0;
	croak "Second '$_[0]' out of range 0..59" if $_[0] > 59 || $_[0] < 0;
    }
    $guess = $^T;
    @g = gmtime($guess);
    $lastguess = "";
    $counter = 0;
    while ($diff = $year - $g[5]) {
	croak "Can't handle date (".join(", ",@_).")" if ++$counter > 255;
	$guess += $diff * (363 * $DAY);
	@g = gmtime($guess);
	if (($thisguess = "@g") eq $lastguess){
	    croak "Can't handle date (".join(", ",@_).")";
	    #date beyond this machine's integer limit
	}
	$lastguess = $thisguess;
    }
    while ($diff = $month - $g[4]) {
	croak "Can't handle date (".join(", ",@_).")" if ++$counter > 255;
	$guess += $diff * (27 * $DAY);
	@g = gmtime($guess);
	if (($thisguess = "@g") eq $lastguess){
	    croak "Can't handle date (".join(", ",@_).")";
	    #date beyond this machine's integer limit
	}
	$lastguess = $thisguess;
    }
    @gfake = gmtime($guess-1); #still being sceptic
    if ("@gfake" eq $lastguess){
        croak "Can't handle date (".join(", ",@_).")";
        #date beyond this machine's integer limit
    }
    $g[3]--;
    $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAY;
    $cheat{$ym} = $guess;
}

1;

