Add lib to @INC so that ./perl t/op/local.t works.
[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 );
d15eb09c 10$VERSION = '1.15';
e6f8b432 11
1eed7ad1 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
bd4bdc1b 94 # Need to check leap year before altering the value
4cdff51d 95 my $leap_year = _is_leap_year($year);
bd4bdc1b 96
1eed7ad1 97 if ( $year >= 1000 ) {
98 $year -= 1900;
326557bd 99 }
1eed7ad1 100 elsif ( $year < 100 and $year >= 0 ) {
101 $year += ( $year > $Breakpoint ) ? $Century : $NextCentury;
326557bd 102 }
103
1eed7ad1 104 unless ( $Options{no_range_check} ) {
105 if ( abs($year) >= 0x7fff ) {
106 $year += 1900;
107 croak
108 "Cannot handle date ($sec, $min, $hour, $mday, $month, *$year*)";
109 }
326557bd 110
1eed7ad1 111 croak "Month '$month' out of range 0..11"
112 if $month > 11
113 or $month < 0;
326557bd 114
115 my $md = $MonthDays[$month];
1eed7ad1 116 ++$md
bd4bdc1b 117 if $month == 1 && $leap_year;
1eed7ad1 118
119 croak "Day '$mday' out of range 1..$md" if $mday > $md or $mday < 1;
120 croak "Hour '$hour' out of range 0..23" if $hour > 23 or $hour < 0;
121 croak "Minute '$min' out of range 0..59" if $min > 59 or $min < 0;
122 croak "Second '$sec' out of range 0..59" if $sec > 59 or $sec < 0;
06ef4121 123 }
326557bd 124
1eed7ad1 125 my $days = _daygm( undef, undef, undef, $mday, $month, $year );
126
127 unless ($Options{no_range_check} or abs($days) < $MaxDay) {
128 my $msg = '';
129 $msg .= "Day too big - $days > $MaxDay\n" if $days > $MaxDay;
130
326557bd 131 $year += 1900;
1eed7ad1 132 $msg .= "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
326557bd 133
1eed7ad1 134 croak $msg;
135 }
67627c52 136
1eed7ad1 137 return $sec
138 + $SecOff
139 + ( SECS_PER_MINUTE * $min )
140 + ( SECS_PER_HOUR * $hour )
141 + ( SECS_PER_DAY * $days );
9bb8015a 142}
143
d15eb09c 144sub _is_leap_year {
145 return 0 if $_[0] % 4;
146 return 1 if $_[0] % 100;
147 return 0 if $_[0] % 400;
148
149 return 1;
150}
151
e36f48eb 152sub timegm_nocheck {
b75c8c73 153 local $Options{no_range_check} = 1;
1eed7ad1 154 return &timegm;
e36f48eb 155}
156
9bb8015a 157sub timelocal {
326557bd 158 my $ref_t = &timegm;
e6f8b432 159 my $loc_for_ref_t = _timegm( localtime($ref_t) );
16bb4654 160
e6f8b432 161 my $zone_off = $loc_for_ref_t - $ref_t
162 or return $loc_for_ref_t;
823a6996 163
326557bd 164 # Adjust for timezone
e6f8b432 165 my $loc_t = $ref_t - $zone_off;
16bb4654 166
326557bd 167 # Are we close to a DST change or are we done
e6f8b432 168 my $dst_off = $ref_t - _timegm( localtime($loc_t) );
169
170 # If this evaluates to true, it means that the value in $loc_t is
171 # the _second_ hour after a DST change where the local time moves
172 # backward.
173 if ( ! $dst_off &&
174 ( ( $ref_t - SECS_PER_HOUR ) - _timegm( localtime( $loc_t - SECS_PER_HOUR ) ) < 0 )
175 ) {
176 return $loc_t - SECS_PER_HOUR;
177 }
326557bd 178
179 # Adjust for DST change
13ef5feb 180 $loc_t += $dst_off;
181
e6f8b432 182 return $loc_t if $dst_off > 0;
823a6996 183
e6f8b432 184 # If the original date was a non-extent gap in a forward DST jump,
185 # we should now have the wrong answer - undo the DST adjustment
1eed7ad1 186 my ( $s, $m, $h ) = localtime($loc_t);
13ef5feb 187 $loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2];
188
1eed7ad1 189 return $loc_t;
a0d0e21e 190}
191
e36f48eb 192sub timelocal_nocheck {
b75c8c73 193 local $Options{no_range_check} = 1;
1eed7ad1 194 return &timelocal;
e36f48eb 195}
196
a0d0e21e 1971;
06ef4121 198
199__END__
200
201=head1 NAME
202
203Time::Local - efficiently compute time from local and GMT time
204
205=head1 SYNOPSIS
206
396e3838 207 $time = timelocal($sec,$min,$hour,$mday,$mon,$year);
208 $time = timegm($sec,$min,$hour,$mday,$mon,$year);
06ef4121 209
210=head1 DESCRIPTION
211
e6f8b432 212This module provides functions that are the inverse of built-in perl
213functions C<localtime()> and C<gmtime()>. They accept a date as a
214six-element array, and return the corresponding C<time(2)> value in
215seconds since the system epoch (Midnight, January 1, 1970 GMT on Unix,
216for example). This value can be positive or negative, though POSIX
217only requires support for positive values, so dates before the
218system's epoch may not work on all operating systems.
06ef4121 219
220It is worth drawing particular attention to the expected ranges for
e6f8b432 221the values provided. The value for the day of the month is the actual
1eed7ad1 222day (ie 1..31), while the month is the number of months since January
e6f8b432 223(0..11). This is consistent with the values returned from
224C<localtime()> and C<gmtime()>.
225
226=head1 FUNCTIONS
227
228This module exports two functions by default, C<timelocal()> and
229C<timegm()>.
06ef4121 230
e6f8b432 231The C<timelocal()> and C<timegm()> functions perform range checking on
232the input $sec, $min, $hour, $mday, and $mon values by default.
233
234If you are working with data you know to be valid, you can speed your
235code up by using the "nocheck" variants, C<timelocal_nocheck()> and
236C<timegm_nocheck()>. These variants must be explicitly imported.
ac54365a 237
1eed7ad1 238 use Time::Local 'timelocal_nocheck';
ac54365a 239
1eed7ad1 240 # The 365th day of 1999
241 print scalar localtime timelocal_nocheck 0,0,0,365,0,99;
ac54365a 242
e6f8b432 243If you supply data which is not valid (month 27, second 1,000) the
244results will be unpredictable (so don't do that).
245
246=head2 Year Value Interpretation
247
248Strictly speaking, the year should be specified in a form consistent
249with C<localtime()>, i.e. the offset from 1900. In order to make the
250interpretation of the year easier for humans, however, who are more
251accustomed to seeing years as two-digit or four-digit values, the
252following conventions are followed:
06ef4121 253
254=over 4
255
256=item *
257
258Years greater than 999 are interpreted as being the actual year,
e6f8b432 259rather than the offset from 1900. Thus, 1964 would indicate the year
5847cf89 260Martin Luther King won the Nobel prize, not the year 3864.
06ef4121 261
262=item *
263
e6f8b432 264Years in the range 100..999 are interpreted as offset from 1900, so
265that 112 indicates 2012. This rule also applies to years less than
266zero (but see note below regarding date range).
06ef4121 267
268=item *
269
270Years in the range 0..99 are interpreted as shorthand for years in the
1eed7ad1 271rolling "current century," defined as 50 years on either side of the
e6f8b432 272current year. Thus, today, in 1999, 0 would refer to 2000, and 45 to
2732045, but 55 would refer to 1955. Twenty years from now, 55 would
274instead refer to 2055. This is messy, but matches the way people
275currently think about two digit dates. Whenever possible, use an
1eed7ad1 276absolute four digit year instead.
06ef4121 277
278=back
279
1eed7ad1 280The scheme above allows interpretation of a wide range of dates,
281particularly if 4-digit years are used.
90ca0aaa 282
e6f8b432 283=head2 Limits of time_t
284
285The range of dates that can be actually be handled depends on the size
286of C<time_t> (usually a signed integer) on the given
1eed7ad1 287platform. Currently, this is 32 bits for most systems, yielding an
288approximate range from Dec 1901 to Jan 2038.
06ef4121 289
e6f8b432 290Both C<timelocal()> and C<timegm()> croak if given dates outside the
1eed7ad1 291supported range.
06ef4121 292
823a6996 293=head2 Ambiguous Local Times (DST)
294
295Because of DST changes, there are many time zones where the same local
e6f8b432 296time occurs for two different GMT times on the same day. For example,
823a6996 297in the "Europe/Paris" time zone, the local time of 2001-10-28 02:30:00
4ab0373f 298can represent either 2001-10-28 00:30:00 GMT, B<or> 2001-10-28
29901:30:00 GMT.
823a6996 300
301When given an ambiguous local time, the timelocal() function should
4ab0373f 302always return the epoch for the I<earlier> of the two possible GMT
823a6996 303times.
304
4ab0373f 305=head2 Non-Existent Local Times (DST)
306
307When a DST change causes a locale clock to skip one hour forward,
e6f8b432 308there will be an hour's worth of local times that don't exist. Again,
4ab0373f 309for the "Europe/Paris" time zone, the local clock jumped from
3102001-03-25 01:59:59 to 2001-03-25 03:00:00.
311
e6f8b432 312If the C<timelocal()> function is given a non-existent local time, it
4ab0373f 313will simply return an epoch value for the time one hour later.
314
823a6996 315=head2 Negative Epoch Values
316
e6f8b432 317Negative epoch (C<time_t>) values are not officially supported by the
318POSIX standards, so this module's tests do not test them. On some
319systems, they are known not to work. These include MacOS (pre-OSX) and
320Win32.
823a6996 321
322On systems which do support negative epoch values, this module should
323be able to cope with dates before the start of the epoch, down the
324minimum value of time_t for the system.
325
06ef4121 326=head1 IMPLEMENTATION
327
1eed7ad1 328These routines are quite efficient and yet are always guaranteed to
e6f8b432 329agree with C<localtime()> and C<gmtime()>. We manage this by caching
330the start times of any months we've seen before. If we know the start
1eed7ad1 331time of the month, we can always calculate any time within the month.
332The start times are calculated using a mathematical formula. Unlike
e6f8b432 333other algorithms that do multiple calls to C<gmtime()>.
06ef4121 334
e6f8b432 335The C<timelocal()> function is implemented using the same cache. We
336just assume that we're translating a GMT time, and then fudge it when
337we're done for the timezone and daylight savings arguments. Note that
338the timezone is evaluated for each date because countries occasionally
339change their official timezones. Assuming that C<localtime()> corrects
340for these changes, this routine will also be correct.
06ef4121 341
342=head1 BUGS
343
1eed7ad1 344The whole scheme for interpreting two-digit years can be considered a
345bug.
06ef4121 346
1c41b6a4 347=head1 SUPPORT
348
1eed7ad1 349Support for this module is provided via the datetime@perl.org email
e6f8b432 350list. See http://lists.perl.org/ for more details.
1c41b6a4 351
e6f8b432 352Please submit bugs to the CPAN RT system at
353http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Time-Local or via email
354at bug-time-local@rt.cpan.org.
1c41b6a4 355
356=head1 AUTHOR
357
358This module is based on a Perl 4 library, timelocal.pl, that was
359included with Perl 4.036, and was most likely written by Tom
360Christiansen.
361
362The current version was written by Graham Barr.
363
364It is now being maintained separately from the Perl core by Dave
365Rolsky, <autarch@urth.org>.
366
06ef4121 367=cut