154359Sroberto;# timelocal.pl
254359Sroberto;#
354359Sroberto;# Usage:
454359Sroberto;#	$time = timelocal($sec,$min,$hours,$mday,$mon,$year,$junk,$junk,$isdst);
554359Sroberto;#	$time = timegm($sec,$min,$hours,$mday,$mon,$year);
654359Sroberto
754359Sroberto;# These routines are quite efficient and yet are always guaranteed to agree
854359Sroberto;# with localtime() and gmtime().  We manage this by caching the start times
954359Sroberto;# of any months we've seen before.  If we know the start time of the month,
1054359Sroberto;# we can always calculate any time within the month.  The start times
1154359Sroberto;# themselves are guessed by successive approximation starting at the
1254359Sroberto;# current time, since most dates seen in practice are close to the
1354359Sroberto;# current date.  Unlike algorithms that do a binary search (calling gmtime
1454359Sroberto;# once for each bit of the time value, resulting in 32 calls), this algorithm
1554359Sroberto;# calls it at most 6 times, and usually only once or twice.  If you hit
1654359Sroberto;# the month cache, of course, it doesn't call it at all.
1754359Sroberto
1854359Sroberto;# timelocal is implemented using the same cache.  We just assume that we're
1954359Sroberto;# translating a GMT time, and then fudge it when we're done for the timezone
2054359Sroberto;# and daylight savings arguments.  The timezone is determined by examining
2154359Sroberto;# the result of localtime(0) when the package is initialized.  The daylight
2254359Sroberto;# savings offset is currently assumed to be one hour.
2354359Sroberto
2454359SrobertoCONFIG: {
2554359Sroberto    package timelocal;
2654359Sroberto
2754359Sroberto    @epoch = localtime(0);
2854359Sroberto    $tzmin = $epoch[2] * 60 + $epoch[1];	# minutes east of GMT
2954359Sroberto    if ($tzmin > 0) {
3054359Sroberto	$tzmin = 24 * 60 - $tzmin;		# minutes west of GMT
3154359Sroberto	$tzmin -= 24 * 60 if $epoch[5] == 70;	# account for the date line
3254359Sroberto    }
3354359Sroberto
3454359Sroberto    $SEC = 1;
3554359Sroberto    $MIN = 60 * $SEC;
3654359Sroberto    $HR = 60 * $MIN;
3754359Sroberto    $DAYS = 24 * $HR;
3854359Sroberto    $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0;
3954359Sroberto}
4054359Sroberto
4154359Srobertosub timegm {
4254359Sroberto    package timelocal;
4354359Sroberto
4454359Sroberto    $ym = pack(C2, @_[5,4]);
4554359Sroberto    $cheat = $cheat{$ym} || &cheat;
4654359Sroberto    $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS;
4754359Sroberto}
4854359Sroberto
4954359Srobertosub timelocal {
5054359Sroberto    package timelocal;
5154359Sroberto
5254359Sroberto    $ym = pack(C2, @_[5,4]);
5354359Sroberto    $cheat = $cheat{$ym} || &cheat;
5454359Sroberto    $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS
5554359Sroberto	+ $tzmin * $MIN - 60 * 60 * ($_[8] != 0);
5654359Sroberto}
5754359Sroberto
5854359Srobertopackage timelocal;
5954359Sroberto
6054359Srobertosub cheat {
6154359Sroberto    $year = $_[5];
6254359Sroberto    $month = $_[4];
6354359Sroberto    $guess = $^T;
6454359Sroberto    @g = gmtime($guess);
6554359Sroberto    $year += $YearFix if $year < $epoch[5];
6654359Sroberto    while ($diff = $year - $g[5]) {
6754359Sroberto	$guess += $diff * (364 * $DAYS);
6854359Sroberto	@g = gmtime($guess);
6954359Sroberto    }
7054359Sroberto    while ($diff = $month - $g[4]) {
7154359Sroberto	$guess += $diff * (28 * $DAYS);
7254359Sroberto	@g = gmtime($guess);
7354359Sroberto    }
7454359Sroberto    $g[3]--;
7554359Sroberto    $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAYS;
7654359Sroberto    $cheat{$ym} = $guess;
7754359Sroberto}
78