typos and other minor things
[p5sagit/p5-mst-13.2.git] / lib / Time / Local.pm
CommitLineData
a0d0e21e 1package Time::Local;
3b825e41 2use 5.006;
a0d0e21e 3require Exporter;
4use Carp;
e7ec2331 5use Config;
b75c8c73 6use strict;
326557bd 7use integer;
a0d0e21e 8
e7ec2331 9our $VERSION = '1.04';
b75c8c73 10our @ISA = qw( Exporter );
11our @EXPORT = qw( timegm timelocal );
12our @EXPORT_OK = qw( timegm_nocheck timelocal_nocheck );
a0d0e21e 13
326557bd 14my @MonthDays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
15
06ef4121 16# Determine breakpoint for rolling century
326557bd 17my $ThisYear = (localtime())[5];
18my $Breakpoint = ($ThisYear + 50) % 100;
19my $NextCentury = $ThisYear - $ThisYear % 100;
20 $NextCentury += 100 if $Breakpoint < 50;
21my $Century = $NextCentury - 100;
67627c52 22my $SecOff = 0;
326557bd 23
24my (%Options, %Cheat);
25
67627c52 26my $MaxInt = ((1<<(8 * $Config{intsize} - 2))-1)*2 + 1;
27my $MaxDay = int(($MaxInt-43200)/86400)-1;
28
326557bd 29# Determine the EPOC day for this machine
88db9e9a 30my $Epoc = 0;
31if ($^O eq 'vos') {
32# work around posix-977 -- VOS doesn't handle dates in
33# the range 1970-1980.
34 $Epoc = _daygm((0, 0, 0, 1, 0, 70, 4, 0));
67627c52 35}
36elsif ($^O eq 'MacOS') {
37 no integer;
38
39 $MaxDay *=2 if $^O eq 'MacOS'; # time_t unsigned ... quick hack?
40 # MacOS time() is seconds since 1 Jan 1904, localtime
41 # so we need to calculate an offset to apply later
42 $Epoc = 693901;
43 $SecOff = timelocal(localtime(0)) - timelocal(gmtime(0));
44 $Epoc += _daygm(gmtime(0));
45}
46else {
88db9e9a 47 $Epoc = _daygm(gmtime(0));
48}
49
326557bd 50%Cheat=(); # clear the cache as epoc has changed
51
326557bd 52sub _daygm {
53 $_[3] + ($Cheat{pack("ss",@_[4,5])} ||= do {
54 my $month = ($_[4] + 10) % 12;
55 my $year = $_[5] + 1900 - $month/10;
56 365*$year + $year/4 - $year/100 + $year/400 + ($month*306 + 5)/10 - $Epoc
57 });
58}
59
60
61sub _timegm {
67627c52 62 my $sec = $SecOff + $_[0] + 60 * $_[1] + 3600 * $_[2];
63
64 no integer;
65
66 $sec + 86400 * &_daygm;
326557bd 67}
9bb8015a 68
e36f48eb 69
9bb8015a 70sub timegm {
326557bd 71 my ($sec,$min,$hour,$mday,$month,$year) = @_;
72
73 if ($year >= 1000) {
74 $year -= 1900;
75 }
76 elsif ($year < 100 and $year >= 0) {
77 $year += ($year > $Breakpoint) ? $Century : $NextCentury;
78 }
79
80 unless ($Options{no_range_check}) {
81 if (abs($year) >= 0x7fff) {
82 $year += 1900;
83 croak "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
84 }
85
86 croak "Month '$month' out of range 0..11" if $month > 11 or $month < 0;
87
88 my $md = $MonthDays[$month];
89 ++$md unless $month != 1 or $year % 4 or !($year % 400);
90
91 croak "Day '$mday' out of range 1..$md" if $mday > $md or $mday < 1;
92 croak "Hour '$hour' out of range 0..23" if $hour > 23 or $hour < 0;
93 croak "Minute '$min' out of range 0..59" if $min > 59 or $min < 0;
94 croak "Second '$sec' out of range 0..59" if $sec > 59 or $sec < 0;
06ef4121 95 }
326557bd 96
97 my $days = _daygm(undef, undef, undef, $mday, $month, $year);
98
99 unless ($Options{no_range_check} or abs($days) < $MaxDay) {
100 $year += 1900;
101 croak "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
06ef4121 102 }
326557bd 103
67627c52 104 $sec += $SecOff + 60*$min + 3600*$hour;
105
106 no integer;
107
108 $sec + 86400*$days;
9bb8015a 109}
110
326557bd 111
e36f48eb 112sub timegm_nocheck {
b75c8c73 113 local $Options{no_range_check} = 1;
e36f48eb 114 &timegm;
115}
116
326557bd 117
9bb8015a 118sub timelocal {
67627c52 119 no integer;
326557bd 120 my $ref_t = &timegm;
121 my $loc_t = _timegm(localtime($ref_t));
a0d0e21e 122
326557bd 123 # Is there a timezone offset from GMT or are we done
124 my $zone_off = $ref_t - $loc_t
125 or return $loc_t;
16bb4654 126
326557bd 127 # Adjust for timezone
128 $loc_t = $ref_t + $zone_off;
16bb4654 129
326557bd 130 # Are we close to a DST change or are we done
131 my $dst_off = $ref_t - _timegm(localtime($loc_t))
132 or return $loc_t;
133
134 # Adjust for DST change
13ef5feb 135 $loc_t += $dst_off;
136
137 # for a negative offset from GMT, and if the original date
138 # was a non-extent gap in a forward DST jump, we should
139 # now have the wrong answer - undo the DST adjust;
140
141 return $loc_t if $zone_off <= 0;
142
143 my ($s,$m,$h) = localtime($loc_t);
144 $loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2];
145
146 $loc_t;
a0d0e21e 147}
148
326557bd 149
e36f48eb 150sub timelocal_nocheck {
b75c8c73 151 local $Options{no_range_check} = 1;
e36f48eb 152 &timelocal;
153}
154
a0d0e21e 1551;
06ef4121 156
157__END__
158
159=head1 NAME
160
161Time::Local - efficiently compute time from local and GMT time
162
163=head1 SYNOPSIS
164
396e3838 165 $time = timelocal($sec,$min,$hour,$mday,$mon,$year);
166 $time = timegm($sec,$min,$hour,$mday,$mon,$year);
06ef4121 167
168=head1 DESCRIPTION
169
396e3838 170These routines are the inverse of built-in perl functions localtime()
06ef4121 171and gmtime(). They accept a date as a six-element array, and return
172the corresponding time(2) value in seconds since the Epoch (Midnight,
173January 1, 1970). This value can be positive or negative.
174
175It is worth drawing particular attention to the expected ranges for
eee32007 176the values provided. The value for the day of the month is the actual day
177(ie 1..31), while the month is the number of months since January (0..11).
06ef4121 178This is consistent with the values returned from localtime() and gmtime().
179
e36f48eb 180The timelocal() and timegm() functions perform range checking on the
396e3838 181input $sec, $min, $hour, $mday, and $mon values by default. If you'd
e36f48eb 182rather they didn't, you can explicitly import the timelocal_nocheck()
183and timegm_nocheck() functions.
ac54365a 184
e36f48eb 185 use Time::Local 'timelocal_nocheck';
3cb6de81 186
a1f33342 187 {
a1f33342 188 # The 365th day of 1999
e36f48eb 189 print scalar localtime timelocal_nocheck 0,0,0,365,0,99;
ac54365a 190
a1f33342 191 # The twenty thousandth day since 1970
e36f48eb 192 print scalar localtime timelocal_nocheck 0,0,0,20000,0,70;
ac54365a 193
a1f33342 194 # And even the 10,000,000th second since 1999!
e36f48eb 195 print scalar localtime timelocal_nocheck 10000000,0,0,1,0,99;
a1f33342 196 }
ac54365a 197
e36f48eb 198Your mileage may vary when trying these with minutes and hours,
ac54365a 199and it doesn't work at all for months.
200
06ef4121 201Strictly speaking, the year should also be specified in a form consistent
202with localtime(), i.e. the offset from 1900.
203In order to make the interpretation of the year easier for humans,
204however, who are more accustomed to seeing years as two-digit or four-digit
205values, the following conventions are followed:
206
207=over 4
208
209=item *
210
211Years greater than 999 are interpreted as being the actual year,
212rather than the offset from 1900. Thus, 1963 would indicate the year
90ca0aaa 213Martin Luther King won the Nobel prize, not the year 2863.
06ef4121 214
215=item *
216
217Years in the range 100..999 are interpreted as offset from 1900,
218so that 112 indicates 2012. This rule also applies to years less than zero
219(but see note below regarding date range).
220
221=item *
222
223Years in the range 0..99 are interpreted as shorthand for years in the
224rolling "current century," defined as 50 years on either side of the current
225year. Thus, today, in 1999, 0 would refer to 2000, and 45 to 2045,
226but 55 would refer to 1955. Twenty years from now, 55 would instead refer
227to 2055. This is messy, but matches the way people currently think about
228two digit dates. Whenever possible, use an absolute four digit year instead.
229
230=back
231
232The scheme above allows interpretation of a wide range of dates, particularly
233if 4-digit years are used.
90ca0aaa 234
06ef4121 235Please note, however, that the range of dates that can be actually be handled
236depends on the size of an integer (time_t) on a given platform.
237Currently, this is 32 bits for most systems, yielding an approximate range
238from Dec 1901 to Jan 2038.
239
240Both timelocal() and timegm() croak if given dates outside the supported
241range.
242
243=head1 IMPLEMENTATION
244
245These routines are quite efficient and yet are always guaranteed to agree
246with localtime() and gmtime(). We manage this by caching the start times
247of any months we've seen before. If we know the start time of the month,
248we can always calculate any time within the month. The start times
326557bd 249are calculated using a mathematical formula. Unlike other algorithms
250that do multiple calls to gmtime().
06ef4121 251
252timelocal() is implemented using the same cache. We just assume that we're
253translating a GMT time, and then fudge it when we're done for the timezone
254and daylight savings arguments. Note that the timezone is evaluated for
255each date because countries occasionally change their official timezones.
256Assuming that localtime() corrects for these changes, this routine will
326557bd 257also be correct.
06ef4121 258
259=head1 BUGS
260
261The whole scheme for interpreting two-digit years can be considered a bug.
262
06ef4121 263The proclivity to croak() is probably a bug.
264
265=cut
326557bd 266