Ignore:
Timestamp:
11/22/08 11:11:26 (4 years ago)
Author:
CrawfordCurrie
Message:

Item240: recoded interval parser and added unit tests

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/core/lib/Foswiki/Time.pm

    r830 r848  
    350350   * y(year), m(month), w(week), d(day), h(hour), M(minute), S(second) 
    351351 
    352 =date= follows ISO8601 and must include hypens.  (any amount of trailing 
     352=date= follows ISO8601 and must include hyphens.  (any amount of trailing 
    353353       elements may be omitted and will be filled in differently on the 
    354354       differents ends of the interval as to include the longest possible 
     
    368368 
    369369sub parseInterval { 
    370     my ($theInterval) = @_; 
    371  
     370    my ($interval) = @_; 
    372371    my @lt    = localtime(); 
    373372    my $today = sprintf( '%04d-%02d-%02d', $lt[5] + 1900, $lt[4] + 1, $lt[3] ); 
     
    375374 
    376375    # replace $now and $today shortcuts 
    377     $theInterval =~ s/\$today/$today/g; 
    378     $theInterval =~ s/\$now/$now/g; 
     376    $interval =~ s/\$today/$today/g; 
     377    $interval =~ s/\$now/$now/g; 
    379378 
    380379    # if $theDate does not contain a '/': force it to do so. 
    381     $theInterval = $theInterval . '/' . $theInterval 
    382       unless ( $theInterval =~ /\// ); 
    383  
    384     my @ends = split( /\//, $theInterval ); 
     380    $interval = $interval . '/' . $interval 
     381      unless ( $interval =~ /\// ); 
     382 
     383    my ($first, $last) = split( /\//, $interval, 2 ); 
     384    my ( $start, $end ); 
    385385 
    386386    # first translate dates into seconds from epoch, 
    387387    # in the second loop we will examine interval durations. 
    388388 
    389     foreach my $i ( 0, 1 ) { 
    390  
    391         #   if not a period of time: 
    392         next if ( $ends[$i] =~ /^P/ ); 
    393  
    394         #   TODO assert(must include the year) 
    395         if ($i) { 
    396  
    397             # fillEnd 
    398             #     if ending point, complete with parts from "-12-31T23:59:60" 
    399             #     if completing ending point, check last day of month 
    400             # TODO: do we do leap years? 
    401             if ( length( $ends[$i] ) == 7 ) { 
    402                 my $month = substr( $ends[$i], 5 ); 
    403                 $ends[$i] .= $MONTHLENS[ $month - 1 ]; 
    404             } 
    405             $ends[$i] .= substr( "0000-12-31T23:59:59", length( $ends[$i] ) ); 
    406         } 
    407         else { 
    408  
    409             # fillStart 
    410             #     if starting point, complete with parts from "-01-01T00:00:00" 
    411             $ends[$i] .= substr( "0000-01-01T00:00:00", length( $ends[$i] ) ); 
    412         } 
    413  
    414         #     convert the string into integer amount of seconds 
    415         #     from 1970-01-01T00:00:00.00 UTC 
    416  
    417         $ends[$i] = parseTime( $ends[$i], 1 ); 
    418     } 
    419  
    420     # now we're ready to translate interval durations... 
    421     # ... we don't do P<whatever/P<whatever> !!! 
    422  
    423     my @oper = ( "-", "+" ); 
    424  
    425     # if any extreme was a time duration, examine it 
    426     foreach my $i ( 0, 1 ) { 
    427         next unless ( $ends[$i] =~ /^P/ ); 
    428  
    429         #   drop the 'P', substitute each letter with '*<value>+', 
    430         #   where <value> is the amount of seconds represented by 
    431         #   the unit.  for example: w (week) becomes '*604800+'. 
    432         $ends[$i] =~ s/^P//; 
    433         $ends[$i] =~ s/y/\*31556925\+/gi;    # tropical year 
    434         $ends[$i] =~ s/m/\*2592000\+/g;      # 1m = 30 days 
    435         $ends[$i] =~ s/w/\*604800\+/gi;      # 1w = 7 days 
    436         $ends[$i] =~ s/d/\*86400\+/gi; 
    437         $ends[$i] =~ s/h/\*3600\+/gi; 
    438         $ends[$i] =~ s/M/\*60\+/g;           # note: m != M 
    439         $ends[$i] =~ s/S/\*1\+/gi; 
    440  
    441         #   possibly append '0' and evaluate numerically the string. 
    442         $ends[$i] =~ s/\+$/+0/; 
    443         my $duration = eval( $ends[$i] ); 
    444  
    445         #   the value computed, if it specifies the starting point 
    446         #   in time, must be subtracted from the previously 
    447         #   computed ending point.  if it specifies the ending 
    448         #   point, it must be added to the previously computed 
    449         #   starting point. 
    450         $ends[$i] = eval( $ends[ 1 - $i ] . $oper[$i] . $ends[$i] ); 
    451  
    452         # SMELL: if the user specified both start and end as a 
    453         # time duration, some kind of error must be reported. 
    454     } 
    455     return @ends; 
     389    if ( $first !~ /^P/ ) { 
     390        # complete with parts from "-01-01T00:00:00" 
     391        if ( length($first) < length('0000-01-01T00:00:00')) { 
     392            $first .= substr( '0000-01-01T00:00:00', length( $first ) ); 
     393        } 
     394        $start = parseTime( $first, 1 ); 
     395    } 
     396 
     397    if ($last !~ /^P/) { 
     398        # complete with parts from "-12-31T23:59:60" 
     399        # check last day of month 
     400        # TODO: do we do leap years? 
     401        if ( length( $last ) == 7 ) { 
     402            my $month = substr( $last, 5 ); 
     403            $last .= '-'.$MONTHLENS[ $month - 1 ]; 
     404        } 
     405        if ( length($last) < length('0000-12-31T23:59:59')) { 
     406            $last .= substr( '0000-12-31T23:59:59', length( $last ) ); 
     407        } 
     408        $end = parseTime( $last, 1 ); 
     409    } 
     410 
     411    if (!defined($start)) { 
     412        $start = $end - _parseDuration( $first ); 
     413    } 
     414    if (!defined($end)) { 
     415        $end = $start + _parseDuration( $last ); 
     416    } 
     417    return ( $start || 0, $end || 0); 
     418} 
     419 
     420sub _parseDuration { 
     421    my $s = shift; 
     422    my $d = 0; 
     423    $s =~ s/(\d+)y/$d += $1 * 31556925;''/gei;    # tropical year 
     424    $s =~ s/(\d+)m/$d += $1 * 2592000; ''/ge;     # 1m = 30 days 
     425    $s =~ s/(\d+)w/$d += $1 * 604800;  ''/gei;    # 1w = 7 days 
     426    $s =~ s/(\d+)d/$d += $1 * 86400;   ''/gei;    # 1d = 24 hours 
     427    $s =~ s/(\d+)h/$d += $1 * 3600;    ''/gei;    # 1 hour = 60 mins 
     428    $s =~ s/(\d+)M/$d += $1 * 60;      ''/ge;     # note: m != M 
     429    $s =~ s/(\d+)S/$d += $1 * 1;       ''/gei; 
     430    return $d; 
    456431} 
    457432 
Note: See TracChangeset for help on using the changeset viewer.