]> granicus.if.org Git - php/commitdiff
- Fixed bug #49081 (DateTime::diff() mistake if start in January and interval >
authorDerick Rethans <derick@php.net>
Tue, 4 May 2010 15:11:41 +0000 (15:11 +0000)
committerDerick Rethans <derick@php.net>
Tue, 4 May 2010 15:11:41 +0000 (15:11 +0000)
  28 days). (Derick)

ext/date/lib/interval.c
ext/date/lib/tm2unixtime.c
ext/date/tests/bug49081.phpt [new file with mode: 0644]
ext/date/tests/date_diff.phpt [new file with mode: 0644]

index 74165d243e0e8d9ce22ebad9e6ba9f2085034e94..6ac07419b4bce2f1ec860d23ca9f5929353ef09a 100644 (file)
@@ -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);
index 3707771f6719c6cf08758f959d194d7f5779e31e..59f7daf9fb0d1d14f1ab3c956107cba9a825a3d2 100644 (file)
@@ -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 (file)
index 0000000..f4f0290
--- /dev/null
@@ -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
+)
diff --git a/ext/date/tests/date_diff.phpt b/ext/date/tests/date_diff.phpt
new file mode 100644 (file)
index 0000000..967e913
--- /dev/null
@@ -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