From 7cf3911cb8b26f90cca39e955d201fb88279ae34 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 4 May 2010 15:11:41 +0000 Subject: [PATCH] - Fixed bug #49081 (DateTime::diff() mistake if start in January and interval > 28 days). (Derick) --- NEWS | 2 + ext/date/lib/interval.c | 2 +- ext/date/lib/tm2unixtime.c | 75 +++++++++++++++++++++++------------ ext/date/tests/bug49081.phpt | 22 ++++++++++ ext/date/tests/date_diff.phpt | 57 ++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 26 deletions(-) create mode 100644 ext/date/tests/bug49081.phpt create mode 100644 ext/date/tests/date_diff.phpt diff --git a/NEWS b/NEWS index 453bf7a547..bb0e759502 100644 --- a/NEWS +++ b/NEWS @@ -129,6 +129,8 @@ PHP NEWS - 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). (yoarvi@gmail.com, Derick) - Fixed bug #48983 (DomDocument : saveHTMLFile wrong charset). (Rob) diff --git a/ext/date/lib/interval.c b/ext/date/lib/interval.c index 74165d243e..6ac07419b4 100644 --- a/ext/date/lib/interval.c +++ b/ext/date/lib/interval.c @@ -56,7 +56,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) 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); diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c index 3707771f67..59f7daf9fb 100644 --- a/ext/date/lib/tm2unixtime.c +++ b/ext/date/lib/tm2unixtime.c @@ -41,39 +41,64 @@ static int do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, t 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; - } - if (*d > days_next_month) { - *d -= days_next_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); + } } - 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 @@ void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt) 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)); } diff --git a/ext/date/tests/bug49081.phpt b/ext/date/tests/bug49081.phpt new file mode 100644 index 0000000000..f4f02903d1 --- /dev/null +++ b/ext/date/tests/bug49081.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug #49081 (DateTime::diff() mistake if start in January and interval > 28 days) +--FILE-- +diff($d2); + print_r($d); +?> +--EXPECT-- +DateInterval Object +( + [y] => 0 + [m] => 0 + [d] => 30 + [h] => 4 + [i] => 0 + [s] => 0 + [invert] => 0 + [days] => 30 +) diff --git a/ext/date/tests/date_diff.phpt b/ext/date/tests/date_diff.phpt new file mode 100644 index 0000000000..967e9136ce --- /dev/null +++ b/ext/date/tests/date_diff.phpt @@ -0,0 +1,57 @@ +--TEST-- +Extensive test for date_diff(). +--FILE-- +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 -- 2.40.0