perldoc under OS/2
[p5sagit/p5-mst-13.2.git] / lib / Time / Local.pm
CommitLineData
a0d0e21e 1package Time::Local;
2require 5.000;
3require Exporter;
4use Carp;
5
6@ISA = qw(Exporter);
7@EXPORT = qw(timegm timelocal);
8
cb1a09d0 9=head1 NAME
a0d0e21e 10
1fef88e7 11Time::Local - efficiently compute time from local and GMT time
a0d0e21e 12
cb1a09d0 13=head1 SYNOPSIS
a0d0e21e 14
cb1a09d0 15 $time = timelocal($sec,$min,$hours,$mday,$mon,$year);
16 $time = timegm($sec,$min,$hours,$mday,$mon,$year);
17
18=head1 DESCRIPTION
19
20These routines are quite efficient and yet are always guaranteed to agree
21with localtime() and gmtime(). We manage this by caching the start times
22of any months we've seen before. If we know the start time of the month,
23we can always calculate any time within the month. The start times
24themselves are guessed by successive approximation starting at the
25current time, since most dates seen in practice are close to the
26current date. Unlike algorithms that do a binary search (calling gmtime
27once for each bit of the time value, resulting in 32 calls), this algorithm
28calls it at most 6 times, and usually only once or twice. If you hit
29the month cache, of course, it doesn't call it at all.
30
31timelocal is implemented using the same cache. We just assume that we're
32translating a GMT time, and then fudge it when we're done for the timezone
33and daylight savings arguments. The timezone is determined by examining
34the result of localtime(0) when the package is initialized. The daylight
35savings offset is currently assumed to be one hour.
36
37Both routines return -1 if the integer limit is hit. I.e. for dates
38after the 1st of January, 2038 on most machines.
39
40=cut
a0d0e21e 41
16bb4654 42BEGIN {
16bb4654 43 $SEC = 1;
44 $MIN = 60 * $SEC;
45 $HR = 60 * $MIN;
46 $DAY = 24 * $HR;
55497cff 47 $epoch = (localtime(2*$DAY))[5]; # Allow for bugs near localtime == 0.
48
16bb4654 49 $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0;
50
9bb8015a 51}
52
53sub timegm {
54 $ym = pack(C2, @_[5,4]);
55 $cheat = $cheat{$ym} || &cheat;
56 return -1 if $cheat<0 and $^O ne 'VMS';
57 $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAY;
58}
59
60sub timelocal {
61 my $t = &timegm;
62
63 my (@lt) = localtime($t);
64 my (@gt) = gmtime($t);
a0d0e21e 65
9bb8015a 66 my $tzsec = ($gt[1] - $lt[1]) * $MIN + ($gt[2] - $lt[2]) * $HR;
16bb4654 67
68 my($lday,$gday) = ($lt[7],$gt[7]);
69 if($lt[5] > $gt[5]) {
70 $tzsec -= $DAY;
71 }
72 elsif($gt[5] > $lt[5]) {
73 $tzsec += $DAY;
74 }
75 else {
76 $tzsec += ($gt[7] - $lt[7]) * $DAY;
77 }
78
9bb8015a 79 $tzsec += $HR if($lt[8]);
80
81 $time = $t + $tzsec;
55497cff 82 return -1 if $cheat<0 and $^O ne 'VMS';
a0d0e21e 83 @test = localtime($time);
84 $time -= $HR if $test[2] != $_[2];
85 $time;
86}
87
88sub cheat {
89 $year = $_[5];
16bb4654 90 $year -= 1900
91 if $year > 1900;
a0d0e21e 92 $month = $_[4];
0c160758 93 croak "Month '$month' out of range 0..11" if $month > 11 || $month < 0;
94 croak "Day '$_[3]' out of range 1..31" if $_[3] > 31 || $_[3] < 1;
95 croak "Hour '$_[2]' out of range 0..23" if $_[2] > 23 || $_[2] < 0;
96 croak "Minute '$_[1]' out of range 0..59" if $_[1] > 59 || $_[1] < 0;
97 croak "Second '$_[0]' out of range 0..59" if $_[0] > 59 || $_[0] < 0;
a0d0e21e 98 $guess = $^T;
99 @g = gmtime($guess);
55497cff 100 $year += $YearFix if $year < $epoch;
a0d0e21e 101 $lastguess = "";
102 while ($diff = $year - $g[5]) {
16bb4654 103 $guess += $diff * (363 * $DAY);
a0d0e21e 104 @g = gmtime($guess);
105 if (($thisguess = "@g") eq $lastguess){
106 return -1; #date beyond this machine's integer limit
107 }
108 $lastguess = $thisguess;
109 }
110 while ($diff = $month - $g[4]) {
16bb4654 111 $guess += $diff * (27 * $DAY);
a0d0e21e 112 @g = gmtime($guess);
113 if (($thisguess = "@g") eq $lastguess){
114 return -1; #date beyond this machine's integer limit
115 }
116 $lastguess = $thisguess;
117 }
118 @gfake = gmtime($guess-1); #still being sceptic
119 if ("@gfake" eq $lastguess){
120 return -1; #date beyond this machine's integer limit
121 }
122 $g[3]--;
16bb4654 123 $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAY;
a0d0e21e 124 $cheat{$ym} = $guess;
125}
126
1271;