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

Reply via email to