1############################################################################### 2# DateTime.pm # 3# $Date: 12.02.14 $ # 4############################################################################### 5# YaBB: Yet another Bulletin Board # 6# Open-Source Community Software for Webmasters # 7# Version: YaBB 2.6.11 # 8# Packaged: December 2, 2014 # 9# Distributed by: http://www.yabbforum.com # 10# =========================================================================== # 11# Copyright (c) 2000-2014 YaBB (www.yabbforum.com) - All Rights Reserved. # 12# Software by: The YaBB Development Team # 13# with assistance from the YaBB community. # 14############################################################################### 15no warnings qw(uninitialized once redefine); 16use CGI::Carp qw(fatalsToBrowser); 17use English qw(-no_match_vars); 18use Time::Local; 19our $VERSION = '2.6.11'; 20 21$datetimepmver = 'YaBB 2.6.11 $Revision: 1611 $'; 22 23@days_rfc = qw( Sun Mon Tue Wed Thu Fri Sat ); 24 # for RFC compliant feed time 25@months_rfc = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); 26 27sub calcdifference { # Input: $date1 $date2 28 $result = int( $date2 / 86400 ) - int( $date1 / 86400 ); 29 return $result; 30} 31 32sub toffs { 33 my ($mydate, $forum_default) = @_; 34 my $toffs = 0; 35 36 if ( $iamguest || $forum_default || !${ $uid . $username }{'user_tz'} ) { 37 $tzname = $default_tz || 'UTC'; 38 } 39 else { 40 $tzname = ${ $uid . $username }{'user_tz'}; 41 } 42 43 eval { 44 require DateTime; 45 require DateTime::TimeZone; 46 }; 47 if( !$EVAL_ERROR ) { 48 DateTime->import(); 49 DateTime::TimeZone->import(); 50 if ( $tzname eq 'local' ) { 51 $tzname = 'UTC'; 52 } 53 my $tz = DateTime::TimeZone->new(name => $tzname); 54 my $now = DateTime->from_epoch( 'epoch' => $mydate ); 55 $toffs = $tz->offset_for_datetime($now); 56 } 57 elsif ( $EVAL_ERROR ) { 58 if ( $tzname eq 'local' ) { 59 $toffs = $timeoffset; 60 $toffs += 61 ( localtime( $mydate + ( 3600 * $toffs ) ) )[8] ? $dstoffset : 0; 62 $toffs = 3600 * $toffs; 63 } 64 else { $toffs = 0; } 65 } 66 else { $toffs = 0; } 67 68 return $toffs; 69} 70 71sub timetostring { 72 my ($thedate) = @_; 73 return 0 if !$thedate; 74 if ( !$maintxt{'107'} ) { $maintxt{'107'} = 'at'; } 75 my $toffs = 0; 76 if ($enabletz) { 77 $toffs = toffs($thedate); 78 } 79 my $newtime = $thedate + $toffs; 80 81 ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, undef ) = 82 gmtime( $newtime ); 83 $sec = sprintf '%02d', $sec; 84 $min = sprintf '%02d', $min; 85 $hour = sprintf '%02d', $hour; 86 $mday = sprintf '%02d', $mday; 87 $mon_num = $mon + 1; 88 $mon_num = sprintf '%02d', $mon_num; 89 $year = 1900 + $year; 90 $saveyear = ( $year % 100 ); 91 $saveyear = sprintf '%02d', $saveyear; 92 return "$mon_num/$mday/$saveyear $maintxt{'107'} $hour\:$min\:$sec"; 93} 94 95# generic string-to-time converter 96 97sub stringtotime { 98 my ($spvar) = @_; 99 if ( !$spvar ) { return 0; } 100 $splitvar = $spvar; 101 102# receive standard format yabb date/time string. 103# allow for oddities thrown up from y1 , with full year / single digit day/month 104 my $amonth = 1; 105 my $aday = 1; 106 my $ayear = 0; 107 my $ahour = 0; 108 my $amin = 0; 109 my $asec = 0; 110 111 if ( $splitvar =~ 112 m/(\d{1,2})\/(\d{1,2})\/(\d{2,4}).*?(\d{1,2})\:(\d{1,2})\:(\d{1,2})/sm ) 113 { 114 $amonth = int $1; 115 $aday = int $2; 116 $ayear = int $3; 117 $ahour = int $4; 118 $amin = int $5; 119 $asec = int $6; 120 } 121 elsif ( $splitvar =~ m/(\d{1,2})\/(\d{1,2})\/(\d{2,4})/sm ) { 122 $amonth = int $1; 123 $aday = int $2; 124 $ayear = int $3; 125 $ahour = 0; 126 $amin = 0; 127 $asec = 0; 128 } 129 130 # Uses 1904 and 2036 as the default dates, as both are leap years. 131 # If we used the real extremes (1901 and 2038) - there would be problems 132 # As time dies if you provide 29th Feb as a date in a non-leap year 133 # Using leap years as the default years prevents this from happening. 134 135 if ( $ayear >= 36 && $ayear <= 99 ) { $ayear += 1900; } 136 elsif ( $ayear >= 00 && $ayear <= 35 ) { $ayear += 2000; } 137 if ( $ayear < 1904 ) { $ayear = 1904; } 138 elsif ( $ayear > 2036 ) { $ayear = 2036; } 139 140 if ( $amonth < 1 ) { $amonth = 0; } 141 elsif ( $amonth > 12 ) { $amonth = 11; } 142 else { --$amonth; } 143 144 if ( $amonth == 3 || $amonth == 5 || $amonth == 8 || $amonth == 10 ) { 145 $max_days = 30; 146 } 147 elsif ( $amonth == 1 && $ayear % 4 == 0 ) { $max_days = 29; } 148 elsif ( $amonth == 1 && $ayear % 4 != 0 ) { $max_days = 28; } 149 else { $max_days = 31; } 150 if ( $aday > $max_days ) { $aday = $max_days; } 151 152 if ( $ahour < 1 ) { $ahour = 0; } 153 elsif ( $ahour > 23 ) { $ahour = 23; } 154 if ( $amin < 1 ) { $amin = 0; } 155 elsif ( $amin > 59 ) { $amin = 59; } 156 if ( $asec < 1 ) { $asec = 0; } 157 elsif ( $asec > 59 ) { $asec = 59; } 158 159 return ( timegm( $asec, $amin, $ahour, $aday, $amonth, $ayear ) ); 160} 161 162sub timeformat { 163 my ( $oldformat, $dontusetoday, $use_rfc, $forum_default, $lower ) = @_; 164 165 # use forum default time and format 166 167 $mytimeselected = 168 ( $forum_default || !${ $uid . $username }{'timeselect'} ) 169 ? $timeselected 170 : ${ $uid . $username }{'timeselect'}; 171 172 chomp $oldformat; 173 return if !$oldformat; 174 175 # find out what timezone is to be used. 176 my $toffs = 0; 177 if ( $enabletz) { 178 $toffs = toffs($oldformat, $forum_default); 179 } 180 my $mynewtime = $oldformat + $toffs; 181 182 my ( 183 $newsecond, $newminute, $newhour, $newday, $newmonth, 184 $newyear, $newweekday, $newyearday, $newoff 185 ) = gmtime( $mynewtime ); 186 $newmonth++; 187 $newyear += 1900; 188 189 # Calculate number of full weeks this year 190 $newweek = int( ( $newyearday + 1 - $newweekday ) / 7 ) + 1; 191 192 # Add 1 if today isn't Saturday 193 if ( $newweekday < 6 ) { $newweek = $newweek + 1; } 194 $newweek = sprintf '%02d', $newweek; 195 196 if ($use_rfc) { 197 $shortday = $days_rfc[$newweekday]; 198 } 199 else { 200 $shortday = $days_short[$newweekday]; 201 } 202 203 $longday = $days[$newweekday]; 204 $newmonth = sprintf '%02d', $newmonth; 205 $newshortyear = ( $newyear % 100 ); 206 $newshortyear = sprintf '%02d', $newshortyear; 207 if ( $mytimeselected != 4 && $mytimeselected != 8 ) { 208 $newday = sprintf '%02d', $newday; 209 } 210 $newhour = sprintf '%02d', $newhour; 211 $newminute = sprintf '%02d', $newminute; 212 $newsecond = sprintf '%02d', $newsecond; 213 214 $newtime = $newhour . q{:} . $newminute . q{:} . $newsecond; 215 216 ( undef, undef, undef, undef, undef, $yy, undef, $yd, undef ) = 217 gmtime( $date + $toffs ); 218 $yy += 1900; 219 $daytxt = undef; # must be a global variable 220 if ( !$dontusetoday ) { 221 if ( $yd == $newyearday && $yy == $newyear ) { 222 223 # today 224 $daytxt = qq~<b>$maintxt{'769'}</b>~; 225 if ( $lower && $maintxt{'769l'} ) { 226 $daytxt = qq~<b>$maintxt{'769l'}</b>~; 227 } 228 } 229 elsif ( 230 ( ( $yd - 1 ) == $newyearday && $yy == $newyear ) 231 || ( $yd == 0 232 && $newday == 31 233 && $newmonth == 12 234 && ( $yy - 1 ) == $newyear ) 235 ) 236 { 237 238 # yesterday || yesterday, over a year end. 239 $daytxt = qq~<b>$maintxt{'769a'}</b>~; 240 if ( $lower && $maintxt{'769al'} ) { 241 $daytxt = qq~<b>$maintxt{'769al'}</b>~; 242 } 243 } 244 } 245 246 if ( !$maintxt{'107'} ) { $maintxt{'107'} = $admin_txt{'107'}; } 247 my @timform = ( 248 q{}, 249 time_1( $daytxt, $newday, $newmonth, $newyear, $newtime ), 250 time_2( $daytxt, $newday, $newmonth, $newyear, $newtime ), 251 time_3( $daytxt, $newday, $newmonth, $newyear, $newtime ), 252 time_4( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute, $lower ), 253 time_5( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 254 time_6( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 255 q{}, 256 time_8( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 257 ); 258 foreach my $i ( 1 .. 8 ) { 259 if ( $mytimeselected == $i ) { 260 $newformat = $timform[$i]; 261 } 262 } 263 return $newformat; 264} 265 266sub timeformatcal { 267 my ( $mynewtime, $usetoday ) = @_; 268 269 # use forum default time and format 270 271 $mytimeselected = 272 ( $forum_default || !${ $uid . $username }{'timeselect'} ) 273 ? $timeselected 274 : ${ $uid . $username }{'timeselect'}; 275 276 chomp $mynewtime; 277 return if !$mynewtime; 278 279 # find out what timezone is to be used. 280 my $toffs = 0; 281 my ( 282 $newsecond, $newminute, $newhour, $newday, $newmonth, 283 $newyear, $newweekday, $newyearday, $newoff 284 ) = gmtime( $mynewtime ); 285 $newmonth++; 286 $newyear += 1900; 287 288 # Calculate number of full weeks this year 289 $newweek = int( ( $newyearday + 1 - $newweekday ) / 7 ) + 1; 290 291 # Add 1 if today isn't Saturday 292 if ( $newweekday < 6 ) { $newweek = $newweek + 1; } 293 $newweek = sprintf '%02d', $newweek; 294 295 if ($use_rfc) { 296 $shortday = $days_rfc[$newweekday]; 297 } 298 else { 299 $shortday = $days_short[$newweekday]; 300 } 301 302 $longday = $days[$newweekday]; 303 $newmonth = sprintf '%02d', $newmonth; 304 $newshortyear = ( $newyear % 100 ); 305 $newshortyear = sprintf '%02d', $newshortyear; 306 if ( $mytimeselected != 4 && $mytimeselected != 8 ) { 307 $newday = sprintf '%02d', $newday; 308 } 309 $newhour = sprintf '%02d', $newhour; 310 $newminute = sprintf '%02d', $newminute; 311 $newsecond = sprintf '%02d', $newsecond; 312 313 $newtime = $newhour . q{:} . $newminute . q{:} . $newsecond; 314 315 if ( $enabletz) { 316 $toffs = toffs($date); 317 } 318 ( undef, undef, undef, undef, undef, $yy, undef, $yd, undef ) = 319 gmtime( $date + $toffs ); 320 $yy += 1900; 321 $daytxt = undef; # must be a global variable 322 if ( $usetoday == 1 ) { 323 $myleap = IsLeap($yy); 324 if ( $yd == $newyearday && $yy == $newyear ) { 325 326 # today 327 $daytxt = qq~<b>$maintxt{'769'}</b>~; 328 329 } 330 elsif ( 331 ( ( $yd - 1 ) == $newyearday && $yy == $newyear ) 332 || ( $yd == 0 333 && $newday == 31 334 && $newmonth == 12 335 && ( $yy - 1 ) == $newyear ) 336 ) 337 { 338 339 # yesterday || yesterday, over a year end. 340 $daytxt = qq~<b>$maintxt{'769a'}</b>~; 341 } 342 elsif ( 343 ( ( $yd + 1 ) == $newyearday && $yy == $newyear ) 344 || ( $yd == ( 365 + $myleap ) 345 && $newday == 1 346 && $newmonth == 0 347 && ( $yy + 1 ) == $newyear ) 348 ) 349 { 350 351 # tomorrow || tomorrow, over a year end. 352 $daytxt = qq~<b>$maintxt{'769b'}</b>~; 353 } 354 } 355 356 if ( !$maintxt{'107'} ) { $maintxt{'107'} = $admin_txt{'107'}; } 357 my @timform = ( 358 q{}, 359 time_1( $daytxt, $newday, $newmonth, $newyear, $newtime ), 360 time_2( $daytxt, $newday, $newmonth, $newyear, $newtime ), 361 time_3( $daytxt, $newday, $newmonth, $newyear, $newtime ), 362 time_4( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 363 time_5( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 364 time_6( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 365 q{}, 366 time_8( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ), 367 ); 368 foreach my $i ( 1 .. 8 ) { 369 if ( $mytimeselected == $i ) { 370 $newformat = $timform[$i]; 371 } 372 } 373 $newformat = dtonly($newformat); 374 return $newformat; 375} 376 377sub CalcAge { 378 my ( $user, $act ) = @_; 379 380 timetostring($date); 381 my ( $usermonth, $userday, $useryear ); 382 383 if ( ${ $uid . $user }{'bday'} ne q{} ) { 384 ( $usermonth, $userday, $useryear ) = 385 split /\//xsm, ${ $uid . $user }{'bday'}; 386 387 if ( $act eq 'calc' ) { 388 if ( length( ${ $uid . $user }{'bday'} ) <= 2 ) { 389 $age = ${ $uid . $user }{'bday'}; 390 } 391 else { 392 $age = $year - $useryear; 393 if ( $usermonth > $mon_num 394 || ( $usermonth == $mon_num && $userday > $mday ) ) 395 { 396 --$age; 397 } 398 } 399 } 400 if ( $act eq 'parse' ) { 401 if ( length( ${ $uid . $user }{'bday'} ) <= 2 ) { return; } 402 $umonth = $usermonth; 403 $uday = $userday; 404 $uyear = $useryear; 405 } 406 if ( $act eq 'isbday' ) { 407 if ( $usermonth == $mon_num && $userday == $mday ) { 408 $isbday = 'yes'; 409 } 410 } 411 } 412 else { 413 $age = q{}; 414 $isbday = q{}; 415 } 416 return; 417} 418 419sub NumberFormat { 420 my ($inp) = @_; 421 my ( $decimal, $fraction ) = split /\./xsm, $inp; 422 my $tmpforumformat = $forumnumberformat || 1; 423 my $numberformat = ${ $uid . $username }{'numberformat'} || $tmpforumformat; 424 my @septor = 425 ( [ q{}, q{}, q{,}, q{.}, q{ }, ], [ q{.}, q{,}, q{.}, q{,}, q{,}, ], ); 426 427 foreach my $i ( 0 .. 4 ) { 428 $dra = $septor[0]->[$i]; 429 $drb = $septor[1]->[$i]; 430 if ( $numberformat == ( $i + 1 ) ) { 431 $separator = $dra; 432 $decimalpt = $drb; 433 } 434 } 435 if ( $decimal =~ m/\d{4,}/sm ) { 436 $decimal = reverse $decimal; 437 $decimal =~ s/(\d{3})/$1$separator/gsm; 438 $decimal = reverse $decimal; 439 $decimal =~ s/^(\.|\,| )//sm; 440 } 441 $newnumber = $decimal; 442 if ($fraction) { 443 $newnumber .= "$decimalpt$fraction"; 444 } 445 return $newnumber; 446} 447 448sub time_1 { 449 my ( $daytxt, $newday, $newmonth, $newyear, $newtime ) = @_; 450 $newformat = 451 $daytxt 452 ? qq~$daytxt $maintxt{'107'} $newtime~ 453 : qq~$newmonth/$newday/$newshortyear $maintxt{'107'} $newtime~; 454 455 return $newformat; 456} 457 458sub time_2 { 459 my ( $daytxt, $newday, $newmonth, $newyear, $newtime ) = @_; 460 $newformat = 461 $daytxt 462 ? qq~$daytxt $maintxt{'107'} $newtime~ 463 : qq~$newday.$newmonth.$newshortyear $maintxt{'107'} $newtime~; 464 465 return $newformat; 466} 467 468sub time_3 { 469 my ( $daytxt, $newday, $newmonth, $newyear, $newtime ) = @_; 470 $newformat = 471 $daytxt 472 ? qq~$daytxt $maintxt{'107'} $newtime~ 473 : qq~$newday.$newmonth.$newyear $maintxt{'107'} $newtime~; 474 475 return $newformat; 476} 477 478sub time_4 { 479 my ( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute, $lower ) = @_; 480 $ampm = $newhour > 11 ? 'pm' : 'am'; 481 $newhour2 = $newhour % 12 || 12; 482 if ( !@months_m ) { @months_m = @months; } 483 if ($use_rfc) { $newmonth2 = $months_rfc[ $newmonth - 1 ]; } 484 elsif ( $lower ) { $newmonth2 = $months_m[ $newmonth - 1 ]; } 485 else { $newmonth2 = $months[ $newmonth - 1 ]; } 486 $newday2 = "$timetxt{'4'}"; 487 if ( $newday > 10 && $newday < 20 ) { 488 $newday2 = "$timetxt{'4'}"; 489 } 490 else { 491 foreach my $i ( 1 .. 3 ) { 492 if ( $newday % 10 == $i ) { 493 $newday2 = qq~$timetxt{$i}~; 494 } 495 } 496 } 497 $newformat = 498 $daytxt 499 ? qq~$daytxt $maintxt{'107'} $newhour2:$newminute$ampm~ 500 : qq~$newmonth2$maintxt{'770'} $newday$newday2, $newyear $maintxt{'107'} $newhour2:$newminute$ampm~; 501 502 return $newformat; 503} 504 505sub time_5 { 506 my ( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ) = @_; 507 $ampm = $newhour > 11 ? 'pm' : 'am'; 508 $newhour2 = $newhour % 12 || 12; 509 $newformat = 510 $daytxt 511 ? qq~$daytxt $maintxt{'107'} $newhour2:$newminute$ampm~ 512 : qq~$newmonth/$newday/$newshortyear $maintxt{'107'} $newhour2:$newminute$ampm~; 513 514 return ($newformat); 515} 516 517sub time_6 { 518 my ( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ) = @_; 519 if ($use_rfc) { $newmonth2 = $months_rfc[ $newmonth - 1 ]; } 520 elsif ( @months_m ) { $newmonth2 = $months_m[ $newmonth - 1 ]; } 521 else { $newmonth2 = $months[ $newmonth - 1 ]; } 522 $newformat = 523 $daytxt 524 ? qq~$daytxt $maintxt{'107'} $newhour:$newminute~ 525 : qq~$newday. $newmonth2$maintxt{'770a'} $newyear $maintxt{'107'} $newhour:$newminute~; 526 527 return $newformat; 528} 529 530sub time_8 { 531 my ( $daytxt, $newday, $newmonth, $newyear, $newhour, $newminute ) = @_; 532 $ampm = $newhour > 11 ? 'pm' : 'am'; 533 $newhour2 = $newhour % 12 || 12; 534 if ($use_rfc) { $newmonth2 = $months_rfc[ $newmonth - 1 ]; } 535 elsif ( @months_m ) { $newmonth2 = $months_m[ $newmonth - 1 ]; } 536 else { $newmonth2 = $months[ $newmonth - 1 ]; } 537 $newday2 = "$timetxt{'4'}"; 538 if ( $newday > 10 && $newday < 20 ) { 539 $newday2 = "$timetxt{'4'}"; 540 } 541 else { 542 foreach my $i ( 1 .. 3 ) { 543 if ( $newday % 10 == $i ) { 544 $newday2 = qq~$timetxt{$i}~; 545 } 546 } 547 } 548 $newformat = 549 $daytxt 550 ? qq~$daytxt $maintxt{'107'} $newhour2:$newminute$ampm~ 551 : qq~$newday$newday2 $newmonth2$maintxt{'770a'}, $newyear $maintxt{'107'} $newhour2:$newminute$ampm~; 552 553 return $newformat; 554} 555 556sub dtonly { 557 my ($newformat) = @_; 558 if ( $newformat =~ m/\A(.*?)\s*$maintxt{'107'}\s*(.*?)\Z/ism ) { 559 $dateonly = $1; 560 } 561 562 return ($dateonly); 563} 564 565sub tmonly { 566 my ($newformat) = @_; 567 if ( $newformat =~ m/\A(.*?)\s*$maintxt{'107'}\s*(.*?)\Z/ism ) { 568 $timeonly = $2; 569 } 570 571 return ($timeonly); 572} 573 574sub bdayno_year { 575 my ($newformat) = @_; 576 $date_noyear = $newformat; 577 if ( $mytimeselected == 4 || $mytimeselected == 8 ) { 578 ( $date_noyear, undef ) = split /\,/xsm, $newformat; 579 } 580 elsif ( $mytimeselected == 1 || $mytimeselected == 5 ) { 581 @date_noyear = split /\//xsm, $newformat; 582 $date_noyear = qq~$date_noyear[0]~ . q{/} . qq~$date_noyear[1]~; 583 } 584 elsif ( $mytimeselected == 2 || $mytimeselected == 3 ) { 585 @date_noyear = split /[.]/xsm, $newformat; 586 $date_noyear = qq~$date_noyear[0]~ . q{/} . qq~$date_noyear[1]~; 587 } 588 elsif ( $mytimeselected == 6 ) { 589 @date_noyear = split / /sm, $newformat; 590 $date_noyear = qq~$date_noyear[0] $date_noyear[1]~; 591 } 592 593 return ($date_noyear); 594} 595 596sub IsLeap { 597 my ($year ) = @_; 598 return 0 if $year % 4; 599 return 1 if $year % 100; 600 return 0 if $year % 400; 601 return 1; 602} 603 6041;