Skip to content

Commit

Permalink
Item11003: make time functions work with dates before 1970
Browse files Browse the repository at this point in the history
A date before 01.01.1970 has a negative epoch. Perl time functions can handle this. This patch allows a negative epoch in CALC time functions.
  • Loading branch information
khnz authored and gac410 committed Aug 22, 2014
1 parent 291ba7f commit ccb0cad
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 55 deletions.
38 changes: 19 additions & 19 deletions SpreadSheetPlugin/lib/Foswiki/Plugins/SpreadSheetPlugin/Calc.pm
Expand Up @@ -1104,7 +1104,7 @@ sub _FORMATTIME {
#elsif ( $theFunc =~ /^(FORMATTIME|FORMATGMTIME)$/ ) {

my ( $time, $str ) = split( /,\s*/, $_[0], 2 );
if ( $time =~ /([0-9]+)/ ) {
if ( $time =~ /(-?[0-9]+)/ ) {
$time = $1;
}
else {
Expand All @@ -1123,7 +1123,7 @@ sub _FORMATTIMEDIFF {
$prec = int( _getNumber($prec) - 1 );
$prec = 0 if ( $prec < 0 );
$time = _getNumber($time);
$time = 0 if ( $time < 0 );
$time *= -1 if ( $time < 0 );
my @unit = ( 0, 0, 0, 0, 0, 0 ); # sec, min, hours, days, month, years
my @factor =
( 1, 60, 60, 24, 30.4166, 12 ); # sec, min, hours, days, month, years
Expand Down Expand Up @@ -1212,8 +1212,8 @@ sub _TIMEADD {
$time = 0 unless ($time);
$value = 0 unless ($value);
$scale = "" unless ($scale);
$time =~ s/.*?([0-9]+).*/$1/ || 0;
$value =~ s/.*?(\-?[0-9\.]+).*/$1/ || 0;
$time =~ s/.*?(-?[0-9]+).*/$1/ || 0;
$value =~ s/.*?(-?[0-9\.]+).*/$1/ || 0;
$value *= 60 if ( $scale =~ /^min/i );
$value *= 3600 if ( $scale =~ /^hou/i );
$value *= 3600 * 24 if ( $scale =~ /^day/i );
Expand All @@ -1232,8 +1232,8 @@ sub _TIMEDIFF {
$scale ||= '';
$time1 = 0 unless ($time1);
$time2 = 0 unless ($time2);
$time1 =~ s/.*?([0-9]+).*/$1/ || 0;
$time2 =~ s/.*?([0-9]+).*/$1/ || 0;
$time1 =~ s/[^-0-9]*?(-?[0-9]+).*/$1/ || 0;
$time2 =~ s/[^-0-9]*?(-?[0-9]+).*/$1/ || 0;
my $result = $time2 - $time1;
$result /= 60 if ( $scale =~ /^min/i );
$result /= 3600 if ( $scale =~ /^hou/i );
Expand Down Expand Up @@ -1738,7 +1738,7 @@ m|([Dd][Oo][Yy])\s*([0-9]{4})[\.]([0-9]{1,3})[\.]([0-9]{1,2})[\.]([0-9]{1,2})[\.
{

# "DOY2003.122.23.15.59", "DOY2003.2.9.3.5.9" i.e. year.ddd.hh.mm.ss
$year = $2 - 1900;
$year = $2;
$day = $3;
$hour = $4;
$min = $5;
Expand All @@ -1750,7 +1750,7 @@ m|([Dd][Oo][Yy])\s*([0-9]{4})[\.]([0-9]{1,3})[\.]([0-9]{1,2})[\.]([0-9]{1,2})|
{

# "DOY2003.122.23.15", "DOY2003.2.9.3" i.e. year.ddd.hh.mm
$year = $2 - 1900;
$year = $2;
$day = $3;
$hour = $4;
$min = $5;
Expand All @@ -1760,14 +1760,14 @@ m|([Dd][Oo][Yy])\s*([0-9]{4})[\.]([0-9]{1,3})[\.]([0-9]{1,2})[\.]([0-9]{1,2})|
{

# "DOY2003.122.23", "DOY2003.2.9" i.e. year.ddd.hh
$year = $2 - 1900;
$year = $2;
$day = $3;
$hour = $4;
}
elsif ( $theText =~ m|([Dd][Oo][Yy])\s*([0-9]{4})[\.]([0-9]{1,3})| ) {

# "DOY2003.122", "DOY2003.2" i.e. year.ddd
$year = $2 - 1900;
$year = $2;
$day = $3;
}
elsif ( $theText =~
Expand All @@ -1778,7 +1778,7 @@ m|([0-9]{1,2})[-\s/]+([A-Z][a-z][a-z])[-\s/]+([0-9]{4})[-\s/]+([0-9]{1,2}):([0-9
# "31 Dec 2003 - 23:59:59", "31-Dec-2003 - 23:59:59", "31 Dec 2003 - 23:59:59 - any suffix"
$day = $1;
$mon = $mon2num{$2} || 0;
$year = $3 - 1900;
$year = $3;
$hour = $4;
$min = $5;
$sec = $6;
Expand All @@ -1791,7 +1791,7 @@ m|([0-9]{1,2})[-\s/]+([A-Z][a-z][a-z])[-\s/]+([0-9]{4})[-\s/]+([0-9]{1,2}):([0-9
# "31 Dec 2003 - 23:59", "31-Dec-2003 - 23:59", "31 Dec 2003 - 23:59 - any suffix"
$day = $1;
$mon = $mon2num{$2} || 0;
$year = $3 - 1900;
$year = $3;
$hour = $4;
$min = $5;
}
Expand All @@ -1803,16 +1803,16 @@ m|([0-9]{1,2})[-\s/]+([A-Z][a-z][a-z])[-\s/]+([0-9]{4})[-\s/]+([0-9]{1,2}):([0-9
$day = $1;
$mon = $mon2num{$2} || 0;
$year = $3;
$year += 100 if ( $year < 80 ); # "05" --> "105" (leave "99" as is)
$year -= 1900 if ( $year >= 1900 ); # "2005" --> "105"
$year += 2000 if ( $year < 80 );
$year += 1900 if ( $year < 100 and $year >= 80 );
}
elsif ( $theText =~
m|([0-9]{4})[-/\.]([0-9]{1,2})[-/\.]([0-9]{1,2})[-/\.\,\s]+([0-9]{1,2})[-\:/\.]([0-9]{1,2})[-\:/\.]([0-9]{1,2})|
)
{

# "2003/12/31 23:59:59", "2003-12-31-23-59-59", "2003.12.31.23.59.59"
$year = $1 - 1900;
$year = $1;
$mon = $2 - 1;
$day = $3;
$hour = $4;
Expand All @@ -1825,7 +1825,7 @@ m|([0-9]{4})[-/\.]([0-9]{1,2})[-/\.]([0-9]{1,2})[-/\.\,\s]+([0-9]{1,2})[-\:/\.](
{

# "2003/12/31 23:59", "2003-12-31-23-59", "2003.12.31.23.59"
$year = $1 - 1900;
$year = $1;
$mon = $2 - 1;
$day = $3;
$hour = $4;
Expand All @@ -1834,7 +1834,7 @@ m|([0-9]{4})[-/\.]([0-9]{1,2})[-/\.]([0-9]{1,2})[-/\.\,\s]+([0-9]{1,2})[-\:/\.](
elsif ( $theText =~ m|([0-9]{4})[-/]([0-9]{1,2})[-/]([0-9]{1,2})| ) {

# "2003/12/31", "2003-12-31"
$year = $1 - 1900;
$year = $1;
$mon = $2 - 1;
$day = $3;
}
Expand All @@ -1845,8 +1845,8 @@ m|([0-9]{4})[-/\.]([0-9]{1,2})[-/\.]([0-9]{1,2})[-/\.\,\s]+([0-9]{1,2})[-\:/\.](
$year = $3;
$mon = $1 - 1;
$day = $2;
$year += 100 if ( $year < 80 ); # "05" --> "105" (leave "99" as is)
$year -= 1900 if ( $year >= 1900 ); # "2005" --> "105"
$year += 2000 if ( $year < 80 );
$year += 1900 if ( $year < 100 and $year >= 80 );
}
else {

Expand Down
Expand Up @@ -406,48 +406,61 @@ sub test_FORMAT {

sub test_FORMATGMTIME {
my ($this) = @_;
$this->assert( $this->CALC('$FORMATGMTIME(1041379200, $day $mon $year)') eq
'01 Jan 2003' );

$this->assert_equals(
'2004-W53-6',
$this->CALC(
'$FORMATGMTIME($TIME(2005-01-01), $isoweek($year-W$wk-$day))')
);
$this->assert_equals(
'2009-W01-1',
$this->CALC(
'$FORMATGMTIME($TIME(2008-12-29), $isoweek($year-W$wk-$day))')
);
$this->assert_equals( '01 Jan 1970 - 00 00 00',
$this->CALC('$FORMATGMTIME(0, $day $mon $year - $hour $min $sec)') );
$this->assert_equals( '31 Dec 1969 - 23 59 59',
$this->CALC('$FORMATGMTIME(-1, $day $mon $year - $hour $min $sec)') );
$this->assert_equals( '19 Jan 2038 - 03 14 08',
$this->CALC('$FORMATGMTIME(2147483648, $day $mon $year - $hour $min $sec)') );
$this->assert_equals( '13 Dec 1901 - 20 45 51',
$this->CALC('$FORMATGMTIME(-2147483649, $day $mon $year - $hour $min $sec)') );

$this->assert_equals( '2004-W53-6',
$this->CALC('$FORMATGMTIME($TIME(2005-01-01), $isoweek($year-W$wk-$day))') );
$this->assert_equals( '2009-W01-1',
$this->CALC('$FORMATGMTIME($TIME(2008-12-29), $isoweek($year-W$wk-$day))') );
}

sub test_FORMATTIME {
my ($this) = @_;
$this->assert( $this->CALC('$FORMATTIME(0, $year/$month/$day GMT)') eq
'1970/01/01 GMT' );
$this->assert_equals(
'2004-W53-6 GMT',
$this->CALC(
'$FORMATTIME($TIME(2005-01-01 gmt), $isoweek($year-W$wk-$day) GMT)')
);
$this->assert_equals(
'2009-W01-1 GMT',
$this->CALC(
'$FORMATTIME($TIME(2008-12-29 gmt), $isoweek($year-W$wk-$day) GMT)')
);

$this->assert_equals( '1970/01/01 00:00:00 GMT',
$this->CALC('$FORMATTIME(0, $year/$month/$day $hour:$minute:$second GMT)') );
$this->assert_equals( '1969/12/31 23:59:59 GMT',
$this->CALC('$FORMATTIME(-1, $year/$month/$day $hour:$minute:$second GMT)') );
$this->assert_equals( '2038/01/19 03:14:08 GMT',
$this->CALC('$FORMATTIME(2147483648, $year/$month/$day $hour:$minute:$second GMT)') );
$this->assert_equals( '1901/12/13 20:45:51 GMT',
$this->CALC('$FORMATTIME(-2147483649, $year/$month/$day $hour:$minute:$second GMT)') );

$this->assert_equals( '2004-W53-6 GMT',
$this->CALC('$FORMATTIME($TIME(2005-01-01 gmt), $isoweek($year-W$wk-$day) GMT)') );
$this->assert_equals( '2009-W01-1 GMT',
$this->CALC('$FORMATTIME($TIME(2008-12-29 gmt), $isoweek($year-W$wk-$day) GMT)') );

}

sub test_FORMATTIMEDIFF {
my ($this) = @_;
$this->assert( $this->CALC('$FORMATTIMEDIFF(min, 1, 200)') eq '3 hours' );
$this->assert( $this->CALC('$FORMATTIMEDIFF(min, 2, 200)') eq
'3 hours and 20 minutes' );
$this->assert( $this->CALC('$FORMATTIMEDIFF(min, 1, 1640)') eq '1 day' );
$this->assert(
$this->CALC('$FORMATTIMEDIFF(min, 2, 1640)') eq '1 day and 3 hours' );
$this->assert( $this->CALC('$FORMATTIMEDIFF(min, 3, 1640)') eq
'1 day, 3 hours and 20 minutes' );

$this->assert_equals( '0 minutes',
$this->CALC('$FORMATTIMEDIFF(min, 1, 0)') );
$this->assert_equals( '3 hours',
$this->CALC('$FORMATTIMEDIFF(min, 1, 200)') );
$this->assert_equals( '3 hours and 20 minutes',
$this->CALC('$FORMATTIMEDIFF(min, 2, 200)') );
$this->assert_equals( '1 day',
$this->CALC('$FORMATTIMEDIFF(min, 1, 1640)') );
$this->assert_equals( '1 day and 3 hours',
$this->CALC('$FORMATTIMEDIFF(min, 2, 1640)') );
$this->assert_equals( '1 day, 3 hours and 20 minutes',
$this->CALC('$FORMATTIMEDIFF(min, 3, 1640)') );

$this->assert_equals( '0 minutes',
$this->CALC('$FORMATTIMEDIFF(min, 1, -0)') );
$this->assert_equals( '3 hours and 20 minutes',
$this->CALC('$FORMATTIMEDIFF(min, 2, -200)') );
}

sub test_GET_SET {
Expand Down Expand Up @@ -1225,8 +1238,25 @@ sub test_T {

sub test_TIME {
my ($this) = @_;
$this->assert_equals( '1066089600', $this->CALC('$TIME(2003/10/14 GMT)') );
$this->assert_equals( '1066089600', $this->CALC('$TIME(DOY2003.287)') );

$this->assert_equals( '0',
$this->CALC('$TIME(1970/01/01 GMT)') );
$this->assert_equals( '-31536000',
$this->CALC('$TIME(1969/01/01 GMT)') );
$this->assert_equals( '0',
$this->CALC('$TIME(1970/01/01 - 00:00:00 GMT)') );
$this->assert_equals( '-1',
$this->CALC('$TIME(1969/12/31 - 23:59:59 GMT)') );
$this->assert_equals( '2147483648',
$this->CALC('$TIME(2038/01/19 - 03:14:08 GMT)') );
$this->assert_equals( '-2147483649',
$this->CALC('$TIME(1901/12/13 - 20:45:51 GMT)') );

$this->assert_equals( '1066089600',
$this->CALC('$TIME(2003/10/14 GMT)') );
$this->assert_equals( '1066089600',
$this->CALC('$TIME(DOY2003.287)') );

$this->assert_equals(
$this->CALC('$TIME(2003/12/31 - 23:59:59)'),
$this->CALC('$TIME(DOY2003.365.23.59.59)')
Expand Down Expand Up @@ -1266,8 +1296,29 @@ sub test_TIMEADD {

sub test_TIMEDIFF {
my ($this) = @_;
$this->assert(
$this->CALC('$TIMEDIFF($TIME(), $EVAL($TIME()+90), minute)') == 1.5 );

$this->assert_equals( '1.5',
$this->CALC('$TIMEDIFF($TIME(), $EVAL($TIME()+90), minute)') );

$this->assert_equals( '1',
int( $this->CALC('$TIMEDIFF($TIME(2008/09/28 GMT), $TIME(2010/01/28 GMT), years)') ) );
$this->assert_equals( '16',
int( $this->CALC('$TIMEDIFF($TIME(2008/09/28 GMT), $TIME(2010/01/28 GMT), month)') ) );
$this->assert_equals( '487',
int( $this->CALC('$TIMEDIFF($TIME(2008/09/28 GMT), $TIME(2010/01/28 GMT), days)') ) );
$this->assert_equals( '11688',
int( $this->CALC('$TIMEDIFF($TIME(2008/09/28 GMT), $TIME(2010/01/28 GMT), hours)') ) );
$this->assert_equals( '701280',
int( $this->CALC('$TIMEDIFF($TIME(2008/09/28 GMT), $TIME(2010/01/28 GMT), minutes)') ) );
$this->assert_equals( '42076800',
int( $this->CALC('$TIMEDIFF($TIME(2008/09/28 GMT), $TIME(2010/01/28 GMT), seconds)') ) );

$this->assert_equals( '18',
int( $this->CALC('$TIMEDIFF($TIME(1990/1/1 GMT), $TIME(2008/1/1 GMT), year)') ) );
$this->assert_equals( '38',
int( $this->CALC('$TIMEDIFF($TIME(1970/1/1 GMT), $TIME(2008/1/1 GMT), year)') ) );
$this->assert_equals( '58',
int( $this->CALC('$TIMEDIFF($TIME(1950/1/1 GMT), $TIME(2008/1/1 GMT), year)') ) );
}

sub test_TODAY {
Expand Down

0 comments on commit ccb0cad

Please sign in to comment.