Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the DateTime CPAN distribution.

Report information
The Basics
Id: 67631
Status: resolved
Priority: 0/
Queue: DateTime

People
Owner: Nobody in particular
Requestors: chansen [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: (no value)
Fixed in: 0.67



Subject: Fix for %U and %W strftime patterns + some minor optimizations
The attached file contains several patches, first patch addresses a bug in %U and %W, the rest is optimizations. %U formula is off by one in years where January 1st is a Sunday, such as 2006, 2012 .. 2006-02-01 Expected: 05 DT: 04 2012-02-01 Expected: 05 DT: 04 2017-02-01 Expected: 05 DT: 04 %W formula is off by one in years where January 1st is either a Sunday or Monday 2006, 2007, 2012 .. 2006-02-01 Expected: 05 DT: 04 2007-02-01 Expected: 05 DT: 04 2012-02-01 Expected: 05 DT: 04 -- chansen
Subject: DT.pm.patch
# HG changeset patch # User chansen <chansen@cpan.org> # Date 1303203118 -7200 # Branch chansen # Node ID 985444736637b5f9c68909ddc8ab71e28a5e268d # Parent e877dc806b3e75ac04bc09115d6b4172e3f9e5cd Fix %U and %W strftime patterns, off by one in certain years diff -r e877dc806b3e -r 985444736637 lib/DateTime.pm --- a/lib/DateTime.pm Fri Nov 26 15:39:59 2010 -0600 +++ b/lib/DateTime.pm Tue Apr 19 10:51:58 2011 +0200 @@ -1013,13 +1013,9 @@ 't' => sub {"\t"}, 'T' => sub { $_[0]->strftime('%H:%M:%S') }, 'u' => sub { $_[0]->day_of_week }, - - # algorithm from Date::Format::wkyr 'U' => sub { - my $dow = $_[0]->day_of_week; - $dow = 0 if $dow == 7; # convert to 0-6, Sun-Sat - my $doy = $_[0]->day_of_year - 1; - return sprintf( '%02d', int( ( $doy - $dow + 13 ) / 7 - 1 ) ); + my $sun = $_[0]->day_of_year - ($_[0]->day_of_week + 7) % 7; + return sprintf( '%02d', int( ( $sun + 6 ) / 7) ); }, 'V' => sub { sprintf( '%02d', $_[0]->week_number ) }, 'w' => sub { @@ -1027,9 +1023,8 @@ return $dow % 7; }, 'W' => sub { - my $dow = $_[0]->day_of_week; - my $doy = $_[0]->day_of_year - 1; - return sprintf( '%02d', int( ( $doy - $dow + 13 ) / 7 - 1 ) ); + my $mon = $_[0]->day_of_year - ($_[0]->day_of_week + 6) % 7; + return sprintf( '%02d', int( ( $mon + 6 ) / 7 ) ); }, 'x' => sub { $_[0]->format_cldr( $_[0]->{locale}->date_format_default() ); diff -r e877dc806b3e -r 985444736637 t/13strftime.t --- a/t/13strftime.t Fri Nov 26 15:39:59 2010 -0600 +++ b/t/13strftime.t Tue Apr 19 10:51:58 2011 +0200 @@ -265,3 +265,10 @@ %z => '+0000' %{month} => '9' %{year} => '1999' +year => 2012, month => 1, day => 1 +%U => '01' +%W => '00' +year => 2012, month => 1, day => 10 +%U => '02' +%W => '02' + # HG changeset patch # User chansen <chansen@cpan.org> # Date 1303205242 -7200 # Branch chansen # Node ID 15377a87417d24a447ad0b55d0d736caf700d1c3 # Parent 985444736637b5f9c68909ddc8ab71e28a5e268d optimized formula in ->week_of_month diff -r 985444736637 -r 15377a87417d lib/DateTime.pm --- a/lib/DateTime.pm Tue Apr 19 10:51:58 2011 +0200 +++ b/lib/DateTime.pm Tue Apr 19 11:27:22 2011 +0200 @@ -910,18 +910,10 @@ # ISO says that the first week of a year is the first week containing # a Thursday. Extending that says that the first week of the month is # the first week containing a Thursday. ICU agrees. -# -# Algorithm supplied by Rick Measham, who doesn't understand how it -# works. Neither do I. Please feel free to explain this to me! sub week_of_month { my $self = shift; - - # Faster than cloning just to get the dow - my $first_wday_of_month = ( 8 - ( $self->day - $self->dow ) % 7 ) % 7; - $first_wday_of_month = 7 unless $first_wday_of_month; - - my $wom = int( ( $self->day + $first_wday_of_month - 2 ) / 7 ); - return ( $first_wday_of_month <= 4 ) ? $wom + 1 : $wom; + my $thu = $self->day + 4 - $self->day_of_week; + return int( ( $thu + 6 ) / 7 ); } sub time_zone { # HG changeset patch # User chansen <chansen@cpan.org> # Date 1303205393 -7200 # Branch chansen # Node ID 2087d6604b1c946c7af1b96c62aedca93c27833b # Parent 15377a87417d24a447ad0b55d0d736caf700d1c3 minor optimization in ->new, only invoke ->_month_length if day is greater than 28 diff -r 15377a87417d -r 2087d6604b1c lib/DateTime.pm --- a/lib/DateTime.pm Tue Apr 19 11:27:22 2011 +0200 +++ b/lib/DateTime.pm Tue Apr 19 11:29:53 2011 +0200 @@ -197,7 +197,7 @@ Carp::croak( "Invalid day of month (day = $p{day} - month = $p{month} - year = $p{year})\n" - ) if $p{day} > $class->_month_length( $p{year}, $p{month} ); + ) if $p{day} > 28 && $p{day} > $class->_month_length( $p{year}, $p{month} ); return $class->_new(%p); } # HG changeset patch # User chansen <chansen@cpan.org> # Date 1303206107 -7200 # Branch chansen # Node ID 51a5753ea680caf53117c8381e9e25dd5c31c414 # Parent 2087d6604b1c946c7af1b96c62aedca93c27833b optimized ->from_day_of_year, only invoke ->_month_length once per iteration diff -r 2087d6604b1c -r 51a5753ea680 lib/DateTime.pm --- a/lib/DateTime.pm Tue Apr 19 11:29:53 2011 +0200 +++ b/lib/DateTime.pm Tue Apr 19 11:41:47 2011 +0200 @@ -617,18 +617,19 @@ my $class = shift; my %p = validate( @_, $FromDayOfYearValidate ); - my $is_leap_year = $class->_is_leap_year( $p{year} ); - Carp::croak("$p{year} is not a leap year.\n") - if $p{day_of_year} == 366 && !$is_leap_year; + if $p{day_of_year} == 366 && !$class->_is_leap_year( $p{year} ); my $month = 1; my $day = delete $p{day_of_year}; - while ( $month <= 12 && $day > $class->_month_length( $p{year}, $month ) ) - { - $day -= $class->_month_length( $p{year}, $month ); - $month++; + if ($day > 31) { + my $dim = $class->_month_length( $p{year}, $month ); + while ($day > $dim) { + $day -= $dim; + $month++; + $dim = $class->_month_length( $p{year}, $month ); + } } return $class->_new( # HG changeset patch # User chansen <chansen@cpan.org> # Date 1303208862 -7200 # Branch chansen # Node ID b9bf1e80fd07d3cff21049f22a8309221af1c609 # Parent 51a5753ea680caf53117c8381e9e25dd5c31c414 optimize ->_weeks_in_year diff -r 51a5753ea680 -r b9bf1e80fd07 lib/DateTime.pm --- a/lib/DateTime.pm Tue Apr 19 11:41:47 2011 +0200 +++ b/lib/DateTime.pm Tue Apr 19 12:27:42 2011 +0200 @@ -894,15 +894,15 @@ return @{ $self->{local_c} }{ 'week_year', 'week_number' }; } -# Also from DateCalc.c sub _weeks_in_year { my $self = shift; my $year = shift; - my $jan_one_dow = ( ( $self->_ymd2rd( $year, 1, 1 ) + 6 ) % 7 ) + 1; - my $dec_31_dow = ( ( $self->_ymd2rd( $year, 12, 31 ) + 6 ) % 7 ) + 1; - - return $jan_one_dow == 4 || $dec_31_dow == 4 ? 53 : 52; + my $dow = $self->_ymd2rd( $year, 1, 1 ) % 7; # Sun=0 + + # All years starting with Thursday, and leap years starting with Wednesday + # has 53 weeks. + return ($dow == 4 || ($dow == 3 && $self->_is_leap_year($year))) ? 53 : 52; } sub week_year { ( $_[0]->week )[0] }