Skip to content

Commit

Permalink
Item753: time parsing doesn't actually conform to System.TimeSpecific…
Browse files Browse the repository at this point in the history
…ations

git-svn-id: http://svn.foswiki.org/trunk@1974 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
SvenDowideit authored and SvenDowideit committed Jan 14, 2009
1 parent 0adb2a3 commit 9c1e095
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 24 deletions.
61 changes: 61 additions & 0 deletions UnitTestContrib/test/unit/Fn_IF.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1370,4 +1370,65 @@ sub test_ISEMPTY_EXPR_UNDEF {
$this->simpleTest( test => "isempty undef", then => 1, else => 0 );
}

sub test_d2n {
my $this = shift;

#TRUE cases
$this->simpleTest(test => "d2n('2007-03-26') = d2n('2007-03-26')", then => 1, else => 0);

$this->simpleTest(test => "d2n('2007-03-27') != d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-04-26') != d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2008-03-26') != d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') != d2n('2007-03-27')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') != d2n('2007-04-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') != d2n('2008-03-26')", then => 1, else => 0);

$this->simpleTest(test => "d2n('2007-03-27') > d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-04-26') > d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2008-03-26') > d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') >= d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-27') >= d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-04-26') >= d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2008-03-26') >= d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') < d2n('2007-03-27')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') < d2n('2007-04-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') < d2n('2008-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') <= d2n('2007-03-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') <= d2n('2007-03-27')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') <= d2n('2007-04-26')", then => 1, else => 0);
$this->simpleTest(test => "d2n('2007-03-26') <= d2n('2008-03-26')", then => 1, else => 0);

#FALSE cases
$this->simpleTest(test => "d2n('2007-03-26') != d2n('2007-03-26')", then => 0, else => 1);

$this->simpleTest(test => "d2n('2007-03-27') = d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-04-26') = d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2008-03-26') = d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') = d2n('2007-03-27')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') = d2n('2007-04-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') = d2n('2008-03-26')", then => 0, else => 1);

$this->simpleTest(test => "d2n('2007-03-27') < d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-04-26') < d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2008-03-26') < d2n('2007-03-26')", then => 0, else => 1);
# $this->simpleTest(test => "d2n('2007-03-26') <= d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-27') <= d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-04-26') <= d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2008-03-26') <= d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') > d2n('2007-03-27')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') > d2n('2007-04-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') > d2n('2008-03-26')", then => 0, else => 1);
# $this->simpleTest(test => "d2n('2007-03-26') >= d2n('2007-03-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') >= d2n('2007-03-27')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') >= d2n('2007-04-26')", then => 0, else => 1);
$this->simpleTest(test => "d2n('2007-03-26') >= d2n('2008-03-26')", then => 0, else => 1);

#try to examine the root of my looking into this - d2n('13 Oct 2008') >= d2n('2008/12/1')
$this->simpleTest(test => "d2n('2008/10/13') >= d2n('2008/12/1')", then => 0, else => 1);
$this->simpleTest(test => "d2n('13 Oct 2008') >= d2n('2008/12/1')", then => 0, else => 1);


}


1;
44 changes: 44 additions & 0 deletions UnitTestContrib/test/unit/TimeTests.pm
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,48 @@ sub test_checkInterval {
Foswiki::Time::formatTime($e, 'iso'));
}

sub test_parseTimeRobustness {
my $this = shift;

$this->checkTime(0, 0, 0, 4, 2, 1995, "1995-02-04");
$this->checkTime(0, 0, 0, 1, 2, 1995, "1995-02");
$this->checkTime(0, 0, 0, 1, 1, 1995, "1995");

$this->checkTime(0, 0, 0, 4, 2, 1995, "1995/02/04");
$this->checkTime(0, 0, 0, 1, 2, 1995, "1995/02");
$this->checkTime(0, 0, 0, 1, 1, 1995, "1995");

$this->checkTime(0, 0, 0, 4, 2, 1995, "1995.02.04");
$this->checkTime(0, 0, 0, 1, 2, 1995, "1995.02");
$this->checkTime(0, 0, 0, 1, 1, 1995, "1995");

$this->checkTime(0, 0, 0, 4, 2, 1995, "1995 - 02 -04");
$this->checkTime(0, 0, 0, 1, 2, 1995, "1995- 02");
$this->checkTime(0, 0, 0, 1, 1, 1995, "1995");

$this->checkTime(0, 0, 0, 4, 2, 1995, "1995 / 02/04");
$this->checkTime(0, 0, 0, 1, 2, 1995, "1995 /02");
$this->checkTime(0, 0, 0, 1, 1, 1995, "1995");

$this->checkTime(0, 0, 0, 4, 2, 1995, "1995. 02 .04");
$this->checkTime(0, 0, 0, 1, 2, 1995, "1995.02 ");

$this->checkTime(0, 0, 0, 4, 2, 1995, " 1995-02-04");
$this->checkTime(0, 0, 0, 1, 1, 1995, " 1995 ");

}

sub test_parseErrors {
my $this = shift;

$this->assert_equals(0, Foswiki::Time::parseTime('wibble'));
$this->assert_equals(0, Foswiki::Time::parseTime('1234-qwer-3'));
$this->assert_equals(0, Foswiki::Time::parseTime('1234-1234-1234'));
$this->assert_equals(0, Foswiki::Time::parseTime('2008^12^12'));
$this->assert_equals(0, Foswiki::Time::parseTime('2008--12-23'));

$this->assert_equals(0, Foswiki::Time::parseTime('2008-13-23'));
$this->assert_equals(0, Foswiki::Time::parseTime('2008-10-32'));
}

1;
89 changes: 65 additions & 24 deletions core/lib/Foswiki/Time.pm
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,14 @@ Date and time separated by ' ', '.' and/or '-'
* 2001.12.31.23.59
* 2001-12-31 23:59
* 2001-12-31 - 23:59
* 2009-1-12
* 2009-1
* 2009
ISO format
* 2001-12-31T23:59:59
* 2001-12-31T
ISO dates may have a timezone specifier, either Z or a signed difference
in hh:mm format. For example:
* 2001-12-31T23:59:59+01:00
Expand All @@ -106,6 +111,9 @@ If the date format was not recognised, will return 0.
sub parseTime {
my ( $date, $defaultLocal ) = @_;

$date =~ s/^\s*//; #remove leading spaces without de-tainting.
$date =~ s/\s*$//;

require Time::Local;

# NOTE: This routine *will break* if input is not one of below formats!
Expand All @@ -120,35 +128,21 @@ sub parseTime {

# try "31 Dec 2001 - 23:59" (Foswiki date)
# or "31 Dec 2001"
#TODO: allow /.: too
if ( $date =~ /(\d+)\s+([a-z]{3})\s+(\d+)(?:[-\s]+(\d+):(\d+))?/i ) {
my $year = $3;
$year -= 1900 if ( $year > 1900 );
#TODO: %MON2NUM needs to be updated to use i8n
#TODO: and should really work for long form of the month name too.
return Time::Local::timegm( 0, $5 || 0, $4 || 0, $1, $MON2NUM{ lc($2) },
$year ) - $tzadj;
}

# try "2001/12/31 23:59:59" or "2001.12.31.23.59.59" (RCS date)
# or "2001-12-31 23:59:59" or "2001-12-31 - 23:59:59"
if (
$date =~ m!(\d+)[./\-](\d+)[./\-](\d+)[.\s\-]+(\d+)[.:](\d+)[.:](\d+)! )
{
my $year = $1;
$year -= 1900 if ( $year > 1900 );
return Time::Local::timegm( $6, $5, $4, $3, $2 - 1, $year ) - $tzadj;
}

# try "2001/12/31 23:59" or "2001.12.31.23.59" (RCS short date)
# or "2001-12-31 23:59" or "2001-12-31 - 23:59"
if ( $date =~ m!(\d+)[./\-](\d+)[./\-](\d+)[.\s\-]+(\d+)[.:](\d+)! ) {
my $year = $1;
$year -= 1900 if ( $year > 1900 );
return Time::Local::timegm( 0, $5, $4, $3, $2 - 1, $year ) - $tzadj;
}

# ISO date
if ( $date =~
# ISO date 2001-12-31T23:59:59+01:00
# Sven is going to presume that _all_ ISO dated must have a 'T' in them.
if (($date =~ /T/) && ( $date =~
/(\d\d\d\d)(?:-(\d\d)(?:-(\d\d))?)?(?:T(\d\d)(?::(\d\d)(?::(\d\d(?:\.\d+)?))?)?)?(Z|[-+]\d\d(?::\d\d)?)?/
)
) )
{
my ( $Y, $M, $D, $h, $m, $s, $tz ) =
( $1, $2 || 1, $3 || 1, $4 || 0, $5 || 0, $6 || 0, $7 || '' );
Expand All @@ -164,6 +158,53 @@ sub parseTime {
return Time::Local::timegm( $s, $m, $h, $D, $M, $Y ) - $tzadj;
}

#any date that leads with a year (2 digit years too)
if ($date =~ m|^
(\d\d+) #year
(?:\s*[/\s.-]\s* #datesep
(\d\d?) #month
(?:\s*[/\s.-]\s* #datesep
(\d\d?) #day
(?:\s*[/\s.-]\s* #datetimesep
(\d\d?) #hour
(?:\s*[:.]\s* #timesep
(\d\d?) #min
(?:\s*[:.]\s* #timesep
(\d\d?)
)?
)?
)?
)?
)?
$|x) {
#no defaulting yet so we can detect the 2009--12 error
my ( $year, $M, $D, $h, $m, $s ) =
( $1, $2 , $3, $4, $5, $6 );

#without range checking on the 12 Jan 2009 case above, there is abmiguity - what is 14 Jan 12 ?
#similarly, how would you decide what Jan 02 and 02 Jan are?
#$month_p = $MON2NUM{ lc($month_p) } if (defined($MON2NUM{ lc($month_p) }));

#range checks
return 0 if (defined($M) && ($M < 1 || $M > 12));
my $month = ($M || 1)-1;
return 0 if (defined($D) && ($D < 0 || $D > $MONTHLENS[$month]));
return 0 if (defined($h) && ($h < 0 || $h > 24));
return 0 if (defined($m) && ($m < 0 || $m > 60));
return 0 if (defined($s) && ($s < 0 || $s > 60));

my $day = $D || 1;
my $hour = $h || 0;
my $min = $m || 0;
my $sec = $s || 0;

#TODO: unhappily, this means 09 == 1909 not 2009
$year -= 1900 if ( $year > 1900 );

return Time::Local::timegm( $sec, $min, $hour, $day, $month, $year ) - $tzadj;
}

#TODO: returning 0 makes it very hard to detect parse errors :(
# give up, return start of epoch (01 Jan 1970 GMT)
return 0;
}
Expand Down Expand Up @@ -465,9 +506,9 @@ __DATA__
# file as follows:
#
# Copyright (C) 2002 John Talintyre, john.talintyre@btinternet.com
# Copyright (C) 2002-2007 Peter Thoeny, peter@thoeny.org
# and TWiki Contributors. All Rights Reserved. TWiki Contributors
# are listed in the AUTHORS file in the root of this distribution.
# Copyright (C) 2002-2007 TWiki Contributors. All Rights Reserved.
# TWiki Contributors are listed in the AUTHORS file in the root of
# this distribution.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
Expand Down

0 comments on commit 9c1e095

Please sign in to comment.