Skip to content

Commit

Permalink
Item6018: ISO8601 week number fixed
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.foswiki.org/trunk@2655 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
CrawfordCurrie authored and CrawfordCurrie committed Feb 24, 2009
1 parent 1994064 commit 9b135fb
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 16 deletions.
32 changes: 32 additions & 0 deletions UnitTestContrib/test/unit/TimeTests.pm
Expand Up @@ -219,4 +219,36 @@ sub test_parseErrors {
$this->assert_equals(0, Foswiki::Time::parseTime('2008-10-32'));
}

sub test_week {
my $this = shift;
# 2008 started on a tuesday...
my $time = Time::Local::timegm(0, 0, 0, 1, 0, 108);
my $week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(1, $week);
# 3rd was the saturday of first week
$time = Time::Local::timegm(0, 0, 0, 3, 0, 108);
$week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(1, $week);
# 7th was monday of second week
$time = Time::Local::timegm(0, 0, 0, 7, 0, 108);
$week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(2, $week);
# poke back into 2007; 30th was in week 52
$time = Time::Local::timegm(0, 0, 0, 30, 11, 107);
$week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(52, $week);
# and 31st in week 53 (2007 started on a monday)
$time = Time::Local::timegm(0, 0, 0, 31, 11, 107);
$week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(53, $week);
# 1999 started on a friday, so 4th is week one
$time = Time::Local::timegm(0, 0, 0, 4, 0, 99);
$week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(1, $week);
# And 3rd is week 0
$time = Time::Local::timegm(0, 0, 0, 3, 0, 99);
$week = Foswiki::Time::formatTime($time, '$week', 'gmtime');
$this->assert_equals(0, $week);
}

1;
58 changes: 42 additions & 16 deletions core/lib/Foswiki/Time.pm
Expand Up @@ -216,14 +216,15 @@ sub parseTime {
* =$epochSeconds= epochSecs GMT
* =$formatString= twiki time date format, default =$day $month $year - $hour:$min=
* =$outputTimeZone= timezone to display, =gmtime= or =servertime=, default is whatever is set in $Foswiki::cfg{DisplayTimeValues}
=$formatString= supports:
| $seconds | secs |
| $minutes | mins |
| $hours | hours |
| $day | date |
| $wday | weekday name |
| $dow | day number (0 = Sunday) |
| $week | week number |
| $week | week number (ISO 8601) |
| $month | month name |
| $mo | month number |
| $year | 4-digit year |
Expand Down Expand Up @@ -251,13 +252,13 @@ sub formatTime {
$outputTimeZone = 'gmtime';
}

my ( $sec, $min, $hour, $day, $mon, $year, $wday );
my ( $sec, $min, $hour, $day, $mon, $year, $wday, $yday );
if ( $outputTimeZone eq 'servertime' ) {
( $sec, $min, $hour, $day, $mon, $year, $wday ) =
( $sec, $min, $hour, $day, $mon, $year, $wday, $yday ) =
localtime($epochSeconds);
}
else {
( $sec, $min, $hour, $day, $mon, $year, $wday ) =
( $sec, $min, $hour, $day, $mon, $year, $wday, $yday ) =
gmtime($epochSeconds);
}

Expand Down Expand Up @@ -287,10 +288,10 @@ sub formatTime {
$value =~ s/\$day/sprintf('%.2u',$day)/gei;
$value =~ s/\$wday/$WEEKDAY[$wday]/gi;
$value =~ s/\$dow/$wday/gi;
$value =~ s/\$week/_weekNumber($day,$mon,$year,$wday)/egi;
$value =~ s/\$week/_weekNumber($wday, $yday, $year + 1900)/egi;
$value =~ s/\$mont?h?/$ISOMONTH[$mon]/gi;
$value =~ s/\$mo/sprintf('%.2u',$mon+1)/gei;
$value =~ s/\$year?/sprintf('%.4u',$year+1900)/gei;
$value =~ s/\$year?/sprintf('%.4u',$year + 1900)/gei;
$value =~ s/\$ye/sprintf('%.2u',$year%100)/gei;
$value =~ s/\$epoch/$epochSeconds/gi;

Expand Down Expand Up @@ -371,19 +372,44 @@ sub _tzOffset {
return $off;
}

# Returns the ISO8601 week number for a date.
# Year is the real year
# Day of week is 0..6 where 0==Monday
# Day of year is 0..364 (or 365) where 0==Jan1
# From http://www.perlmonks.org/?node_id=710571
sub _weekNumber {
my ( $day, $mon, $year, $wday ) = @_;
my( $dayOfWeek, $dayOfYear, $year ) = @_;

# Locate the nearest Thursday
# (Done by locating the Monday at or before and going forwards 3 days)
my $dayOfNearestThurs = $dayOfYear - $dayOfWeek + 3;

# Is nearest thursday in last year or next year?
if ($dayOfNearestThurs < 0) {
# Nearest Thurs is last year
# We are at the start of the year
# Adjust by the number of days in LAST year
$dayOfNearestThurs += daysInYear($year - 1);
}
my $daysInThisYear = daysInYear($year);
if ($dayOfNearestThurs > $daysInThisYear) {
# Nearest Thurs is next year
# We are at the end of the year
# Adjust by the number of days in THIS year
$dayOfNearestThurs -= $daysInThisYear;
}

require Time::Local;
# Which week does the Thurs fall into?
return int ($dayOfNearestThurs / 7) + 1;
}

# calculate the calendar week (ISO 8601)
my $nextThursday =
Time::Local::timegm( 0, 0, 0, $day, $mon, $year ) +
( 3 - ( $wday + 6 ) % 7 ) * 24 * 60 * 60; # nearest thursday
my $firstFourth =
Time::Local::timegm( 0, 0, 0, 4, 0, $year ); # january, 4th
return
sprintf( '%.0f', ( $nextThursday - $firstFourth ) / ( 7 * 86400 ) ) + 1;
# Returns the number of...
sub _daysInYear {
my $year = shift + 1900;
return 366 unless $_[0] % 400;
return 365 unless $_[0] % 100;
return 366 unless $_[0] % 4;
return 365;
}

=begin TML
Expand Down

0 comments on commit 9b135fb

Please sign in to comment.