xref: /openbsd-src/gnu/usr.bin/perl/cpan/Time-Local/lib/Time/Local.pm (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1package Time::Local;
2
3require Exporter;
4use Carp;
5use Config;
6use strict;
7
8use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
9$VERSION   = '1.2300';
10
11@ISA       = qw( Exporter );
12@EXPORT    = qw( timegm timelocal );
13@EXPORT_OK = qw( timegm_nocheck timelocal_nocheck );
14
15my @MonthDays = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
16
17# Determine breakpoint for rolling century
18my $ThisYear    = ( localtime() )[5];
19my $Breakpoint  = ( $ThisYear + 50 ) % 100;
20my $NextCentury = $ThisYear - $ThisYear % 100;
21$NextCentury += 100 if $Breakpoint < 50;
22my $Century = $NextCentury - 100;
23my $SecOff  = 0;
24
25my ( %Options, %Cheat );
26
27use constant SECS_PER_MINUTE => 60;
28use constant SECS_PER_HOUR   => 3600;
29use constant SECS_PER_DAY    => 86400;
30
31my $MaxDay;
32if ($] < 5.012000) {
33    my $MaxInt;
34    if ( $^O eq 'MacOS' ) {
35        # time_t is unsigned...
36        $MaxInt = ( 1 << ( 8 * $Config{ivsize} ) ) - 1;
37    }
38    else {
39        $MaxInt = ( ( 1 << ( 8 * $Config{ivsize} - 2 ) ) - 1 ) * 2 + 1;
40    }
41
42    $MaxDay = int( ( $MaxInt - ( SECS_PER_DAY / 2 ) ) / SECS_PER_DAY ) - 1;
43}
44else {
45    # recent localtime()'s limit is the year 2**31
46    $MaxDay = 365 * (2**31);
47}
48
49# Determine the EPOC day for this machine
50my $Epoc = 0;
51if ( $^O eq 'vos' ) {
52    # work around posix-977 -- VOS doesn't handle dates in the range
53    # 1970-1980.
54    $Epoc = _daygm( 0, 0, 0, 1, 0, 70, 4, 0 );
55}
56elsif ( $^O eq 'MacOS' ) {
57    $MaxDay *=2 if $^O eq 'MacOS';  # time_t unsigned ... quick hack?
58    # MacOS time() is seconds since 1 Jan 1904, localtime
59    # so we need to calculate an offset to apply later
60    $Epoc = 693901;
61    $SecOff = timelocal( localtime(0)) - timelocal( gmtime(0) ) ;
62    $Epoc += _daygm( gmtime(0) );
63}
64else {
65    $Epoc = _daygm( gmtime(0) );
66}
67
68%Cheat = ();    # clear the cache as epoc has changed
69
70sub _daygm {
71
72    # This is written in such a byzantine way in order to avoid
73    # lexical variables and sub calls, for speed
74    return $_[3] + (
75        $Cheat{ pack( 'ss', @_[ 4, 5 ] ) } ||= do {
76            my $month = ( $_[4] + 10 ) % 12;
77            my $year  = $_[5] + 1900 - int($month / 10);
78
79            ( ( 365 * $year )
80              + int( $year / 4 )
81              - int( $year / 100 )
82              + int( $year / 400 )
83              + int( ( ( $month * 306 ) + 5 ) / 10 )
84            )
85            - $Epoc;
86        }
87    );
88}
89
90sub _timegm {
91    my $sec =
92        $SecOff + $_[0] + ( SECS_PER_MINUTE * $_[1] ) + ( SECS_PER_HOUR * $_[2] );
93
94    return $sec + ( SECS_PER_DAY * &_daygm );
95}
96
97sub timegm {
98    my ( $sec, $min, $hour, $mday, $month, $year ) = @_;
99
100    if ( $year >= 1000 ) {
101        $year -= 1900;
102    }
103    elsif ( $year < 100 and $year >= 0 ) {
104        $year += ( $year > $Breakpoint ) ? $Century : $NextCentury;
105    }
106
107    unless ( $Options{no_range_check} ) {
108        croak "Month '$month' out of range 0..11"
109            if $month > 11
110            or $month < 0;
111
112    my $md = $MonthDays[$month];
113        ++$md
114            if $month == 1 && _is_leap_year( $year + 1900 );
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 >= 60  or $sec < 0;
120    }
121
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
128        $year += 1900;
129        $msg .=  "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
130
131        croak $msg;
132    }
133
134    return $sec
135           + $SecOff
136           + ( SECS_PER_MINUTE * $min )
137           + ( SECS_PER_HOUR * $hour )
138           + ( SECS_PER_DAY * $days );
139}
140
141sub _is_leap_year {
142    return 0 if $_[0] % 4;
143    return 1 if $_[0] % 100;
144    return 0 if $_[0] % 400;
145
146    return 1;
147}
148
149sub timegm_nocheck {
150    local $Options{no_range_check} = 1;
151    return &timegm;
152}
153
154sub timelocal {
155    my $ref_t = &timegm;
156    my $loc_for_ref_t = _timegm( localtime($ref_t) );
157
158    my $zone_off = $loc_for_ref_t - $ref_t
159        or return $loc_for_ref_t;
160
161    # Adjust for timezone
162    my $loc_t = $ref_t - $zone_off;
163
164    # Are we close to a DST change or are we done
165    my $dst_off = $ref_t - _timegm( localtime($loc_t) );
166
167    # If this evaluates to true, it means that the value in $loc_t is
168    # the _second_ hour after a DST change where the local time moves
169    # backward.
170    if ( ! $dst_off &&
171         ( ( $ref_t - SECS_PER_HOUR ) - _timegm( localtime( $loc_t - SECS_PER_HOUR ) ) < 0 )
172       ) {
173        return $loc_t - SECS_PER_HOUR;
174    }
175
176    # Adjust for DST change
177    $loc_t += $dst_off;
178
179    return $loc_t if $dst_off > 0;
180
181    # If the original date was a non-extent gap in a forward DST jump,
182    # we should now have the wrong answer - undo the DST adjustment
183    my ( $s, $m, $h ) = localtime($loc_t);
184    $loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2];
185
186    return $loc_t;
187}
188
189sub timelocal_nocheck {
190    local $Options{no_range_check} = 1;
191    return &timelocal;
192}
193
1941;
195
196__END__
197
198=head1 NAME
199
200Time::Local - efficiently compute time from local and GMT time
201
202=head1 SYNOPSIS
203
204    $time = timelocal( $sec, $min, $hour, $mday, $mon, $year );
205    $time = timegm( $sec, $min, $hour, $mday, $mon, $year );
206
207=head1 DESCRIPTION
208
209This module provides functions that are the inverse of built-in perl
210functions C<localtime()> and C<gmtime()>. They accept a date as a
211six-element array, and return the corresponding C<time(2)> value in
212seconds since the system epoch (Midnight, January 1, 1970 GMT on Unix,
213for example). This value can be positive or negative, though POSIX
214only requires support for positive values, so dates before the
215system's epoch may not work on all operating systems.
216
217It is worth drawing particular attention to the expected ranges for
218the values provided. The value for the day of the month is the actual
219day (ie 1..31), while the month is the number of months since January
220(0..11). This is consistent with the values returned from
221C<localtime()> and C<gmtime()>.
222
223=head1 FUNCTIONS
224
225=head2 C<timelocal()> and C<timegm()>
226
227This module exports two functions by default, C<timelocal()> and
228C<timegm()>.
229
230The C<timelocal()> and C<timegm()> functions perform range checking on
231the input $sec, $min, $hour, $mday, and $mon values by default.
232
233=head2 C<timelocal_nocheck()> and C<timegm_nocheck()>
234
235If you are working with data you know to be valid, you can speed your
236code up by using the "nocheck" variants, C<timelocal_nocheck()> and
237C<timegm_nocheck()>. These variants must be explicitly imported.
238
239    use Time::Local 'timelocal_nocheck';
240
241    # The 365th day of 1999
242    print scalar localtime timelocal_nocheck( 0, 0, 0, 365, 0, 99 );
243
244If you supply data which is not valid (month 27, second 1,000) the
245results will be unpredictable (so don't do that).
246
247=head2 Year Value Interpretation
248
249Strictly speaking, the year should be specified in a form consistent
250with C<localtime()>, i.e. the offset from 1900. In order to make the
251interpretation of the year easier for humans, however, who are more
252accustomed to seeing years as two-digit or four-digit values, the
253following conventions are followed:
254
255=over 4
256
257=item *
258
259Years greater than 999 are interpreted as being the actual year,
260rather than the offset from 1900. Thus, 1964 would indicate the year
261Martin Luther King won the Nobel prize, not the year 3864.
262
263=item *
264
265Years in the range 100..999 are interpreted as offset from 1900, so
266that 112 indicates 2012. This rule also applies to years less than
267zero (but see note below regarding date range).
268
269=item *
270
271Years in the range 0..99 are interpreted as shorthand for years in the
272rolling "current century," defined as 50 years on either side of the
273current year. Thus, today, in 1999, 0 would refer to 2000, and 45 to
2742045, but 55 would refer to 1955. Twenty years from now, 55 would
275instead refer to 2055. This is messy, but matches the way people
276currently think about two digit dates. Whenever possible, use an
277absolute four digit year instead.
278
279=back
280
281The scheme above allows interpretation of a wide range of dates,
282particularly if 4-digit years are used.
283
284=head2 Limits of time_t
285
286On perl versions older than 5.12.0, the range of dates that can be
287actually be handled depends on the size of C<time_t> (usually a signed
288integer) on the given platform. Currently, this is 32 bits for most
289systems, yielding an approximate range from Dec 1901 to Jan 2038.
290
291Both C<timelocal()> and C<timegm()> croak if given dates outside the
292supported range.
293
294As of version 5.12.0, perl has stopped using the underlying time
295library of the operating system it's running on and has its own
296implementation of those routines with a safe range of at least
297+/ 2**52 (about 142 million years).
298
299=head2 Ambiguous Local Times (DST)
300
301Because of DST changes, there are many time zones where the same local
302time occurs for two different GMT times on the same day. For example,
303in the "Europe/Paris" time zone, the local time of 2001-10-28 02:30:00
304can represent either 2001-10-28 00:30:00 GMT, B<or> 2001-10-28
30501:30:00 GMT.
306
307When given an ambiguous local time, the timelocal() function should
308always return the epoch for the I<earlier> of the two possible GMT
309times.
310
311=head2 Non-Existent Local Times (DST)
312
313When a DST change causes a locale clock to skip one hour forward,
314there will be an hour's worth of local times that don't exist. Again,
315for the "Europe/Paris" time zone, the local clock jumped from
3162001-03-25 01:59:59 to 2001-03-25 03:00:00.
317
318If the C<timelocal()> function is given a non-existent local time, it
319will simply return an epoch value for the time one hour later.
320
321=head2 Negative Epoch Values
322
323On perl version 5.12.0 and newer, negative epoch values are fully
324supported.
325
326On older versions of perl, negative epoch (C<time_t>) values, which
327are not officially supported by the POSIX standards, are known not to
328work on some systems. These include MacOS (pre-OSX) and Win32.
329
330On systems which do support negative epoch values, this module should
331be able to cope with dates before the start of the epoch, down the
332minimum value of time_t for the system.
333
334=head1 IMPLEMENTATION
335
336These routines are quite efficient and yet are always guaranteed to
337agree with C<localtime()> and C<gmtime()>. We manage this by caching
338the start times of any months we've seen before. If we know the start
339time of the month, we can always calculate any time within the month.
340The start times are calculated using a mathematical formula. Unlike
341other algorithms that do multiple calls to C<gmtime()>.
342
343The C<timelocal()> function is implemented using the same cache. We
344just assume that we're translating a GMT time, and then fudge it when
345we're done for the timezone and daylight savings arguments. Note that
346the timezone is evaluated for each date because countries occasionally
347change their official timezones. Assuming that C<localtime()> corrects
348for these changes, this routine will also be correct.
349
350=head1 BUGS
351
352The whole scheme for interpreting two-digit years can be considered a
353bug.
354
355=head1 SUPPORT
356
357Support for this module is provided via the datetime@perl.org email
358list. See http://lists.perl.org/ for more details.
359
360Please submit bugs to the CPAN RT system at
361http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Time-Local or via email
362at bug-time-local@rt.cpan.org.
363
364=head1 COPYRIGHT
365
366Copyright (c) 1997-2003 Graham Barr, 2003-2007 David Rolsky.  All
367rights reserved.  This program is free software; you can redistribute
368it and/or modify it under the same terms as Perl itself.
369
370The full text of the license can be found in the LICENSE file included
371with this module.
372
373=head1 AUTHOR
374
375This module is based on a Perl 4 library, timelocal.pl, that was
376included with Perl 4.036, and was most likely written by Tom
377Christiansen.
378
379The current version was written by Graham Barr.
380
381It is now being maintained separately from the Perl core by Dave
382Rolsky, <autarch@urth.org>.
383
384=cut
385