This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
make timelocal work better when time is close to the epoch
[perl5.git] / lib / Time / Local.pm
CommitLineData
a0d0e21e
LW
1package Time::Local;
2require 5.000;
3require Exporter;
4use Carp;
5
6@ISA = qw(Exporter);
7@EXPORT = qw(timegm timelocal);
8
06ef4121 9# Set up constants
16bb4654 10 $SEC = 1;
11 $MIN = 60 * $SEC;
12 $HR = 60 * $MIN;
13 $DAY = 24 * $HR;
06ef4121
PC
14# Determine breakpoint for rolling century
15 my $thisYear = (localtime())[5];
16 $nextCentury = int($thisYear / 100) * 100;
17 $breakpoint = ($thisYear + 50) % 100;
18 $nextCentury += 100 if $breakpoint < 50;
9bb8015a
MK
19
20sub timegm {
06ef4121
PC
21 my (@date) = @_;
22 if ($date[5] > 999) {
23 $date[5] -= 1900;
24 }
25 elsif ($date[5] >= 0 && $date[5] < 100) {
26 $date[5] -= 100 if $date[5] > $breakpoint;
27 $date[5] += $nextCentury;
28 }
29 $ym = pack(C2, @date[5,4]);
30 $cheat = $cheat{$ym} || &cheat(@date);
31 $cheat
32 + $date[0] * $SEC
33 + $date[1] * $MIN
34 + $date[2] * $HR
35 + ($date[3]-1) * $DAY;
9bb8015a
MK
36}
37
38sub timelocal {
39 my $t = &timegm;
84902520 40 my $tt = $t;
9bb8015a
MK
41
42 my (@lt) = localtime($t);
43 my (@gt) = gmtime($t);
84902520 44 if ($t < $DAY and ($lt[5] >= 70 or $gt[5] >= 70 )) {
06ef4121
PC
45 # Wrap error, too early a date
46 # Try a safer date
e85ca32b 47 $tt += $DAY;
06ef4121
PC
48 @lt = localtime($tt);
49 @gt = gmtime($tt);
84902520 50 }
a0d0e21e 51
9bb8015a 52 my $tzsec = ($gt[1] - $lt[1]) * $MIN + ($gt[2] - $lt[2]) * $HR;
16bb4654 53
54 my($lday,$gday) = ($lt[7],$gt[7]);
55 if($lt[5] > $gt[5]) {
56 $tzsec -= $DAY;
57 }
58 elsif($gt[5] > $lt[5]) {
59 $tzsec += $DAY;
60 }
61 else {
62 $tzsec += ($gt[7] - $lt[7]) * $DAY;
63 }
64
9bb8015a
MK
65 $tzsec += $HR if($lt[8]);
66
67 $time = $t + $tzsec;
84902520 68 @test = localtime($time + ($tt - $t));
a0d0e21e
LW
69 $time -= $HR if $test[2] != $_[2];
70 $time;
71}
72
73sub cheat {
74 $year = $_[5];
75 $month = $_[4];
0c160758 76 croak "Month '$month' out of range 0..11" if $month > 11 || $month < 0;
76113898 77 croak "Day '$_[3]' out of range 1..31" if $_[3] > 31 || $_[3] < 1;
0c160758
AB
78 croak "Hour '$_[2]' out of range 0..23" if $_[2] > 23 || $_[2] < 0;
79 croak "Minute '$_[1]' out of range 0..59" if $_[1] > 59 || $_[1] < 0;
80 croak "Second '$_[0]' out of range 0..59" if $_[0] > 59 || $_[0] < 0;
a0d0e21e
LW
81 $guess = $^T;
82 @g = gmtime($guess);
a0d0e21e 83 $lastguess = "";
390badbd 84 $counter = 0;
a0d0e21e 85 while ($diff = $year - $g[5]) {
390badbd 86 croak "Can't handle date (".join(", ",@_).")" if ++$counter > 255;
16bb4654 87 $guess += $diff * (363 * $DAY);
a0d0e21e
LW
88 @g = gmtime($guess);
89 if (($thisguess = "@g") eq $lastguess){
06ef4121
PC
90 croak "Can't handle date (".join(", ",@_).")";
91 #date beyond this machine's integer limit
a0d0e21e
LW
92 }
93 $lastguess = $thisguess;
94 }
95 while ($diff = $month - $g[4]) {
390badbd 96 croak "Can't handle date (".join(", ",@_).")" if ++$counter > 255;
16bb4654 97 $guess += $diff * (27 * $DAY);
a0d0e21e
LW
98 @g = gmtime($guess);
99 if (($thisguess = "@g") eq $lastguess){
06ef4121
PC
100 croak "Can't handle date (".join(", ",@_).")";
101 #date beyond this machine's integer limit
a0d0e21e
LW
102 }
103 $lastguess = $thisguess;
104 }
105 @gfake = gmtime($guess-1); #still being sceptic
106 if ("@gfake" eq $lastguess){
06ef4121
PC
107 croak "Can't handle date (".join(", ",@_).")";
108 #date beyond this machine's integer limit
a0d0e21e
LW
109 }
110 $g[3]--;
16bb4654 111 $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAY;
a0d0e21e
LW
112 $cheat{$ym} = $guess;
113}
114
1151;
06ef4121
PC
116
117__END__
118
119=head1 NAME
120
121Time::Local - efficiently compute time from local and GMT time
122
123=head1 SYNOPSIS
124
125 $time = timelocal($sec,$min,$hours,$mday,$mon,$year);
126 $time = timegm($sec,$min,$hours,$mday,$mon,$year);
127
128=head1 DESCRIPTION
129
130These routines are the inverse of built-in perl fuctions localtime()
131and gmtime(). They accept a date as a six-element array, and return
132the corresponding time(2) value in seconds since the Epoch (Midnight,
133January 1, 1970). This value can be positive or negative.
134
135It is worth drawing particular attention to the expected ranges for
136the values provided. While the day of the month is expected to be in
137the range 1..31, the month should be in the range 0..11.
138This is consistent with the values returned from localtime() and gmtime().
139
140Strictly speaking, the year should also be specified in a form consistent
141with localtime(), i.e. the offset from 1900.
142In order to make the interpretation of the year easier for humans,
143however, who are more accustomed to seeing years as two-digit or four-digit
144values, the following conventions are followed:
145
146=over 4
147
148=item *
149
150Years greater than 999 are interpreted as being the actual year,
151rather than the offset from 1900. Thus, 1963 would indicate the year
90ca0aaa 152Martin Luther King won the Nobel prize, not the year 2863.
06ef4121
PC
153
154=item *
155
156Years in the range 100..999 are interpreted as offset from 1900,
157so that 112 indicates 2012. This rule also applies to years less than zero
158(but see note below regarding date range).
159
160=item *
161
162Years in the range 0..99 are interpreted as shorthand for years in the
163rolling "current century," defined as 50 years on either side of the current
164year. Thus, today, in 1999, 0 would refer to 2000, and 45 to 2045,
165but 55 would refer to 1955. Twenty years from now, 55 would instead refer
166to 2055. This is messy, but matches the way people currently think about
167two digit dates. Whenever possible, use an absolute four digit year instead.
168
169=back
170
171The scheme above allows interpretation of a wide range of dates, particularly
172if 4-digit years are used.
90ca0aaa 173
06ef4121
PC
174Please note, however, that the range of dates that can be actually be handled
175depends on the size of an integer (time_t) on a given platform.
176Currently, this is 32 bits for most systems, yielding an approximate range
177from Dec 1901 to Jan 2038.
178
179Both timelocal() and timegm() croak if given dates outside the supported
180range.
181
182=head1 IMPLEMENTATION
183
184These routines are quite efficient and yet are always guaranteed to agree
185with localtime() and gmtime(). We manage this by caching the start times
186of any months we've seen before. If we know the start time of the month,
187we can always calculate any time within the month. The start times
188themselves are guessed by successive approximation starting at the
189current time, since most dates seen in practice are close to the
190current date. Unlike algorithms that do a binary search (calling gmtime
191once for each bit of the time value, resulting in 32 calls), this algorithm
192calls it at most 6 times, and usually only once or twice. If you hit
193the month cache, of course, it doesn't call it at all.
194
195timelocal() is implemented using the same cache. We just assume that we're
196translating a GMT time, and then fudge it when we're done for the timezone
197and daylight savings arguments. Note that the timezone is evaluated for
198each date because countries occasionally change their official timezones.
199Assuming that localtime() corrects for these changes, this routine will
200also be correct. The daylight savings offset is currently assumed
201to be one hour.
202
203=head1 BUGS
204
205The whole scheme for interpreting two-digit years can be considered a bug.
206
207Note that the cache currently handles only years from 1900 through 2155.
208
209The proclivity to croak() is probably a bug.
210
211=cut