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;