1898184e3Ssthen#!./perl 2898184e3Ssthen 3898184e3Ssthenuse strict; 491f110e0Safresh1use warnings; 5898184e3Ssthen 6898184e3Ssthenuse Config; 7f3efcd01Safresh1use Test::More 0.96; 8256a93a4Safresh1use Time::Local qw( 9256a93a4Safresh1 timegm 10256a93a4Safresh1 timelocal 11256a93a4Safresh1 timegm_modern 12256a93a4Safresh1 timelocal_modern 13256a93a4Safresh1 timegm_nocheck 14256a93a4Safresh1 timelocal_nocheck 15256a93a4Safresh1 timegm_posix 16256a93a4Safresh1 timelocal_posix 17256a93a4Safresh1); 18256a93a4Safresh1 19256a93a4Safresh1my @local_subs = qw( 20256a93a4Safresh1 timelocal 21256a93a4Safresh1 timelocal_modern 22256a93a4Safresh1 timelocal_posix 23256a93a4Safresh1 timelocal_nocheck 24256a93a4Safresh1); 25256a93a4Safresh1 26256a93a4Safresh1my @gm_subs = qw( 27256a93a4Safresh1 timegm 28256a93a4Safresh1 timegm_modern 29256a93a4Safresh1 timegm_posix 30256a93a4Safresh1 timegm_nocheck 31256a93a4Safresh1); 32898184e3Ssthen 33f3efcd01Safresh1# Use 3 days before the start of the epoch because with Borland on 34f3efcd01Safresh1# Win32 it will work for -3600 _if_ your time zone is +01:00 (or 35f3efcd01Safresh1# greater). 36f3efcd01Safresh1my $neg_epoch_ok 37f3efcd01Safresh1 = $^O eq 'VMS' ? 0 : defined( ( localtime(-259200) )[0] ) ? 1 : 0; 385759b3d2Safresh1 39*5486feefSafresh1# On some old 32-bit Perls the call to gmtime here may return an undef. 40*5486feefSafresh1my $large_epoch_ok = eval { ( ( gmtime 2**40 )[5] || 0 ) == 34912 }; 41f3efcd01Safresh1 42256a93a4Safresh1subtest( 'valid times', \&_test_valid_times ); 43256a93a4Safresh1subtest( 'diff between two calls', \&_test_diff_between_two_calls ); 44f3efcd01Safresh1subtest( 45256a93a4Safresh1 'DST transition bug - https://rt.perl.org/Ticket/Display.html?id=19393', 46256a93a4Safresh1 \&_test_dst_transition_bug, 47f3efcd01Safresh1); 48256a93a4Safresh1subtest( 'Time::Local::_is_leap_year', \&_test_is_leap_year ); 49256a93a4Safresh1subtest( 'negative epochs', \&_test_negative_epochs ); 50256a93a4Safresh1subtest( 'large epoch values', \&_test_large_epoch_values ); 51256a93a4Safresh1subtest( '2-digit years', \&_test_2_digit_years ); 52256a93a4Safresh1subtest( 'invalid values', \&_test_invalid_values ); 53*5486feefSafresh1subtest( 'non-integer seconds', \&_test_non_integer_seconds ); 54f3efcd01Safresh1 55256a93a4Safresh1sub _test_valid_times { 56f3efcd01Safresh1 my %tests = ( 57f3efcd01Safresh1 'simple times' => [ 585759b3d2Safresh1 [ 1970, 1, 2, 0, 0, 0 ], 595759b3d2Safresh1 [ 1980, 2, 28, 12, 0, 0 ], 605759b3d2Safresh1 [ 1980, 2, 29, 12, 0, 0 ], 61898184e3Ssthen [ 1999, 12, 31, 23, 59, 59 ], 625759b3d2Safresh1 [ 2000, 1, 1, 0, 0, 0 ], 63898184e3Ssthen [ 2010, 10, 12, 14, 13, 12 ], 64f3efcd01Safresh1 ], 65f3efcd01Safresh1 'leap days' => [ 66898184e3Ssthen [ 2020, 2, 29, 12, 59, 59 ], 675759b3d2Safresh1 [ 2030, 7, 4, 17, 7, 6 ], 68f3efcd01Safresh1 ], 69f3efcd01Safresh1 'non-integer seconds' => [ 70f3efcd01Safresh1 [ 2010, 10, 12, 14, 13, 12.1 ], 71f3efcd01Safresh1 [ 2010, 10, 12, 14, 13, 59.1 ], 72f3efcd01Safresh1 ], 73f3efcd01Safresh1 ); 74898184e3Ssthen 75898184e3Ssthen # The following test fails on a surprising number of systems 76898184e3Ssthen # so it is commented out. The end of the Epoch for a 32-bit signed 77898184e3Ssthen # implementation of time_t should be Jan 19, 2038 03:14:07 UTC. 78898184e3Ssthen # [2038, 1, 17, 23, 59, 59], # last full day in any tz 7991f110e0Safresh1 80898184e3Ssthen # more than 2**31 time_t - requires a 64bit safe localtime/gmtime 81f3efcd01Safresh1 $tests{'greater than 2**31 seconds'} = [ [ 2258, 8, 11, 1, 49, 17 ] ] 82898184e3Ssthen if $] >= 5.012000; 83898184e3Ssthen 84f3efcd01Safresh1 # use vmsish 'time' makes for oddness around the Unix epoch 85f3efcd01Safresh1 $tests{'simple times'}[0][2]++ 86f3efcd01Safresh1 if $^O eq 'VMS'; 875759b3d2Safresh1 88f3efcd01Safresh1 $tests{'negative epoch'} = [ 89898184e3Ssthen [ 1969, 12, 31, 16, 59, 59 ], 905759b3d2Safresh1 [ 1950, 4, 12, 9, 30, 31 ], 91f3efcd01Safresh1 ] if $neg_epoch_ok; 92898184e3Ssthen 93256a93a4Safresh1 for my $group ( sort keys %tests ) { 94256a93a4Safresh1 subtest( 95256a93a4Safresh1 $group, 96256a93a4Safresh1 sub { _test_group( $tests{$group} ) }, 97256a93a4Safresh1 ); 98256a93a4Safresh1 } 99898184e3Ssthen} 100898184e3Ssthen 101f3efcd01Safresh1sub _test_group { 102f3efcd01Safresh1 my $group = shift; 103898184e3Ssthen 104f3efcd01Safresh1 for my $vals ( @{$group} ) { 105f3efcd01Safresh1 my ( $year, $mon, $mday, $hour, $min, $sec ) = @{$vals}; 106898184e3Ssthen $mon--; 107898184e3Ssthen 108f3efcd01Safresh1 # 1970 test on VOS fails 109f3efcd01Safresh1 next if $^O eq 'vos' && $year == 1970; 110898184e3Ssthen 111256a93a4Safresh1 for my $sub (@local_subs) { 112256a93a4Safresh1 my $y = $year; 113256a93a4Safresh1 $y -= 1900 if $sub =~ /posix/; 114256a93a4Safresh1 my $time = __PACKAGE__->can($sub) 115256a93a4Safresh1 ->( $sec, $min, $hour, $mday, $mon, $y ); 116256a93a4Safresh1 117256a93a4Safresh1 my @lt = localtime($time); 118256a93a4Safresh1 is_deeply( 119256a93a4Safresh1 { 120256a93a4Safresh1 second => $lt[0], 121256a93a4Safresh1 minute => $lt[1], 122256a93a4Safresh1 hour => $lt[2], 123256a93a4Safresh1 day => $lt[3], 124256a93a4Safresh1 month => $lt[4], 125256a93a4Safresh1 year => $lt[5], 126256a93a4Safresh1 }, 127256a93a4Safresh1 { 128256a93a4Safresh1 second => int($sec), 129256a93a4Safresh1 minute => $min, 130256a93a4Safresh1 hour => $hour, 131256a93a4Safresh1 day => $mday, 132256a93a4Safresh1 month => $mon, 133256a93a4Safresh1 year => $year - 1900, 134256a93a4Safresh1 }, 135256a93a4Safresh1 "$sub( $sec, $min, $hour, $mday, $mon, $y )" 136256a93a4Safresh1 ); 137256a93a4Safresh1 } 138256a93a4Safresh1 139256a93a4Safresh1 for my $sub (@gm_subs) { 140256a93a4Safresh1 my $y = $year; 141256a93a4Safresh1 $y -= 1900 if $sub =~ /posix/; 142256a93a4Safresh1 my $time = __PACKAGE__->can($sub) 143256a93a4Safresh1 ->( $sec, $min, $hour, $mday, $mon, $y ); 144256a93a4Safresh1 145256a93a4Safresh1 my @gt = gmtime($time); 146256a93a4Safresh1 is_deeply( 147256a93a4Safresh1 { 148256a93a4Safresh1 second => $gt[0], 149256a93a4Safresh1 minute => $gt[1], 150256a93a4Safresh1 hour => $gt[2], 151256a93a4Safresh1 day => $gt[3], 152256a93a4Safresh1 month => $gt[4], 153256a93a4Safresh1 year => $gt[5], 154256a93a4Safresh1 }, 155256a93a4Safresh1 { 156256a93a4Safresh1 second => int($sec), 157256a93a4Safresh1 minute => $min, 158256a93a4Safresh1 hour => $hour, 159256a93a4Safresh1 day => $mday, 160256a93a4Safresh1 month => $mon, 161256a93a4Safresh1 year => $year - 1900, 162256a93a4Safresh1 }, 163256a93a4Safresh1 "$sub( $sec, $min, $hour, $mday, $mon, $y )" 164256a93a4Safresh1 ); 165256a93a4Safresh1 } 166256a93a4Safresh1 } 167256a93a4Safresh1} 168256a93a4Safresh1 169256a93a4Safresh1sub _test_diff_between_two_calls { 170256a93a4Safresh1 for my $sub (@local_subs) { 171f3efcd01Safresh1 subtest( 172f3efcd01Safresh1 $sub, 173f3efcd01Safresh1 sub { 174256a93a4Safresh1 my $year = 1990; 175256a93a4Safresh1 $year -= 1900 if $sub =~ /posix/; 176256a93a4Safresh1 my $sub_ref = __PACKAGE__->can($sub); 177*5486feefSafresh1 178*5486feefSafresh1 for my $sec ( 0, 0.1 ) { 1795759b3d2Safresh1 is( 180*5486feefSafresh1 $sub_ref->( $sec, 0, 1, 1, 0, $year ) 181*5486feefSafresh1 - $sub_ref->( $sec, 0, 0, 1, 0, $year ), 182f3efcd01Safresh1 3600, 183256a93a4Safresh1 'one hour difference between two calls' 1845759b3d2Safresh1 ); 185898184e3Ssthen 1865759b3d2Safresh1 is( 187*5486feefSafresh1 $sub_ref->( $sec, 2, 3, 1, 0, $year + 1 ) 188*5486feefSafresh1 - $sub_ref->( $sec, 2, 3, 31, 11, $year ), 1895759b3d2Safresh1 24 * 3600, 190256a93a4Safresh1 'one day difference between two calls across year boundary', 1915759b3d2Safresh1 ); 192*5486feefSafresh1 } 193256a93a4Safresh1 }, 194256a93a4Safresh1 ); 195256a93a4Safresh1 } 196256a93a4Safresh1 197256a93a4Safresh1 for my $sub (@gm_subs) { 198256a93a4Safresh1 subtest( 199256a93a4Safresh1 $sub, 200256a93a4Safresh1 sub { 201256a93a4Safresh1 my $year = 1980; 202256a93a4Safresh1 $year -= 1900 if $sub =~ /posix/; 203256a93a4Safresh1 my $sub_ref = __PACKAGE__->can($sub); 204898184e3Ssthen 205*5486feefSafresh1 for my $sec ( 0, 0.1 ) { 206*5486feefSafresh1 207898184e3Ssthen # Diff beween Jan 1, 1980 and Mar 1, 1980 = (31 + 29 = 60 days) 2085759b3d2Safresh1 is( 209*5486feefSafresh1 $sub_ref->( $sec, 0, 0, 1, 2, $year ) 210*5486feefSafresh1 - $sub_ref->( $sec, 0, 0, 1, 0, $year ), 2115759b3d2Safresh1 60 * 24 * 3600, 212256a93a4Safresh1 '60 day difference between two calls', 2135759b3d2Safresh1 ); 214*5486feefSafresh1 } 215f3efcd01Safresh1 }, 216f3efcd01Safresh1 ); 217256a93a4Safresh1 } 218256a93a4Safresh1} 219898184e3Ssthen 220256a93a4Safresh1sub _test_dst_transition_bug { 221256a93a4Safresh1 for my $sub (@local_subs) { 222f3efcd01Safresh1 subtest( 223256a93a4Safresh1 $sub, 224f3efcd01Safresh1 sub { 225256a93a4Safresh1 my $year = 2002; 226256a93a4Safresh1 $year -= 2002 if $sub =~ /posix/; 227256a93a4Safresh1 my $sub_ref = __PACKAGE__->can($sub); 228256a93a4Safresh1 229256a93a4Safresh1 # At a DST transition, the clock skips forward, eg from 230256a93a4Safresh1 # 01:59:59 to 03:00:00. In this case, 02:00:00 is an 231256a93a4Safresh1 # invalid time, and should be treated like 03:00:00 rather 232256a93a4Safresh1 # than 01:00:00 - negative zone offsets used to do the 233256a93a4Safresh1 # latter. 234256a93a4Safresh1 my $hour 235256a93a4Safresh1 = ( localtime( $sub_ref->( 0, 0, 2, 7, 3, 102 ) ) )[2]; 2365759b3d2Safresh1 237898184e3Ssthen # testers in US/Pacific should get 3, 238898184e3Ssthen # other testers should get 2 239*5486feefSafresh1 ok( $hour == 2 || $hour == 3, 'hour should be 2 or 3' ) 240*5486feefSafresh1 or diag "hour = $hour"; 241f3efcd01Safresh1 }, 242f3efcd01Safresh1 ); 243256a93a4Safresh1 } 244256a93a4Safresh1} 245f3efcd01Safresh1 246256a93a4Safresh1sub _test_is_leap_year { 247f3efcd01Safresh1 my @years = ( 248f3efcd01Safresh1 [ 1900 => 0 ], 249f3efcd01Safresh1 [ 1947 => 0 ], 250f3efcd01Safresh1 [ 1996 => 1 ], 251f3efcd01Safresh1 [ 2000 => 1 ], 252f3efcd01Safresh1 [ 2100 => 0 ], 253f3efcd01Safresh1 ); 254898184e3Ssthen 255898184e3Ssthen for my $p (@years) { 256898184e3Ssthen my ( $year, $is_leap_year ) = @$p; 257898184e3Ssthen 258898184e3Ssthen my $string = $is_leap_year ? 'is' : 'is not'; 2595759b3d2Safresh1 ## no critic (Subroutines::ProtectPrivateSubs) 2605759b3d2Safresh1 is( 2615759b3d2Safresh1 Time::Local::_is_leap_year($year), $is_leap_year, 2625759b3d2Safresh1 "$year $string a leap year" 2635759b3d2Safresh1 ); 264898184e3Ssthen } 265f3efcd01Safresh1} 266898184e3Ssthen 267256a93a4Safresh1sub _test_negative_epochs { 268f3efcd01Safresh1 plan skip_all => 'this platform does not support negative epochs.' 269898184e3Ssthen unless $neg_epoch_ok; 270898184e3Ssthen 271256a93a4Safresh1 for my $sub (@gm_subs) { 272256a93a4Safresh1 subtest( 273256a93a4Safresh1 $sub, 274256a93a4Safresh1 sub { 275256a93a4Safresh1 my $year_mod = $sub =~ /posix/ ? -1900 : 0; 276256a93a4Safresh1 my $sub_ref = __PACKAGE__->can($sub); 277256a93a4Safresh1 278256a93a4Safresh1 unless ( $sub =~ /nocheck/ ) { 279f3efcd01Safresh1 local $@ = undef; 280256a93a4Safresh1 eval { $sub_ref->( 0, 0, 0, 29, 1, 1900 + $year_mod ); }; 2815759b3d2Safresh1 like( 2825759b3d2Safresh1 $@, qr/Day '29' out of range 1\.\.28/, 2835759b3d2Safresh1 'does not accept leap day in 1900' 2845759b3d2Safresh1 ); 285898184e3Ssthen 286f3efcd01Safresh1 local $@ = undef; 287256a93a4Safresh1 eval { $sub_ref->( 0, 0, 0, 29, 1, 200 + $year_mod ) }; 2885759b3d2Safresh1 like( 2895759b3d2Safresh1 $@, qr/Day '29' out of range 1\.\.28/, 2905759b3d2Safresh1 'does not accept leap day in 2100 (year passed as 200)' 2915759b3d2Safresh1 ); 292256a93a4Safresh1 } 293898184e3Ssthen 294f3efcd01Safresh1 local $@ = undef; 295256a93a4Safresh1 eval { $sub_ref->( 0, 0, 0, 29, 1, 0 + $year_mod ) }; 296f3efcd01Safresh1 is( 297f3efcd01Safresh1 $@, q{}, 298f3efcd01Safresh1 'no error with leap day of 2000 (year passed as 0)' 299f3efcd01Safresh1 ); 300898184e3Ssthen 301f3efcd01Safresh1 local $@ = undef; 302256a93a4Safresh1 eval { $sub_ref->( 0, 0, 0, 29, 1, 1904 + $year_mod ) }; 3035759b3d2Safresh1 is( $@, q{}, 'no error with leap day of 1904' ); 304898184e3Ssthen 305f3efcd01Safresh1 local $@ = undef; 306256a93a4Safresh1 eval { $sub_ref->( 0, 0, 0, 29, 1, 4 + $year_mod ) }; 307f3efcd01Safresh1 is( 308f3efcd01Safresh1 $@, q{}, 309f3efcd01Safresh1 'no error with leap day of 2004 (year passed as 4)' 310f3efcd01Safresh1 ); 311898184e3Ssthen 312f3efcd01Safresh1 local $@ = undef; 313256a93a4Safresh1 eval { $sub_ref->( 0, 0, 0, 29, 1, 96 + $year_mod ) }; 314f3efcd01Safresh1 is( 315f3efcd01Safresh1 $@, q{}, 316f3efcd01Safresh1 'no error with leap day of 1996 (year passed as 96)' 317f3efcd01Safresh1 ); 318f3efcd01Safresh1 }, 319f3efcd01Safresh1 ); 320256a93a4Safresh1 } 321256a93a4Safresh1} 322898184e3Ssthen 323256a93a4Safresh1sub _test_large_epoch_values { 324f3efcd01Safresh1 plan skip_all => 'These tests require support for large epoch values' 325f3efcd01Safresh1 unless $large_epoch_ok; 326898184e3Ssthen 327256a93a4Safresh1 for my $sub (@gm_subs) { 328256a93a4Safresh1 subtest( 329256a93a4Safresh1 $sub, 330256a93a4Safresh1 sub { 331256a93a4Safresh1 my $year_mod = $sub =~ /posix/ ? -1900 : 0; 332256a93a4Safresh1 my $sub_ref = __PACKAGE__->can($sub); 333256a93a4Safresh1 3345759b3d2Safresh1 is( 335256a93a4Safresh1 $sub_ref->( 8, 14, 3, 19, 0, 2038 + $year_mod ), 336256a93a4Safresh1 2**31, 337256a93a4Safresh1 'can call with 2**31 epoch seconds', 3385759b3d2Safresh1 ); 3395759b3d2Safresh1 is( 340256a93a4Safresh1 $sub_ref->( 16, 28, 6, 7, 1, 2106 + $year_mod ), 341256a93a4Safresh1 2**32, 342256a93a4Safresh1 'can call with 2**32 epoch seconds (on a 64-bit system)', 3435759b3d2Safresh1 ); 3445759b3d2Safresh1 is( 345256a93a4Safresh1 $sub_ref->( 16, 36, 0, 20, 1, 36812 + $year_mod ), 346256a93a4Safresh1 2**40, 347256a93a4Safresh1 'can call with 2**40 epoch seconds (on a 64-bit system)', 3485759b3d2Safresh1 ); 349f3efcd01Safresh1 }, 3505759b3d2Safresh1 ); 351256a93a4Safresh1 } 352256a93a4Safresh1} 353898184e3Ssthen 354256a93a4Safresh1sub _test_2_digit_years { 355f3efcd01Safresh1 my $current_year = ( localtime() )[5]; 356f3efcd01Safresh1 my $pre_break = ( $current_year + 49 ) - 100; 357f3efcd01Safresh1 my $break = ( $current_year + 50 ) - 100; 358f3efcd01Safresh1 my $post_break = ( $current_year + 51 ) - 100; 359898184e3Ssthen 360f3efcd01Safresh1 subtest( 361f3efcd01Safresh1 'legacy year munging', 362f3efcd01Safresh1 sub { 363f3efcd01Safresh1 plan skip_all => 'Requires support for an large epoch values' 364f3efcd01Safresh1 unless $large_epoch_ok; 365f3efcd01Safresh1 3665759b3d2Safresh1 is( 367f3efcd01Safresh1 ( 368256a93a4Safresh1 ( localtime( timelocal( 0, 0, 0, 1, 1, $pre_break ) ) )[5] 369f3efcd01Safresh1 ), 370f3efcd01Safresh1 $pre_break + 100, 371f3efcd01Safresh1 "year $pre_break is treated as next century", 3725759b3d2Safresh1 ); 3735759b3d2Safresh1 is( 374256a93a4Safresh1 ( ( localtime( timelocal( 0, 0, 0, 1, 1, $break ) ) )[5] ), 375f3efcd01Safresh1 $break + 100, 376f3efcd01Safresh1 "year $break is treated as next century", 3775759b3d2Safresh1 ); 3785759b3d2Safresh1 is( 379f3efcd01Safresh1 ( 380256a93a4Safresh1 ( localtime( timelocal( 0, 0, 0, 1, 1, $post_break ) ) ) 381256a93a4Safresh1 [5] 382f3efcd01Safresh1 ), 383f3efcd01Safresh1 $post_break, 384f3efcd01Safresh1 "year $post_break is treated as current century", 3855759b3d2Safresh1 ); 386898184e3Ssthen } 387f3efcd01Safresh1 ); 388f3efcd01Safresh1 389f3efcd01Safresh1 subtest( 390f3efcd01Safresh1 'modern', 391f3efcd01Safresh1 sub { 392f3efcd01Safresh1 plan skip_all => 393f3efcd01Safresh1 'Requires negative epoch support and large epoch support' 394f3efcd01Safresh1 unless $neg_epoch_ok && $large_epoch_ok; 395f3efcd01Safresh1 396f3efcd01Safresh1 is( 397f3efcd01Safresh1 ( 398f3efcd01Safresh1 ( 399f3efcd01Safresh1 localtime( 400f3efcd01Safresh1 timelocal_modern( 0, 0, 0, 1, 1, $pre_break ) 401f3efcd01Safresh1 ) 402f3efcd01Safresh1 )[5] 403*5486feefSafresh1 ) 404*5486feefSafresh1 + 1900, 405f3efcd01Safresh1 $pre_break, 406f3efcd01Safresh1 "year $pre_break is treated as year $pre_break", 407f3efcd01Safresh1 ); 408f3efcd01Safresh1 is( 409f3efcd01Safresh1 ( 410f3efcd01Safresh1 ( 411f3efcd01Safresh1 localtime( 412f3efcd01Safresh1 timelocal_modern( 0, 0, 0, 1, 1, $break ) 413f3efcd01Safresh1 ) 414f3efcd01Safresh1 )[5] 415*5486feefSafresh1 ) 416*5486feefSafresh1 + 1900, 417f3efcd01Safresh1 $break, 418f3efcd01Safresh1 "year $break is treated as year $break", 419f3efcd01Safresh1 ); 420f3efcd01Safresh1 is( 421f3efcd01Safresh1 ( 422f3efcd01Safresh1 ( 423f3efcd01Safresh1 localtime( 424256a93a4Safresh1 timelocal_modern( 0, 0, 0, 1, 1, $post_break ) 425f3efcd01Safresh1 ) 426f3efcd01Safresh1 )[5] 427*5486feefSafresh1 ) 428*5486feefSafresh1 + 1900, 429f3efcd01Safresh1 $post_break, 430f3efcd01Safresh1 "year $post_break is treated as year $post_break", 431f3efcd01Safresh1 ); 432f3efcd01Safresh1 }, 433f3efcd01Safresh1 ); 434256a93a4Safresh1} 435256a93a4Safresh1 436256a93a4Safresh1sub _test_invalid_values { 437256a93a4Safresh1 my %bad = ( 438256a93a4Safresh1 'month > bounds' => [ 1995, 13, 1, 1, 1, 1 ], 439256a93a4Safresh1 'day > bounds' => [ 1995, 2, 30, 1, 1, 1 ], 440256a93a4Safresh1 'hour > bounds' => [ 1995, 2, 10, 25, 1, 1 ], 441256a93a4Safresh1 'minute > bounds' => [ 1995, 2, 10, 1, 60, 1 ], 442256a93a4Safresh1 'second > bounds' => [ 1995, 2, 10, 1, 1, 60 ], 443256a93a4Safresh1 'month < bounds' => [ 1995, -1, 1, 1, 1, 1 ], 444256a93a4Safresh1 'day < bounds' => [ 1995, 2, -1, 1, 1, 1 ], 445256a93a4Safresh1 'hour < bounds' => [ 1995, 2, 10, -1, 1, 1 ], 446256a93a4Safresh1 'minute < bounds' => [ 1995, 2, 10, 1, -1, 1 ], 447256a93a4Safresh1 'second < bounds' => [ 1995, 2, 10, 1, 1, -1 ], 448256a93a4Safresh1 ); 449256a93a4Safresh1 450256a93a4Safresh1 for my $sub ( grep { !/nocheck/ } @local_subs, @gm_subs ) { 451256a93a4Safresh1 subtest( 452256a93a4Safresh1 $sub, 453256a93a4Safresh1 sub { 454256a93a4Safresh1 for my $key ( sort keys %bad ) { 455256a93a4Safresh1 my ( $year, $mon, $mday, $hour, $min, $sec ) 456256a93a4Safresh1 = @{ $bad{$key} }; 457256a93a4Safresh1 $mon--; 458256a93a4Safresh1 459256a93a4Safresh1 local $@ = undef; 460256a93a4Safresh1 eval { 461256a93a4Safresh1 __PACKAGE__->can($sub) 462256a93a4Safresh1 ->( $sec, $min, $hour, $mday, $mon, $year ); 463256a93a4Safresh1 }; 464256a93a4Safresh1 465256a93a4Safresh1 like( 466256a93a4Safresh1 $@, qr/.*out of range.*/, 467256a93a4Safresh1 "$key - @{ $bad{$key} }" 468256a93a4Safresh1 ); 469256a93a4Safresh1 } 470f3efcd01Safresh1 }, 471f3efcd01Safresh1 ); 472256a93a4Safresh1 } 473256a93a4Safresh1 474256a93a4Safresh1 for my $sub ( grep {/nocheck/} @local_subs, @gm_subs ) { 475256a93a4Safresh1 subtest( 476256a93a4Safresh1 $sub, 477256a93a4Safresh1 sub { 478256a93a4Safresh1 for my $key ( sort keys %bad ) { 479256a93a4Safresh1 local $@ = q{}; 480256a93a4Safresh1 eval { __PACKAGE__->can($sub)->( @{ $bad{$key} } ); }; 481256a93a4Safresh1 is( 482256a93a4Safresh1 $@, q{}, 483256a93a4Safresh1 "$key - @{ $bad{$key} } - no exception with checks disabled" 484256a93a4Safresh1 ); 485256a93a4Safresh1 } 486256a93a4Safresh1 }, 487256a93a4Safresh1 ); 488256a93a4Safresh1 } 489256a93a4Safresh1} 49091f110e0Safresh1 491*5486feefSafresh1sub _test_non_integer_seconds { 492*5486feefSafresh1 my @epochs = ( 0, 1636484894 ); 493*5486feefSafresh1 push @epochs, -1636484894 if $neg_epoch_ok; 494*5486feefSafresh1 495*5486feefSafresh1 # We want to get a lot of different values to smoke out floating point 496*5486feefSafresh1 # issues. 497*5486feefSafresh1 for my $i ( 1 .. 30 ) { 498*5486feefSafresh1 push @epochs, $i * 11; 499*5486feefSafresh1 push @epochs, $i * 11 * -1 if $neg_epoch_ok; 500*5486feefSafresh1 } 501*5486feefSafresh1 502*5486feefSafresh1 for my $epoch (@epochs) { 503*5486feefSafresh1 subtest( 504*5486feefSafresh1 "epoch = $epoch", 505*5486feefSafresh1 sub { 506*5486feefSafresh1 subtest( 507*5486feefSafresh1 'localtime', 508*5486feefSafresh1 sub { 509*5486feefSafresh1 my @lt = localtime($epoch); 510*5486feefSafresh1 my $orig_sec = $lt[0]; 511*5486feefSafresh1 512*5486feefSafresh1 for my $i ( 1 .. 99 ) { 513*5486feefSafresh1 my $subsec = $i / 100; 514*5486feefSafresh1 $lt[0] = $orig_sec + $subsec; 515*5486feefSafresh1 my $time = timelocal_posix( @lt[ 0 .. 5 ] ); 516*5486feefSafresh1 is( 517*5486feefSafresh1 $time, $epoch + $subsec, 518*5486feefSafresh1 "non-integer second of $subsec" 519*5486feefSafresh1 ); 520*5486feefSafresh1 } 521*5486feefSafresh1 } 522*5486feefSafresh1 ); 523*5486feefSafresh1 524*5486feefSafresh1 subtest( 525*5486feefSafresh1 'gmtime', 526*5486feefSafresh1 sub { 527*5486feefSafresh1 my @gt = ( gmtime($epoch) )[ 0 .. 5 ]; 528*5486feefSafresh1 my $orig_sec = $gt[0]; 529*5486feefSafresh1 530*5486feefSafresh1 for my $i ( 1 .. 99 ) { 531*5486feefSafresh1 my $subsec = $i / 100; 532*5486feefSafresh1 $gt[0] = $orig_sec + $subsec; 533*5486feefSafresh1 my $time = timegm_posix(@gt); 534*5486feefSafresh1 is( 535*5486feefSafresh1 $time, $epoch + $subsec, 536*5486feefSafresh1 "non-integer second of $subsec" 537*5486feefSafresh1 ); 538*5486feefSafresh1 } 539*5486feefSafresh1 } 540*5486feefSafresh1 ); 541*5486feefSafresh1 } 542*5486feefSafresh1 ); 543*5486feefSafresh1 } 544*5486feefSafresh1} 545*5486feefSafresh1 54691f110e0Safresh1done_testing(); 547