1package Time::Local; 2 3require Exporter; 4use Carp; 5use Config; 6use strict; 7use integer; 8 9use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK ); 10$VERSION = '1.07'; 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 27my $MaxInt = ((1<<(8 * $Config{intsize} - 2))-1)*2 + 1; 28my $MaxDay = int(($MaxInt-43200)/86400)-1; 29 30# Determine the EPOC day for this machine 31my $Epoc = 0; 32if ($^O eq 'vos') { 33# work around posix-977 -- VOS doesn't handle dates in 34# the range 1970-1980. 35 $Epoc = _daygm((0, 0, 0, 1, 0, 70, 4, 0)); 36} 37elsif ($^O eq 'MacOS') { 38 no integer; 39 40 $MaxDay *=2 if $^O eq 'MacOS'; # time_t unsigned ... quick hack? 41 # MacOS time() is seconds since 1 Jan 1904, localtime 42 # so we need to calculate an offset to apply later 43 $Epoc = 693901; 44 $SecOff = timelocal(localtime(0)) - timelocal(gmtime(0)); 45 $Epoc += _daygm(gmtime(0)); 46} 47else { 48 $Epoc = _daygm(gmtime(0)); 49} 50 51%Cheat=(); # clear the cache as epoc has changed 52 53sub _daygm { 54 $_[3] + ($Cheat{pack("ss",@_[4,5])} ||= do { 55 my $month = ($_[4] + 10) % 12; 56 my $year = $_[5] + 1900 - $month/10; 57 365*$year + $year/4 - $year/100 + $year/400 + ($month*306 + 5)/10 - $Epoc 58 }); 59} 60 61 62sub _timegm { 63 my $sec = $SecOff + $_[0] + 60 * $_[1] + 3600 * $_[2]; 64 65 no integer; 66 67 $sec + 86400 * &_daygm; 68} 69 70 71sub timegm { 72 my ($sec,$min,$hour,$mday,$month,$year) = @_; 73 74 if ($year >= 1000) { 75 $year -= 1900; 76 } 77 elsif ($year < 100 and $year >= 0) { 78 $year += ($year > $Breakpoint) ? $Century : $NextCentury; 79 } 80 81 unless ($Options{no_range_check}) { 82 if (abs($year) >= 0x7fff) { 83 $year += 1900; 84 croak "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)"; 85 } 86 87 croak "Month '$month' out of range 0..11" if $month > 11 or $month < 0; 88 89 my $md = $MonthDays[$month]; 90 ++$md unless $month != 1 or $year % 4 or !($year % 400); 91 92 croak "Day '$mday' out of range 1..$md" if $mday > $md or $mday < 1; 93 croak "Hour '$hour' out of range 0..23" if $hour > 23 or $hour < 0; 94 croak "Minute '$min' out of range 0..59" if $min > 59 or $min < 0; 95 croak "Second '$sec' out of range 0..59" if $sec > 59 or $sec < 0; 96 } 97 98 my $days = _daygm(undef, undef, undef, $mday, $month, $year); 99 100 unless ($Options{no_range_check} or abs($days) < $MaxDay) { 101 $year += 1900; 102 croak "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)"; 103 } 104 105 $sec += $SecOff + 60*$min + 3600*$hour; 106 107 no integer; 108 109 $sec + 86400*$days; 110} 111 112 113sub timegm_nocheck { 114 local $Options{no_range_check} = 1; 115 &timegm; 116} 117 118 119sub timelocal { 120 no integer; 121 my $ref_t = &timegm; 122 my $loc_t = _timegm(localtime($ref_t)); 123 124 # Is there a timezone offset from GMT or are we done 125 my $zone_off = $ref_t - $loc_t 126 or return $loc_t; 127 128 # Adjust for timezone 129 $loc_t = $ref_t + $zone_off; 130 131 # Are we close to a DST change or are we done 132 my $dst_off = $ref_t - _timegm(localtime($loc_t)) 133 or return $loc_t; 134 135 # Adjust for DST change 136 $loc_t += $dst_off; 137 138 # for a negative offset from GMT, and if the original date 139 # was a non-extent gap in a forward DST jump, we should 140 # now have the wrong answer - undo the DST adjust; 141 142 return $loc_t if $zone_off <= 0; 143 144 my ($s,$m,$h) = localtime($loc_t); 145 $loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2]; 146 147 $loc_t; 148} 149 150 151sub timelocal_nocheck { 152 local $Options{no_range_check} = 1; 153 &timelocal; 154} 155 1561; 157 158__END__ 159 160=head1 NAME 161 162Time::Local - efficiently compute time from local and GMT time 163 164=head1 SYNOPSIS 165 166 $time = timelocal($sec,$min,$hour,$mday,$mon,$year); 167 $time = timegm($sec,$min,$hour,$mday,$mon,$year); 168 169=head1 DESCRIPTION 170 171These routines are the inverse of built-in perl functions localtime() 172and gmtime(). They accept a date as a six-element array, and return 173the corresponding time(2) value in seconds since the system epoch 174(Midnight, January 1, 1970 UTC on Unix, for example). This value can 175be positive or negative, though POSIX only requires support for 176positive values, so dates before the system's epoch may not work on 177all operating systems. 178 179It is worth drawing particular attention to the expected ranges for 180the values provided. The value for the day of the month is the actual day 181(ie 1..31), while the month is the number of months since January (0..11). 182This is consistent with the values returned from localtime() and gmtime(). 183 184The timelocal() and timegm() functions perform range checking on the 185input $sec, $min, $hour, $mday, and $mon values by default. If you'd 186rather they didn't, you can explicitly import the timelocal_nocheck() 187and timegm_nocheck() functions. 188 189 use Time::Local 'timelocal_nocheck'; 190 191 { 192 # The 365th day of 1999 193 print scalar localtime timelocal_nocheck 0,0,0,365,0,99; 194 195 # The twenty thousandth day since 1970 196 print scalar localtime timelocal_nocheck 0,0,0,20000,0,70; 197 198 # And even the 10,000,000th second since 1999! 199 print scalar localtime timelocal_nocheck 10000000,0,0,1,0,99; 200 } 201 202Your mileage may vary when trying these with minutes and hours, 203and it doesn't work at all for months. 204 205Strictly speaking, the year should also be specified in a form consistent 206with localtime(), i.e. the offset from 1900. 207In order to make the interpretation of the year easier for humans, 208however, who are more accustomed to seeing years as two-digit or four-digit 209values, the following conventions are followed: 210 211=over 4 212 213=item * 214 215Years greater than 999 are interpreted as being the actual year, 216rather than the offset from 1900. Thus, 1963 would indicate the year 217Martin Luther King won the Nobel prize, not the year 2863. 218 219=item * 220 221Years in the range 100..999 are interpreted as offset from 1900, 222so that 112 indicates 2012. This rule also applies to years less than zero 223(but see note below regarding date range). 224 225=item * 226 227Years in the range 0..99 are interpreted as shorthand for years in the 228rolling "current century," defined as 50 years on either side of the current 229year. Thus, today, in 1999, 0 would refer to 2000, and 45 to 2045, 230but 55 would refer to 1955. Twenty years from now, 55 would instead refer 231to 2055. This is messy, but matches the way people currently think about 232two digit dates. Whenever possible, use an absolute four digit year instead. 233 234=back 235 236The scheme above allows interpretation of a wide range of dates, particularly 237if 4-digit years are used. 238 239Please note, however, that the range of dates that can be actually be handled 240depends on the size of an integer (time_t) on a given platform. 241Currently, this is 32 bits for most systems, yielding an approximate range 242from Dec 1901 to Jan 2038. 243 244Both timelocal() and timegm() croak if given dates outside the supported 245range. 246 247=head1 IMPLEMENTATION 248 249These routines are quite efficient and yet are always guaranteed to agree 250with localtime() and gmtime(). We manage this by caching the start times 251of any months we've seen before. If we know the start time of the month, 252we can always calculate any time within the month. The start times 253are calculated using a mathematical formula. Unlike other algorithms 254that do multiple calls to gmtime(). 255 256timelocal() is implemented using the same cache. We just assume that we're 257translating a GMT time, and then fudge it when we're done for the timezone 258and daylight savings arguments. Note that the timezone is evaluated for 259each date because countries occasionally change their official timezones. 260Assuming that localtime() corrects for these changes, this routine will 261also be correct. 262 263=head1 BUGS 264 265The whole scheme for interpreting two-digit years can be considered a bug. 266 267The proclivity to croak() is probably a bug. 268 269=head1 SUPPORT 270 271Support for this module is provided via the perl5-porters@perl.org 272email list. See http://lists.perl.org/ for more details. 273 274Please submit bugs using the RT system at bugs.perl.org, the perlbug 275script, or as a last resort, to the perl5-porters@perl.org list. 276 277=head1 AUTHOR 278 279This module is based on a Perl 4 library, timelocal.pl, that was 280included with Perl 4.036, and was most likely written by Tom 281Christiansen. 282 283The current version was written by Graham Barr. 284 285It is now being maintained separately from the Perl core by Dave 286Rolsky, <autarch@urth.org>. 287 288=cut 289 290