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