Upgrade to Time::Local 1.12_01
[p5sagit/p5-mst-13.2.git] / lib / Time / Local.pm
CommitLineData
a0d0e21e 1package Time::Local;
1c41b6a4 2
a0d0e21e 3require Exporter;
4use Carp;
e7ec2331 5use Config;
b75c8c73 6use strict;
326557bd 7use integer;
a0d0e21e 8
1c41b6a4 9use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
1eed7ad1 10$VERSION = '1.12_01';
11$VERSION = eval $VERSION;
12@ISA = qw( Exporter );
13@EXPORT = qw( timegm timelocal );
14@EXPORT_OK = qw( timegm_nocheck timelocal_nocheck );
a0d0e21e 15
1eed7ad1 16my @MonthDays = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
326557bd 17
06ef4121 18# Determine breakpoint for rolling century
1eed7ad1 19my $ThisYear = ( localtime() )[5];
20my $Breakpoint = ( $ThisYear + 50 ) % 100;
21my $NextCentury = $ThisYear - $ThisYear % 100;
22$NextCentury += 100 if $Breakpoint < 50;
23my $Century = $NextCentury - 100;
24my $SecOff = 0;
326557bd 25
1eed7ad1 26my ( %Options, %Cheat );
326557bd 27
1eed7ad1 28use constant SECS_PER_MINUTE => 60;
29use constant SECS_PER_HOUR => 3600;
30use constant SECS_PER_DAY => 86400;
8f230aaa 31
1eed7ad1 32my $MaxInt = ( ( 1 << ( 8 * $Config{intsize} - 2 ) ) -1 ) * 2 + 1;
33my $MaxDay = int( ( $MaxInt - ( SECS_PER_DAY / 2 ) ) / SECS_PER_DAY ) - 1;
34
35if ( $^O eq 'MacOS' ) {
823a6996 36 # time_t is unsigned...
1eed7ad1 37 $MaxInt = ( 1 << ( 8 * $Config{intsize} ) ) - 1;
38}
39else {
40 $MaxInt = ( ( 1 << ( 8 * $Config{intsize} - 2 ) ) - 1 ) * 2 + 1;
823a6996 41}
67627c52 42
326557bd 43# Determine the EPOC day for this machine
88db9e9a 44my $Epoc = 0;
1eed7ad1 45if ( $^O eq 'vos' ) {
46 # work around posix-977 -- VOS doesn't handle dates in the range
47 # 1970-1980.
48 $Epoc = _daygm( 0, 0, 0, 1, 0, 70, 4, 0 );
67627c52 49}
1eed7ad1 50elsif ( $^O eq 'MacOS' ) {
51 $MaxDay *=2 if $^O eq 'MacOS'; # time_t unsigned ... quick hack?
52 # MacOS time() is seconds since 1 Jan 1904, localtime
53 # so we need to calculate an offset to apply later
54 $Epoc = 693901;
55 $SecOff = timelocal( localtime(0)) - timelocal( gmtime(0) ) ;
56 $Epoc += _daygm( gmtime(0) );
67627c52 57}
58else {
1eed7ad1 59 $Epoc = _daygm( gmtime(0) );
88db9e9a 60}
61
1eed7ad1 62%Cheat = (); # clear the cache as epoc has changed
326557bd 63
326557bd 64sub _daygm {
326557bd 65
1eed7ad1 66 # This is written in such a byzantine way in order to avoid
67 # lexical variables and sub calls, for speed
68 return $_[3] + (
69 $Cheat{ pack( 'ss', @_[ 4, 5 ] ) } ||= do {
70 my $month = ( $_[4] + 10 ) % 12;
71 my $year = $_[5] + 1900 - $month / 10;
72
73 ( ( 365 * $year )
74 + ( $year / 4 )
75 - ( $year / 100 )
76 + ( $year / 400 )
77 + ( ( ( $month * 306 ) + 5 ) / 10 )
78 )
79 - $Epoc;
80 }
81 );
326557bd 82}
9bb8015a 83
1eed7ad1 84sub _timegm {
85 my $sec =
86 $SecOff + $_[0] + ( SECS_PER_MINUTE * $_[1] ) + ( SECS_PER_HOUR * $_[2] );
e36f48eb 87
1eed7ad1 88 return $sec + ( SECS_PER_DAY * &_daygm );
823a6996 89}
90
9bb8015a 91sub timegm {
1eed7ad1 92 my ( $sec, $min, $hour, $mday, $month, $year ) = @_;
326557bd 93
1eed7ad1 94 if ( $year >= 1000 ) {
95 $year -= 1900;
326557bd 96 }
1eed7ad1 97 elsif ( $year < 100 and $year >= 0 ) {
98 $year += ( $year > $Breakpoint ) ? $Century : $NextCentury;
326557bd 99 }
100
1eed7ad1 101 unless ( $Options{no_range_check} ) {
102 if ( abs($year) >= 0x7fff ) {
103 $year += 1900;
104 croak
105 "Cannot handle date ($sec, $min, $hour, $mday, $month, *$year*)";
106 }
326557bd 107
1eed7ad1 108 croak "Month '$month' out of range 0..11"
109 if $month > 11
110 or $month < 0;
326557bd 111
112 my $md = $MonthDays[$month];
1eed7ad1 113 ++$md
114 unless $month != 1 or $year % 4 or !( $year % 400 );
115
116 croak "Day '$mday' out of range 1..$md" if $mday > $md or $mday < 1;
117 croak "Hour '$hour' out of range 0..23" if $hour > 23 or $hour < 0;
118 croak "Minute '$min' out of range 0..59" if $min > 59 or $min < 0;
119 croak "Second '$sec' out of range 0..59" if $sec > 59 or $sec < 0;
06ef4121 120 }
326557bd 121
1eed7ad1 122 my $days = _daygm( undef, undef, undef, $mday, $month, $year );
123
124 unless ($Options{no_range_check} or abs($days) < $MaxDay) {
125 my $msg = '';
126 $msg .= "Day too big - $days > $MaxDay\n" if $days > $MaxDay;
127
326557bd 128 $year += 1900;
1eed7ad1 129 $msg .= "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
326557bd 130
1eed7ad1 131 croak $msg;
132 }
67627c52 133
1eed7ad1 134 return $sec
135 + $SecOff
136 + ( SECS_PER_MINUTE * $min )
137 + ( SECS_PER_HOUR * $hour )
138 + ( SECS_PER_DAY * $days );
9bb8015a 139}
140
e36f48eb 141sub timegm_nocheck {
b75c8c73 142 local $Options{no_range_check} = 1;
1eed7ad1 143 return &timegm;
e36f48eb 144}
145
9bb8015a 146sub timelocal {
326557bd 147 my $ref_t = &timegm;
1eed7ad1 148 my $loc_t = _timegm( localtime($ref_t) );
823a6996 149
1eed7ad1 150 # Is there a timezone offset from GMT or are we done?
326557bd 151 my $zone_off = $ref_t - $loc_t
1eed7ad1 152 or return $loc_t;
16bb4654 153
823a6996 154 # This hack is needed to always pick the first matching time
155 # during a DST change when time would otherwise be ambiguous
1eed7ad1 156 $zone_off -= SECS_PER_HOUR if $ref_t >= SECS_PER_HOUR;
823a6996 157
326557bd 158 # Adjust for timezone
159 $loc_t = $ref_t + $zone_off;
16bb4654 160
326557bd 161 # Are we close to a DST change or are we done
1eed7ad1 162 my $dst_off = $ref_t - _timegm( localtime($loc_t) )
163 or return $loc_t;
326557bd 164
165 # Adjust for DST change
13ef5feb 166 $loc_t += $dst_off;
167
823a6996 168 return $loc_t if $dst_off >= 0;
169
13ef5feb 170 # for a negative offset from GMT, and if the original date
171 # was a non-extent gap in a forward DST jump, we should
172 # now have the wrong answer - undo the DST adjust;
1eed7ad1 173 my ( $s, $m, $h ) = localtime($loc_t);
13ef5feb 174 $loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2];
175
1eed7ad1 176 return $loc_t;
a0d0e21e 177}
178
e36f48eb 179sub timelocal_nocheck {
b75c8c73 180 local $Options{no_range_check} = 1;
1eed7ad1 181 return &timelocal;
e36f48eb 182}
183
a0d0e21e 1841;
06ef4121 185
186__END__
187
188=head1 NAME
189
190Time::Local - efficiently compute time from local and GMT time
191
192=head1 SYNOPSIS
193
396e3838 194 $time = timelocal($sec,$min,$hour,$mday,$mon,$year);
195 $time = timegm($sec,$min,$hour,$mday,$mon,$year);
06ef4121 196
197=head1 DESCRIPTION
198
396e3838 199These routines are the inverse of built-in perl functions localtime()
06ef4121 200and gmtime(). They accept a date as a six-element array, and return
1c41b6a4 201the corresponding time(2) value in seconds since the system epoch
4ab0373f 202(Midnight, January 1, 1970 GMT on Unix, for example). This value can
1c41b6a4 203be positive or negative, though POSIX only requires support for
204positive values, so dates before the system's epoch may not work on
205all operating systems.
06ef4121 206
207It is worth drawing particular attention to the expected ranges for
1eed7ad1 208the values provided. The value for the day of the month is the actual
209day (ie 1..31), while the month is the number of months since January
210(0..11). This is consistent with the values returned from localtime()
211and gmtime().
06ef4121 212
e36f48eb 213The timelocal() and timegm() functions perform range checking on the
1eed7ad1 214input $sec, $min, $hour, $mday, and $mon values by default. If you
215are confident that your data is good, you can explicitly import the
216timelocal_nocheck() and timegm_nocheck() functions, which may provide
217a small performance improvement.
ac54365a 218
1eed7ad1 219 use Time::Local 'timelocal_nocheck';
ac54365a 220
1eed7ad1 221 # The 365th day of 1999
222 print scalar localtime timelocal_nocheck 0,0,0,365,0,99;
ac54365a 223
1eed7ad1 224Strictly speaking, the year should also be specified in a form
225consistent with localtime(), i.e. the offset from 1900. In order to
226make the interpretation of the year easier for humans, however, who
227are more accustomed to seeing years as two-digit or four-digit values,
228the following conventions are followed:
06ef4121 229
230=over 4
231
232=item *
233
234Years greater than 999 are interpreted as being the actual year,
5847cf89 235rather than the offset from 1900. Thus, 1964 would indicate the year
236Martin Luther King won the Nobel prize, not the year 3864.
06ef4121 237
238=item *
239
240Years in the range 100..999 are interpreted as offset from 1900,
241so that 112 indicates 2012. This rule also applies to years less than zero
242(but see note below regarding date range).
243
244=item *
245
246Years in the range 0..99 are interpreted as shorthand for years in the
1eed7ad1 247rolling "current century," defined as 50 years on either side of the
248current year. Thus, today, in 1999, 0 would refer to 2000, and 45 to
2492045, but 55 would refer to 1955. Twenty years from now, 55 would
250instead refer to 2055. This is messy, but matches the way people
251currently think about two digit dates. Whenever possible, use an
252absolute four digit year instead.
06ef4121 253
254=back
255
1eed7ad1 256The scheme above allows interpretation of a wide range of dates,
257particularly if 4-digit years are used.
90ca0aaa 258
1eed7ad1 259Please note, however, that the range of dates that can be actually be
260handled depends on the size of an integer (time_t) on a given
261platform. Currently, this is 32 bits for most systems, yielding an
262approximate range from Dec 1901 to Jan 2038.
06ef4121 263
1eed7ad1 264Both timelocal() and timegm() croak if given dates outside the
265supported range.
06ef4121 266
823a6996 267=head2 Ambiguous Local Times (DST)
268
269Because of DST changes, there are many time zones where the same local
4ab0373f 270time occurs for two different GMT times on the same day. For example,
823a6996 271in the "Europe/Paris" time zone, the local time of 2001-10-28 02:30:00
4ab0373f 272can represent either 2001-10-28 00:30:00 GMT, B<or> 2001-10-28
27301:30:00 GMT.
823a6996 274
275When given an ambiguous local time, the timelocal() function should
4ab0373f 276always return the epoch for the I<earlier> of the two possible GMT
823a6996 277times.
278
4ab0373f 279=head2 Non-Existent Local Times (DST)
280
281When a DST change causes a locale clock to skip one hour forward,
282there will be an hour's worth of local times that don't exist. Again,
283for the "Europe/Paris" time zone, the local clock jumped from
2842001-03-25 01:59:59 to 2001-03-25 03:00:00.
285
286If the timelocal() function is given a non-existent local time, it
287will simply return an epoch value for the time one hour later.
288
823a6996 289=head2 Negative Epoch Values
290
291Negative epoch (time_t) values are not officially supported by the
292POSIX standards, so this module's tests do not test them. On some
293systems, they are known not to work. These include MacOS (pre-OSX)
294and Win32.
295
296On systems which do support negative epoch values, this module should
297be able to cope with dates before the start of the epoch, down the
298minimum value of time_t for the system.
299
06ef4121 300=head1 IMPLEMENTATION
301
1eed7ad1 302These routines are quite efficient and yet are always guaranteed to
303agree with localtime() and gmtime(). We manage this by caching the
304start times of any months we've seen before. If we know the start
305time of the month, we can always calculate any time within the month.
306The start times are calculated using a mathematical formula. Unlike
307other algorithms that do multiple calls to gmtime().
06ef4121 308
1eed7ad1 309timelocal() is implemented using the same cache. We just assume that
310we're translating a GMT time, and then fudge it when we're done for
311the timezone and daylight savings arguments. Note that the timezone
312is evaluated for each date because countries occasionally change their
313official timezones. Assuming that localtime() corrects for these
314changes, this routine will also be correct.
06ef4121 315
316=head1 BUGS
317
1eed7ad1 318The whole scheme for interpreting two-digit years can be considered a
319bug.
06ef4121 320
1c41b6a4 321=head1 SUPPORT
322
1eed7ad1 323Support for this module is provided via the datetime@perl.org email
324list. See http://lists.perl.org/ for more details.
1c41b6a4 325
4ab0373f 326Please submit bugs using the RT system at rt.cpan.org, or as a last
327resort, to the datetime@perl.org list.
1c41b6a4 328
329=head1 AUTHOR
330
331This module is based on a Perl 4 library, timelocal.pl, that was
332included with Perl 4.036, and was most likely written by Tom
333Christiansen.
334
335The current version was written by Graham Barr.
336
337It is now being maintained separately from the Perl core by Dave
338Rolsky, <autarch@urth.org>.
339
06ef4121 340=cut
326557bd 341