xref: /openbsd-src/gnu/usr.bin/perl/cpan/Time-Local/lib/Time/Local.pm (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
1898184e3Ssthenpackage Time::Local;
2898184e3Ssthen
3898184e3Ssthenuse strict;
4898184e3Ssthen
55759b3d2Safresh1use Carp ();
65759b3d2Safresh1use Exporter;
7898184e3Ssthen
8*5486feefSafresh1our $VERSION = '1.35';
95759b3d2Safresh1
105759b3d2Safresh1use parent 'Exporter';
115759b3d2Safresh1
125759b3d2Safresh1our @EXPORT    = qw( timegm timelocal );
13256a93a4Safresh1our @EXPORT_OK = qw(
14256a93a4Safresh1    timegm_modern
15256a93a4Safresh1    timelocal_modern
16256a93a4Safresh1    timegm_nocheck
17256a93a4Safresh1    timelocal_nocheck
18256a93a4Safresh1    timegm_posix
19256a93a4Safresh1    timelocal_posix
20256a93a4Safresh1);
21898184e3Ssthen
22898184e3Ssthenmy @MonthDays = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
23898184e3Ssthen
24898184e3Ssthen# Determine breakpoint for rolling century
25898184e3Ssthenmy $ThisYear    = ( localtime() )[5];
26898184e3Ssthenmy $Breakpoint  = ( $ThisYear + 50 ) % 100;
27898184e3Ssthenmy $NextCentury = $ThisYear - $ThisYear % 100;
28898184e3Ssthen$NextCentury += 100 if $Breakpoint < 50;
29898184e3Ssthenmy $Century = $NextCentury - 100;
30898184e3Ssthenmy $SecOff  = 0;
31898184e3Ssthen
32898184e3Ssthenmy ( %Options, %Cheat );
33898184e3Ssthen
34898184e3Ssthenuse constant SECS_PER_MINUTE => 60;
35898184e3Ssthenuse constant SECS_PER_HOUR   => 3600;
36898184e3Ssthenuse constant SECS_PER_DAY    => 86400;
37898184e3Ssthen
38898184e3Ssthenmy $MaxDay;
39898184e3Ssthenif ( $] < 5.012000 ) {
405759b3d2Safresh1    require Config;
415759b3d2Safresh1    ## no critic (Variables::ProhibitPackageVars)
425759b3d2Safresh1
43898184e3Ssthen    my $MaxInt;
44898184e3Ssthen    if ( $^O eq 'MacOS' ) {
455759b3d2Safresh1
46898184e3Ssthen        # time_t is unsigned...
475759b3d2Safresh1        $MaxInt = ( 1 << ( 8 * $Config::Config{ivsize} ) )
485759b3d2Safresh1            - 1;    ## no critic qw(ProhibitPackageVars)
49898184e3Ssthen    }
50898184e3Ssthen    else {
515759b3d2Safresh1        $MaxInt
525759b3d2Safresh1            = ( ( 1 << ( 8 * $Config::Config{ivsize} - 2 ) ) - 1 ) * 2
535759b3d2Safresh1            + 1;    ## no critic qw(ProhibitPackageVars)
54898184e3Ssthen    }
55898184e3Ssthen
56898184e3Ssthen    $MaxDay = int( ( $MaxInt - ( SECS_PER_DAY / 2 ) ) / SECS_PER_DAY ) - 1;
57898184e3Ssthen}
58898184e3Ssthenelse {
59898184e3Ssthen    # recent localtime()'s limit is the year 2**31
60898184e3Ssthen    $MaxDay = 365 * ( 2**31 );
61*5486feefSafresh1
62*5486feefSafresh1    # On (some?) 32-bit platforms this overflows and we end up with a negative
63*5486feefSafresh1    # $MaxDay, which totally breaks this module. This is the old calculation
64*5486feefSafresh1    # we used from the days before Perl always had 64-bit time_t.
65*5486feefSafresh1    if ( $MaxDay < 0 ) {
66*5486feefSafresh1        require Config;
67*5486feefSafresh1        ## no critic (Variables::ProhibitPackageVars)
68*5486feefSafresh1        my $max_int
69*5486feefSafresh1            = ( ( 1 << ( 8 * $Config::Config{intsize} - 2 ) ) - 1 ) * 2 + 1;
70*5486feefSafresh1        $MaxDay
71*5486feefSafresh1            = int( ( $max_int - ( SECS_PER_DAY / 2 ) ) / SECS_PER_DAY ) - 1;
72*5486feefSafresh1    }
73898184e3Ssthen}
74898184e3Ssthen
75898184e3Ssthen# Determine the EPOC day for this machine
76898184e3Ssthenmy $Epoc = 0;
77898184e3Ssthenif ( $^O eq 'vos' ) {
785759b3d2Safresh1
79898184e3Ssthen    # work around posix-977 -- VOS doesn't handle dates in the range
80898184e3Ssthen    # 1970-1980.
81898184e3Ssthen    $Epoc = _daygm( 0, 0, 0, 1, 0, 70, 4, 0 );
82898184e3Ssthen}
83898184e3Ssthenelsif ( $^O eq 'MacOS' ) {
84f3efcd01Safresh1    $MaxDay *= 2;    # time_t unsigned ... quick hack?
85898184e3Ssthen                     # MacOS time() is seconds since 1 Jan 1904, localtime
86898184e3Ssthen                     # so we need to calculate an offset to apply later
87898184e3Ssthen    $Epoc   = 693901;
88898184e3Ssthen    $SecOff = timelocal( localtime(0) ) - timelocal( gmtime(0) );
89898184e3Ssthen    $Epoc += _daygm( gmtime(0) );
90898184e3Ssthen}
91898184e3Ssthenelse {
92898184e3Ssthen    $Epoc = _daygm( gmtime(0) );
93898184e3Ssthen}
94898184e3Ssthen
95898184e3Ssthen%Cheat = ();    # clear the cache as epoc has changed
96898184e3Ssthen
97898184e3Ssthensub _daygm {
98898184e3Ssthen
99898184e3Ssthen    # This is written in such a byzantine way in order to avoid
100898184e3Ssthen    # lexical variables and sub calls, for speed
101898184e3Ssthen    return $_[3] + (
102898184e3Ssthen        $Cheat{ pack( 'ss', @_[ 4, 5 ] ) } ||= do {
103898184e3Ssthen            my $month = ( $_[4] + 10 ) % 12;
104898184e3Ssthen            my $year  = $_[5] + 1900 - int( $month / 10 );
105898184e3Ssthen
106898184e3Ssthen            ( ( 365 * $year )
107898184e3Ssthen                + int( $year / 4 )
108898184e3Ssthen                    - int( $year / 100 )
109898184e3Ssthen                    + int( $year / 400 )
110*5486feefSafresh1                    + int( ( ( $month * 306 ) + 5 ) / 10 ) )
111*5486feefSafresh1                - $Epoc;
112898184e3Ssthen        }
113898184e3Ssthen    );
114898184e3Ssthen}
115898184e3Ssthen
116898184e3Ssthensub _timegm {
1175759b3d2Safresh1    my $sec
1185759b3d2Safresh1        = $SecOff + $_[0]
1195759b3d2Safresh1        + ( SECS_PER_MINUTE * $_[1] )
1205759b3d2Safresh1        + ( SECS_PER_HOUR * $_[2] );
121898184e3Ssthen
122898184e3Ssthen    return $sec + ( SECS_PER_DAY * &_daygm );
123898184e3Ssthen}
124898184e3Ssthen
125898184e3Ssthensub timegm {
126898184e3Ssthen    my ( $sec, $min, $hour, $mday, $month, $year ) = @_;
127*5486feefSafresh1    my $subsec = $sec - int($sec);
128*5486feefSafresh1    $sec = int($sec);
129898184e3Ssthen
130f3efcd01Safresh1    if ( $Options{no_year_munging} ) {
131f3efcd01Safresh1        $year -= 1900;
132f3efcd01Safresh1    }
133256a93a4Safresh1    elsif ( !$Options{posix_year} ) {
134898184e3Ssthen        if ( $year >= 1000 ) {
135898184e3Ssthen            $year -= 1900;
136898184e3Ssthen        }
137898184e3Ssthen        elsif ( $year < 100 and $year >= 0 ) {
138898184e3Ssthen            $year += ( $year > $Breakpoint ) ? $Century : $NextCentury;
139898184e3Ssthen        }
140f3efcd01Safresh1    }
141898184e3Ssthen
142898184e3Ssthen    unless ( $Options{no_range_check} ) {
1435759b3d2Safresh1        Carp::croak("Month '$month' out of range 0..11")
144898184e3Ssthen            if $month > 11
145898184e3Ssthen            or $month < 0;
146898184e3Ssthen
147898184e3Ssthen        my $md = $MonthDays[$month];
148898184e3Ssthen        ++$md
149898184e3Ssthen            if $month == 1 && _is_leap_year( $year + 1900 );
150898184e3Ssthen
1515759b3d2Safresh1        Carp::croak("Day '$mday' out of range 1..$md")
1525759b3d2Safresh1            if $mday > $md or $mday < 1;
1535759b3d2Safresh1        Carp::croak("Hour '$hour' out of range 0..23")
1545759b3d2Safresh1            if $hour > 23 or $hour < 0;
1555759b3d2Safresh1        Carp::croak("Minute '$min' out of range 0..59")
1565759b3d2Safresh1            if $min > 59 or $min < 0;
1575759b3d2Safresh1        Carp::croak("Second '$sec' out of range 0..59")
1585759b3d2Safresh1            if $sec >= 60 or $sec < 0;
159898184e3Ssthen    }
160898184e3Ssthen
161898184e3Ssthen    my $days = _daygm( undef, undef, undef, $mday, $month, $year );
162898184e3Ssthen
163*5486feefSafresh1    if ( abs($days) > $MaxDay && !$Options{no_range_check} ) {
164*5486feefSafresh1        my $msg = "Day too big - abs($days) > $MaxDay\n";
165898184e3Ssthen
166898184e3Ssthen        $year += 1900;
1675759b3d2Safresh1        $msg
1685759b3d2Safresh1            .= "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
169898184e3Ssthen
1705759b3d2Safresh1        Carp::croak($msg);
171898184e3Ssthen    }
172898184e3Ssthen
173*5486feefSafresh1    # Adding in the $subsec value last seems to prevent floating point errors
174*5486feefSafresh1    # from creeping in.
175*5486feefSafresh1    return (
176*5486feefSafresh1        (
1775759b3d2Safresh1                  $sec + $SecOff
178898184e3Ssthen                + ( SECS_PER_MINUTE * $min )
179898184e3Ssthen                + ( SECS_PER_HOUR * $hour )
180*5486feefSafresh1                + ( SECS_PER_DAY * $days )
181*5486feefSafresh1        ) + $subsec
182*5486feefSafresh1    );
183898184e3Ssthen}
184898184e3Ssthen
185898184e3Ssthensub _is_leap_year {
186898184e3Ssthen    return 0 if $_[0] % 4;
187898184e3Ssthen    return 1 if $_[0] % 100;
188898184e3Ssthen    return 0 if $_[0] % 400;
189898184e3Ssthen
190898184e3Ssthen    return 1;
191898184e3Ssthen}
192898184e3Ssthen
193898184e3Ssthensub timegm_nocheck {
194898184e3Ssthen    local $Options{no_range_check} = 1;
195898184e3Ssthen    return &timegm;
196898184e3Ssthen}
197898184e3Ssthen
198f3efcd01Safresh1sub timegm_modern {
199f3efcd01Safresh1    local $Options{no_year_munging} = 1;
200f3efcd01Safresh1    return &timegm;
201f3efcd01Safresh1}
202f3efcd01Safresh1
203256a93a4Safresh1sub timegm_posix {
204256a93a4Safresh1    local $Options{posix_year} = 1;
205256a93a4Safresh1    return &timegm;
206256a93a4Safresh1}
207256a93a4Safresh1
208898184e3Ssthensub timelocal {
209*5486feefSafresh1    my $sec    = shift;
210*5486feefSafresh1    my $subsec = $sec - int($sec);
211*5486feefSafresh1    $sec = int($sec);
212*5486feefSafresh1    unshift @_, $sec;
213*5486feefSafresh1
214898184e3Ssthen    my $ref_t         = &timegm;
215898184e3Ssthen    my $loc_for_ref_t = _timegm( localtime($ref_t) );
216898184e3Ssthen
217898184e3Ssthen    my $zone_off = $loc_for_ref_t - $ref_t
218*5486feefSafresh1        or return $loc_for_ref_t + $subsec;
219898184e3Ssthen
220898184e3Ssthen    # Adjust for timezone
221898184e3Ssthen    my $loc_t = $ref_t - $zone_off;
222898184e3Ssthen
223898184e3Ssthen    # Are we close to a DST change or are we done
224898184e3Ssthen    my $dst_off = $ref_t - _timegm( localtime($loc_t) );
225898184e3Ssthen
226898184e3Ssthen    # If this evaluates to true, it means that the value in $loc_t is
227898184e3Ssthen    # the _second_ hour after a DST change where the local time moves
228898184e3Ssthen    # backward.
2295759b3d2Safresh1    if (
2305759b3d2Safresh1        !$dst_off
2315759b3d2Safresh1        && ( ( $ref_t - SECS_PER_HOUR )
2325759b3d2Safresh1            - _timegm( localtime( $loc_t - SECS_PER_HOUR ) ) < 0 )
233898184e3Ssthen    ) {
234*5486feefSafresh1        return ( $loc_t - SECS_PER_HOUR ) + $subsec;
235898184e3Ssthen    }
236898184e3Ssthen
237898184e3Ssthen    # Adjust for DST change
238898184e3Ssthen    $loc_t += $dst_off;
239898184e3Ssthen
240*5486feefSafresh1    return $loc_t + $subsec if $dst_off > 0;
241898184e3Ssthen
242256a93a4Safresh1    # If the original date was a non-existent gap in a forward DST jump, we
243256a93a4Safresh1    # should now have the wrong answer - undo the DST adjustment
244898184e3Ssthen    my ( $s, $m, $h ) = localtime($loc_t);
245898184e3Ssthen    $loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2];
246898184e3Ssthen
247*5486feefSafresh1    return $loc_t + $subsec;
248898184e3Ssthen}
249898184e3Ssthen
250898184e3Ssthensub timelocal_nocheck {
251898184e3Ssthen    local $Options{no_range_check} = 1;
252898184e3Ssthen    return &timelocal;
253898184e3Ssthen}
254898184e3Ssthen
255f3efcd01Safresh1sub timelocal_modern {
256f3efcd01Safresh1    local $Options{no_year_munging} = 1;
257f3efcd01Safresh1    return &timelocal;
258f3efcd01Safresh1}
259f3efcd01Safresh1
260256a93a4Safresh1sub timelocal_posix {
261256a93a4Safresh1    local $Options{posix_year} = 1;
262256a93a4Safresh1    return &timelocal;
263256a93a4Safresh1}
264256a93a4Safresh1
265898184e3Ssthen1;
266898184e3Ssthen
2675759b3d2Safresh1# ABSTRACT: Efficiently compute time from local and GMT time
2685759b3d2Safresh1
269898184e3Ssthen__END__
270898184e3Ssthen
2715759b3d2Safresh1=pod
2725759b3d2Safresh1
2735759b3d2Safresh1=encoding UTF-8
2745759b3d2Safresh1
275898184e3Ssthen=head1 NAME
276898184e3Ssthen
2775759b3d2Safresh1Time::Local - Efficiently compute time from local and GMT time
2785759b3d2Safresh1
2795759b3d2Safresh1=head1 VERSION
2805759b3d2Safresh1
281*5486feefSafresh1version 1.35
282898184e3Ssthen
283898184e3Ssthen=head1 SYNOPSIS
284898184e3Ssthen
285256a93a4Safresh1    use Time::Local qw( timelocal_posix timegm_posix );
2865759b3d2Safresh1
287256a93a4Safresh1    my $time = timelocal_posix( $sec, $min, $hour, $mday, $mon, $year );
288256a93a4Safresh1    my $time = timegm_posix( $sec, $min, $hour, $mday, $mon, $year );
289898184e3Ssthen
290898184e3Ssthen=head1 DESCRIPTION
291898184e3Ssthen
2925759b3d2Safresh1This module provides functions that are the inverse of built-in perl functions
2935759b3d2Safresh1C<localtime()> and C<gmtime()>. They accept a date as a six-element array, and
2945759b3d2Safresh1return the corresponding C<time(2)> value in seconds since the system epoch
2955759b3d2Safresh1(Midnight, January 1, 1970 GMT on Unix, for example). This value can be
2965759b3d2Safresh1positive or negative, though POSIX only requires support for positive values,
2975759b3d2Safresh1so dates before the system's epoch may not work on all operating systems.
298898184e3Ssthen
2995759b3d2Safresh1It is worth drawing particular attention to the expected ranges for the values
3005759b3d2Safresh1provided. The value for the day of the month is the actual day (i.e. 1..31),
3015759b3d2Safresh1while the month is the number of months since January (0..11). This is
3025759b3d2Safresh1consistent with the values returned from C<localtime()> and C<gmtime()>.
303898184e3Ssthen
304898184e3Ssthen=head1 FUNCTIONS
305898184e3Ssthen
306256a93a4Safresh1=head2 C<timelocal_posix()> and C<timegm_posix()>
307256a93a4Safresh1
308*5486feefSafresh1I<Since version 1.30.>
309*5486feefSafresh1
310256a93a4Safresh1These functions are the exact inverse of Perl's built-in C<localtime> and
311256a93a4Safresh1C<gmtime> functions. That means that calling C<< timelocal_posix(
312256a93a4Safresh1localtime($value) ) >> will always give you the same C<$value> you started
313256a93a4Safresh1with. The same applies to C<< timegm_posix( gmtime($value) ) >>.
314256a93a4Safresh1
315256a93a4Safresh1The one exception is when the value returned from C<localtime()> represents an
316256a93a4Safresh1ambiguous local time because of a DST change. See the documentation below for
317256a93a4Safresh1more details.
318256a93a4Safresh1
319256a93a4Safresh1These functions expect the year value to be the number of years since 1900,
320256a93a4Safresh1which is what the C<localtime()> and C<gmtime()> built-ins returns.
321256a93a4Safresh1
322*5486feefSafresh1They perform range checking by default on the input C<$sec>, C<$min>, C<$hour>,
323*5486feefSafresh1C<$mday>, and C<$mon> values and will croak (using C<Carp::croak()>) if given a
324*5486feefSafresh1value outside the allowed ranges.
325256a93a4Safresh1
326256a93a4Safresh1While it would be nice to make this the default behavior, that would almost
327256a93a4Safresh1certainly break a lot of code, so you must explicitly import these functions
328256a93a4Safresh1and use them instead of the default C<timelocal()> and C<timegm()>.
329256a93a4Safresh1
330256a93a4Safresh1You are B<strongly> encouraged to use these functions in any new code which
331256a93a4Safresh1uses this module. It will almost certainly make your code's behavior less
332256a93a4Safresh1surprising.
333256a93a4Safresh1
334f3efcd01Safresh1=head2 C<timelocal_modern()> and C<timegm_modern()>
335f3efcd01Safresh1
336*5486feefSafresh1I<Since version 1.27.>
337*5486feefSafresh1
338f3efcd01Safresh1When C<Time::Local> was first written, it was a common practice to represent
339f3efcd01Safresh1years as a two-digit value like C<99> for C<1999> or C<1> for C<2001>. This
340f3efcd01Safresh1caused all sorts of problems (google "Y2K problem" if you're very young) and
341f3efcd01Safresh1developers eventually realized that this was a terrible idea.
342f3efcd01Safresh1
343f3efcd01Safresh1The default exports of C<timelocal()> and C<timegm()> do a complicated
344f3efcd01Safresh1calculation when given a year value less than 1000. This leads to surprising
345f3efcd01Safresh1results in many cases. See L</Year Value Interpretation> for details.
346f3efcd01Safresh1
347*5486feefSafresh1The C<time*_modern()> functions do not do this year munging and simply take the
348*5486feefSafresh1year value as provided.
349f3efcd01Safresh1
350*5486feefSafresh1They perform range checking by default on the input C<$sec>, C<$min>, C<$hour>,
351*5486feefSafresh1C<$mday>, and C<$mon> values and will croak (using C<Carp::croak()>) if given a
352*5486feefSafresh1value outside the allowed ranges.
353f3efcd01Safresh1
354898184e3Ssthen=head2 C<timelocal()> and C<timegm()>
355898184e3Ssthen
3565759b3d2Safresh1This module exports two functions by default, C<timelocal()> and C<timegm()>.
357898184e3Ssthen
358*5486feefSafresh1They perform range checking by default on the input C<$sec>, C<$min>, C<$hour>,
359*5486feefSafresh1C<$mday>, and C<$mon> values and will croak (using C<Carp::croak()>) if given a
360*5486feefSafresh1value outside the allowed ranges.
361256a93a4Safresh1
362*5486feefSafresh1B<Warning: The year value interpretation that these functions and their nocheck
363*5486feefSafresh1variants use will almost certainly lead to bugs in your code, if not now, then
364*5486feefSafresh1in the future. You are strongly discouraged from using these in new code, and
365*5486feefSafresh1you should convert old code to using either the C<*_posix> or C<*_modern>
366*5486feefSafresh1functions if possible.>
367898184e3Ssthen
368898184e3Ssthen=head2 C<timelocal_nocheck()> and C<timegm_nocheck()>
369898184e3Ssthen
370256a93a4Safresh1If you are working with data you know to be valid, you can use the "nocheck"
371256a93a4Safresh1variants, C<timelocal_nocheck()> and C<timegm_nocheck()>. These variants must
372256a93a4Safresh1be explicitly imported.
373898184e3Ssthen
374*5486feefSafresh1If you supply data which is not valid (month 27, second 1,000) the results will
375*5486feefSafresh1be unpredictable (so don't do that).
376898184e3Ssthen
377256a93a4Safresh1Note that my benchmarks show that this is just a 3% speed increase over the
378256a93a4Safresh1checked versions, so unless calling C<Time::Local> is the hottest spot in your
379256a93a4Safresh1application, using these nocheck variants is unlikely to have much impact on
380256a93a4Safresh1your application.
381256a93a4Safresh1
382898184e3Ssthen=head2 Year Value Interpretation
383898184e3Ssthen
384256a93a4Safresh1B<This does not apply to the C<*_posix> or C<*_modern> functions. Use those
385f3efcd01Safresh1exports if you want to ensure consistent behavior as your code ages.>
386f3efcd01Safresh1
3875759b3d2Safresh1Strictly speaking, the year should be specified in a form consistent with
3885759b3d2Safresh1C<localtime()>, i.e. the offset from 1900. In order to make the interpretation
389*5486feefSafresh1of the year easier for humans, however, who are more accustomed to seeing years
390*5486feefSafresh1as two-digit or four-digit values, the following conventions are followed:
391898184e3Ssthen
392898184e3Ssthen=over 4
393898184e3Ssthen
394898184e3Ssthen=item *
395898184e3Ssthen
3965759b3d2Safresh1Years greater than 999 are interpreted as being the actual year, rather than
397*5486feefSafresh1the offset from 1900. Thus, 1964 would indicate the year Martin Luther King won
398*5486feefSafresh1the Nobel prize, not the year 3864.
399898184e3Ssthen
400898184e3Ssthen=item *
401898184e3Ssthen
4025759b3d2Safresh1Years in the range 100..999 are interpreted as offset from 1900, so that 112
4035759b3d2Safresh1indicates 2012. This rule also applies to years less than zero (but see note
4045759b3d2Safresh1below regarding date range).
405898184e3Ssthen
406898184e3Ssthen=item *
407898184e3Ssthen
4085759b3d2Safresh1Years in the range 0..99 are interpreted as shorthand for years in the rolling
409*5486feefSafresh1"current century," defined as 50 years on either side of the current year.
410*5486feefSafresh1Thus, today, in 1999, 0 would refer to 2000, and 45 to 2045, but 55 would refer
411*5486feefSafresh1to 1955. Twenty years from now, 55 would instead refer to 2055. This is messy,
412*5486feefSafresh1but matches the way people currently think about two digit dates. Whenever
413*5486feefSafresh1possible, use an absolute four digit year instead.
414898184e3Ssthen
415898184e3Ssthen=back
416898184e3Ssthen
4175759b3d2Safresh1The scheme above allows interpretation of a wide range of dates, particularly
418256a93a4Safresh1if 4-digit years are used. But it also means that the behavior of your code
419256a93a4Safresh1changes as time passes, because the rolling "current century" changes each
420256a93a4Safresh1year.
421898184e3Ssthen
422898184e3Ssthen=head2 Limits of time_t
423898184e3Ssthen
4245759b3d2Safresh1On perl versions older than 5.12.0, the range of dates that can be actually be
4255759b3d2Safresh1handled depends on the size of C<time_t> (usually a signed integer) on the
4265759b3d2Safresh1given platform. Currently, this is 32 bits for most systems, yielding an
4275759b3d2Safresh1approximate range from Dec 1901 to Jan 2038.
428898184e3Ssthen
4295759b3d2Safresh1Both C<timelocal()> and C<timegm()> croak if given dates outside the supported
4305759b3d2Safresh1range.
431898184e3Ssthen
432f3efcd01Safresh1As of version 5.12.0, perl has stopped using the time implementation of the
433f3efcd01Safresh1operating system it's running on. Instead, it has its own implementation of
434f3efcd01Safresh1those routines with a safe range of at least +/- 2**52 (about 142 million
435f3efcd01Safresh1years)
436898184e3Ssthen
437898184e3Ssthen=head2 Ambiguous Local Times (DST)
438898184e3Ssthen
4395759b3d2Safresh1Because of DST changes, there are many time zones where the same local time
4405759b3d2Safresh1occurs for two different GMT times on the same day. For example, in the
4415759b3d2Safresh1"Europe/Paris" time zone, the local time of 2001-10-28 02:30:00 can represent
4425759b3d2Safresh1either 2001-10-28 00:30:00 GMT, B<or> 2001-10-28 01:30:00 GMT.
443898184e3Ssthen
444*5486feefSafresh1When given an ambiguous local time, the timelocal() function will always return
445*5486feefSafresh1the epoch for the I<earlier> of the two possible GMT times.
446898184e3Ssthen
447898184e3Ssthen=head2 Non-Existent Local Times (DST)
448898184e3Ssthen
449*5486feefSafresh1When a DST change causes a locale clock to skip one hour forward, there will be
450*5486feefSafresh1an hour's worth of local times that don't exist. Again, for the "Europe/Paris"
451*5486feefSafresh1time zone, the local clock jumped from 2001-03-25 01:59:59 to 2001-03-25
452*5486feefSafresh103:00:00.
453898184e3Ssthen
4545759b3d2Safresh1If the C<timelocal()> function is given a non-existent local time, it will
4555759b3d2Safresh1simply return an epoch value for the time one hour later.
456898184e3Ssthen
457898184e3Ssthen=head2 Negative Epoch Values
458898184e3Ssthen
4595759b3d2Safresh1On perl version 5.12.0 and newer, negative epoch values are fully supported.
460898184e3Ssthen
4615759b3d2Safresh1On older versions of perl, negative epoch (C<time_t>) values, which are not
4625759b3d2Safresh1officially supported by the POSIX standards, are known not to work on some
4635759b3d2Safresh1systems. These include MacOS (pre-OSX) and Win32.
464898184e3Ssthen
4655759b3d2Safresh1On systems which do support negative epoch values, this module should be able
4665759b3d2Safresh1to cope with dates before the start of the epoch, down the minimum value of
4675759b3d2Safresh1time_t for the system.
468898184e3Ssthen
469898184e3Ssthen=head1 IMPLEMENTATION
470898184e3Ssthen
4715759b3d2Safresh1These routines are quite efficient and yet are always guaranteed to agree with
4725759b3d2Safresh1C<localtime()> and C<gmtime()>. We manage this by caching the start times of
4735759b3d2Safresh1any months we've seen before. If we know the start time of the month, we can
4745759b3d2Safresh1always calculate any time within the month.  The start times are calculated
475*5486feefSafresh1using a mathematical formula. Unlike other algorithms that do multiple calls to
476*5486feefSafresh1C<gmtime()>.
477898184e3Ssthen
478*5486feefSafresh1The C<timelocal()> function is implemented using the same cache. We just assume
479*5486feefSafresh1that we're translating a GMT time, and then fudge it when we're done for the
480*5486feefSafresh1timezone and daylight savings arguments. Note that the timezone is evaluated
481*5486feefSafresh1for each date because countries occasionally change their official timezones.
482*5486feefSafresh1Assuming that C<localtime()> corrects for these changes, this routine will also
483*5486feefSafresh1be correct.
484898184e3Ssthen
4855759b3d2Safresh1=head1 AUTHORS EMERITUS
486898184e3Ssthen
487*5486feefSafresh1This module is based on a Perl 4 library, timelocal.pl, that was included with
488*5486feefSafresh1Perl 4.036, and was most likely written by Tom Christiansen.
489898184e3Ssthen
490898184e3SsthenThe current version was written by Graham Barr.
491898184e3Ssthen
4925759b3d2Safresh1=head1 BUGS
4935759b3d2Safresh1
4945759b3d2Safresh1The whole scheme for interpreting two-digit years can be considered a bug.
4955759b3d2Safresh1
496f3efcd01Safresh1Bugs may be submitted at L<https://github.com/houseabsolute/Time-Local/issues>.
4975759b3d2Safresh1
4985759b3d2Safresh1There is a mailing list available for users of this distribution,
4995759b3d2Safresh1L<mailto:datetime@perl.org>.
5005759b3d2Safresh1
501f3efcd01Safresh1=head1 SOURCE
502f3efcd01Safresh1
503f3efcd01Safresh1The source code repository for Time-Local can be found at L<https://github.com/houseabsolute/Time-Local>.
504f3efcd01Safresh1
5055759b3d2Safresh1=head1 AUTHOR
5065759b3d2Safresh1
5075759b3d2Safresh1Dave Rolsky <autarch@urth.org>
5085759b3d2Safresh1
5095759b3d2Safresh1=head1 CONTRIBUTORS
5105759b3d2Safresh1
511*5486feefSafresh1=for stopwords Florian Ragwitz Gregory Oschwald J. Nick Koston Tom Wyant Unknown
5125759b3d2Safresh1
5135759b3d2Safresh1=over 4
5145759b3d2Safresh1
5155759b3d2Safresh1=item *
5165759b3d2Safresh1
5175759b3d2Safresh1Florian Ragwitz <rafl@debian.org>
5185759b3d2Safresh1
5195759b3d2Safresh1=item *
5205759b3d2Safresh1
521*5486feefSafresh1Gregory Oschwald <oschwald@gmail.com>
522*5486feefSafresh1
523*5486feefSafresh1=item *
524*5486feefSafresh1
5255759b3d2Safresh1J. Nick Koston <nick@cpanel.net>
5265759b3d2Safresh1
5275759b3d2Safresh1=item *
5285759b3d2Safresh1
529*5486feefSafresh1Tom Wyant <wyant@cpan.org>
530*5486feefSafresh1
531*5486feefSafresh1=item *
532*5486feefSafresh1
5335759b3d2Safresh1Unknown <unknown@example.com>
5345759b3d2Safresh1
5355759b3d2Safresh1=back
5365759b3d2Safresh1
5375759b3d2Safresh1=head1 COPYRIGHT AND LICENSE
5385759b3d2Safresh1
539*5486feefSafresh1This software is copyright (c) 1997 - 2023 by Graham Barr & Dave Rolsky.
5405759b3d2Safresh1
5415759b3d2Safresh1This is free software; you can redistribute it and/or modify it under
5425759b3d2Safresh1the same terms as the Perl 5 programming language system itself.
543898184e3Ssthen
544f3efcd01Safresh1The full text of the license can be found in the
545f3efcd01Safresh1F<LICENSE> file included with this distribution.
546f3efcd01Safresh1
547898184e3Ssthen=cut
548