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