derick Tue, 04 May 2010 15:11:41 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=298973
Log: - Fixed bug #49081 (DateTime::diff() mistake if start in January and interval > 28 days). (Derick) Bug: http://bugs.php.net/49081 (Assigned) [PATCH] DateTime::diff() mistake if start in January and interval > 28 days Changed paths: U php/php-src/branches/PHP_5_3/NEWS U php/php-src/branches/PHP_5_3/ext/date/lib/interval.c U php/php-src/branches/PHP_5_3/ext/date/lib/tm2unixtime.c A php/php-src/branches/PHP_5_3/ext/date/tests/bug49081.phpt A php/php-src/branches/PHP_5_3/ext/date/tests/date_diff.phpt U php/php-src/trunk/ext/date/lib/interval.c U php/php-src/trunk/ext/date/lib/tm2unixtime.c A php/php-src/trunk/ext/date/tests/bug49081.phpt A php/php-src/trunk/ext/date/tests/date_diff.phpt
Modified: php/php-src/branches/PHP_5_3/NEWS =================================================================== --- php/php-src/branches/PHP_5_3/NEWS 2010-05-04 14:35:48 UTC (rev 298972) +++ php/php-src/branches/PHP_5_3/NEWS 2010-05-04 15:11:41 UTC (rev 298973) @@ -129,6 +129,8 @@ - Fixed bug #49429 (odbc_autocommit doesn't work). (Felipe) - Fixed bug #49234 (mysqli_ssl_set not found). (Andrey) - Fixed bug #49192 (PHP crashes when GC invoked on COM object). (Stas) +- Fixed bug #49081 (DateTime::diff() mistake if start in January and interval > + 28 days). (Derick) - Fixed bug #49059 (DateTime::diff() repeats previous sub() operation). (yoa...@gmail.com, Derick) - Fixed bug #48983 (DomDocument : saveHTMLFile wrong charset). (Rob) Modified: php/php-src/branches/PHP_5_3/ext/date/lib/interval.c =================================================================== --- php/php-src/branches/PHP_5_3/ext/date/lib/interval.c 2010-05-04 14:35:48 UTC (rev 298972) +++ php/php-src/branches/PHP_5_3/ext/date/lib/interval.c 2010-05-04 15:11:41 UTC (rev 298973) @@ -56,7 +56,7 @@ rt->s = two->s - one->s; rt->days = abs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400)); - timelib_do_rel_normalize(one, rt); + timelib_do_rel_normalize(rt->invert ? one : two, rt); timelib_apply_localtime(one, 1); timelib_apply_localtime(two, 1); Modified: php/php-src/branches/PHP_5_3/ext/date/lib/tm2unixtime.c =================================================================== --- php/php-src/branches/PHP_5_3/ext/date/lib/tm2unixtime.c 2010-05-04 14:35:48 UTC (rev 298972) +++ php/php-src/branches/PHP_5_3/ext/date/lib/tm2unixtime.c 2010-05-04 15:11:41 UTC (rev 298973) @@ -41,39 +41,64 @@ return 0; } -static int do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d) +static void inc_month(timelib_sll *y, timelib_sll *m) { + (*m)++; + if (*m > 12) { + *m -= 12; + (*y)++; + } +} + +static void dec_month(timelib_sll *y, timelib_sll *m) +{ + (*m)--; + if (*m < 1) { + *m += 12; + (*y)--; + } +} + +static void do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d, timelib_sll invert) +{ timelib_sll leapyear; - timelib_sll days_this_month; - timelib_sll next_month, next_year; - timelib_sll days_next_month; + timelib_sll month, year; + timelib_sll days; do_range_limit(1, 13, 12, base_m, base_y); - leapyear = timelib_is_leap(*base_y); - days_this_month = leapyear ? days_in_month_leap[*base_m] : days_in_month[*base_m]; - next_month = (*base_m) + 1; + year = *base_y; + month = *base_m; - if (next_month > 12) { - next_month -= 12; - next_year = (*base_y) + 1; +/* + printf( "S: Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); +*/ + if (!invert) { + while (*d < 0) { + dec_month(&year, &month); + leapyear = timelib_is_leap(year); + days = leapyear ? days_in_month_leap[month] : days_in_month[month]; + + /* printf( "I Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); */ + + *d += days; + (*m)--; + } } else { - next_year = (*base_y); - } - leapyear = timelib_is_leap(next_year); - days_next_month = leapyear ? days_in_month_leap[next_month] : days_in_month[next_month]; + while (*d < 0) { + leapyear = timelib_is_leap(year); + days = leapyear ? days_in_month_leap[month] : days_in_month[month]; - if (*d < 0) { - *d += days_this_month; - (*m)--; - return 1; + /* printf( "I Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); */ + + *d += days; + (*m)--; + inc_month(&year, &month); + } } - if (*d > days_next_month) { - *d -= days_next_month; - (*m)++; - return 1; - } - return 0; + /* + printf( "E: Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); + */ } static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d) @@ -150,7 +175,7 @@ do {} while (do_range_limit(0, 24, 24, &rt->h, &rt->d)); do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); - do {} while (do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d)); + do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d, rt->invert); do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); } Added: php/php-src/branches/PHP_5_3/ext/date/tests/bug49081.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/date/tests/bug49081.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/date/tests/bug49081.phpt 2010-05-04 15:11:41 UTC (rev 298973) @@ -0,0 +1,22 @@ +--TEST-- +Bug #49081 (DateTime::diff() mistake if start in January and interval > 28 days) +--FILE-- +<?php + date_default_timezone_set('Europe/Berlin'); + $d1 = new DateTime('2010-01-01 06:00:00'); + $d2 = new DateTime('2010-01-31 10:00:00'); + $d = $d1->diff($d2); + print_r($d); +?> +--EXPECT-- +DateInterval Object +( + [y] => 0 + [m] => 0 + [d] => 30 + [h] => 4 + [i] => 0 + [s] => 0 + [invert] => 0 + [days] => 30 +) Added: php/php-src/branches/PHP_5_3/ext/date/tests/date_diff.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/date/tests/date_diff.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/date/tests/date_diff.phpt 2010-05-04 15:11:41 UTC (rev 298973) @@ -0,0 +1,57 @@ +--TEST-- +Extensive test for date_diff(). +--FILE-- +<?php +$ok = 0; +define( 'COUNT', 120 ); +$d0 = new DateTime('2009-11-20'); +for ( $i = 0; $i < COUNT * 12; $i++ ) +{ + $d = clone $d0; + $dates[$i] = $d->add( new DateInterval( "P{$i}D" ) ); +} + +for ( $i = 0; $i < COUNT; $i++) +{ +// echo $dates[$i]->format( "Y-m-d\n" ); + for ( $j = 0; $j < COUNT * 12; $j++) + { + $diff = date_diff( $dates[$i], $dates[$j] ); + /* + printf( "\t%s %s %3d %s\n", + $dates[$i]->format( 'Y-m-d' ), + $dates[$j]->format( 'Y-m-d' ), + $diff->format( '%a' ), + $diff->format( '%y-%m-%d' ) + ); + */ + + $current = clone $dates[$i]; + $int = new DateInterval( $diff->format( 'P%yY%mM%dD' ) ); + if ( $current > $dates[$j] ) + { + $current->sub( $int ); + } + else + { + $current->add( $int ); + } + if ( $current != $dates[$j] ) + { + echo "FAIL: ", + $dates[$i]->format( 'Y-m-d' ), " + ", + $int->format( '%y-%m-%d' ), " = ", + $current->format( 'Y-m-d' ), " (", + $dates[$j]->format( 'Y-m-d' ), ")\n"; + } + else + { + $ok++; + } + } +} + +echo $ok, "\n"; +?> +--EXPECT-- +172800 Modified: php/php-src/trunk/ext/date/lib/interval.c =================================================================== --- php/php-src/trunk/ext/date/lib/interval.c 2010-05-04 14:35:48 UTC (rev 298972) +++ php/php-src/trunk/ext/date/lib/interval.c 2010-05-04 15:11:41 UTC (rev 298973) @@ -56,7 +56,7 @@ rt->s = two->s - one->s; rt->days = abs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400)); - timelib_do_rel_normalize(one, rt); + timelib_do_rel_normalize(rt->invert ? one : two, rt); timelib_apply_localtime(one, 1); timelib_apply_localtime(two, 1); Modified: php/php-src/trunk/ext/date/lib/tm2unixtime.c =================================================================== --- php/php-src/trunk/ext/date/lib/tm2unixtime.c 2010-05-04 14:35:48 UTC (rev 298972) +++ php/php-src/trunk/ext/date/lib/tm2unixtime.c 2010-05-04 15:11:41 UTC (rev 298973) @@ -41,39 +41,64 @@ return 0; } -static int do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d) +static void inc_month(timelib_sll *y, timelib_sll *m) { + (*m)++; + if (*m > 12) { + *m -= 12; + (*y)++; + } +} + +static void dec_month(timelib_sll *y, timelib_sll *m) +{ + (*m)--; + if (*m < 1) { + *m += 12; + (*y)--; + } +} + +static void do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d, timelib_sll invert) +{ timelib_sll leapyear; - timelib_sll days_this_month; - timelib_sll next_month, next_year; - timelib_sll days_next_month; + timelib_sll month, year; + timelib_sll days; do_range_limit(1, 13, 12, base_m, base_y); - leapyear = timelib_is_leap(*base_y); - days_this_month = leapyear ? days_in_month_leap[*base_m] : days_in_month[*base_m]; - next_month = (*base_m) + 1; + year = *base_y; + month = *base_m; - if (next_month > 12) { - next_month -= 12; - next_year = (*base_y) + 1; +/* + printf( "S: Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); +*/ + if (!invert) { + while (*d < 0) { + dec_month(&year, &month); + leapyear = timelib_is_leap(year); + days = leapyear ? days_in_month_leap[month] : days_in_month[month]; + + /* printf( "I Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); */ + + *d += days; + (*m)--; + } } else { - next_year = (*base_y); - } - leapyear = timelib_is_leap(next_year); - days_next_month = leapyear ? days_in_month_leap[next_month] : days_in_month[next_month]; + while (*d < 0) { + leapyear = timelib_is_leap(year); + days = leapyear ? days_in_month_leap[month] : days_in_month[month]; - if (*d < 0) { - *d += days_this_month; - (*m)--; - return 1; + /* printf( "I Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); */ + + *d += days; + (*m)--; + inc_month(&year, &month); + } } - if (*d > days_next_month) { - *d -= days_next_month; - (*m)++; - return 1; - } - return 0; + /* + printf( "E: Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); + */ } static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d) @@ -150,7 +175,7 @@ do {} while (do_range_limit(0, 24, 24, &rt->h, &rt->d)); do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); - do {} while (do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d)); + do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d, rt->invert); do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y)); } Added: php/php-src/trunk/ext/date/tests/bug49081.phpt =================================================================== --- php/php-src/trunk/ext/date/tests/bug49081.phpt (rev 0) +++ php/php-src/trunk/ext/date/tests/bug49081.phpt 2010-05-04 15:11:41 UTC (rev 298973) @@ -0,0 +1,22 @@ +--TEST-- +Bug #49081 (DateTime::diff() mistake if start in January and interval > 28 days) +--FILE-- +<?php + date_default_timezone_set('Europe/Berlin'); + $d1 = new DateTime('2010-01-01 06:00:00'); + $d2 = new DateTime('2010-01-31 10:00:00'); + $d = $d1->diff($d2); + print_r($d); +?> +--EXPECT-- +DateInterval Object +( + [y] => 0 + [m] => 0 + [d] => 30 + [h] => 4 + [i] => 0 + [s] => 0 + [invert] => 0 + [days] => 30 +) Added: php/php-src/trunk/ext/date/tests/date_diff.phpt =================================================================== --- php/php-src/trunk/ext/date/tests/date_diff.phpt (rev 0) +++ php/php-src/trunk/ext/date/tests/date_diff.phpt 2010-05-04 15:11:41 UTC (rev 298973) @@ -0,0 +1,57 @@ +--TEST-- +Extensive test for date_diff(). +--FILE-- +<?php +$ok = 0; +define( 'COUNT', 120 ); +$d0 = new DateTime('2009-11-20'); +for ( $i = 0; $i < COUNT * 12; $i++ ) +{ + $d = clone $d0; + $dates[$i] = $d->add( new DateInterval( "P{$i}D" ) ); +} + +for ( $i = 0; $i < COUNT; $i++) +{ +// echo $dates[$i]->format( "Y-m-d\n" ); + for ( $j = 0; $j < COUNT * 12; $j++) + { + $diff = date_diff( $dates[$i], $dates[$j] ); + /* + printf( "\t%s %s %3d %s\n", + $dates[$i]->format( 'Y-m-d' ), + $dates[$j]->format( 'Y-m-d' ), + $diff->format( '%a' ), + $diff->format( '%y-%m-%d' ) + ); + */ + + $current = clone $dates[$i]; + $int = new DateInterval( $diff->format( 'P%yY%mM%dD' ) ); + if ( $current > $dates[$j] ) + { + $current->sub( $int ); + } + else + { + $current->add( $int ); + } + if ( $current != $dates[$j] ) + { + echo "FAIL: ", + $dates[$i]->format( 'Y-m-d' ), " + ", + $int->format( '%y-%m-%d' ), " = ", + $current->format( 'Y-m-d' ), " (", + $dates[$j]->format( 'Y-m-d' ), ")\n"; + } + else + { + $ok++; + } + } +} + +echo $ok, "\n"; +?> +--EXPECT-- +172800
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php