summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--ext/date/lib/interval.c2
-rw-r--r--ext/date/lib/tm2unixtime.c75
-rw-r--r--ext/date/tests/bug49081.phpt22
-rw-r--r--ext/date/tests/date_diff.phpt57
5 files changed, 132 insertions, 26 deletions
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--
+<?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
index 0000000000..967e9136ce
--- /dev/null
+++ b/ext/date/tests/date_diff.phpt
@@ -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