]> granicus.if.org Git - php/commitdiff
Fix #61642: modify("+5 weekdays") returns Sunday
authorDmitri Iouchtchenko <johnnyspoon@gmail.com>
Sun, 29 Jul 2012 17:41:40 +0000 (13:41 -0400)
committerStanislav Malyshev <stas@php.net>
Mon, 6 Aug 2012 02:22:36 +0000 (19:22 -0700)
Adding a non-zero multiple of 5 weekdays to any Friday, Saturday, or
Sunday would result in a Sunday instead of the correct date. This patch
provides an implementation of do_adjust_special_weekday() which does
not suffer from this issue.

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

index c4830bbef0c7b787fce9e555f5a04d6dd104809e..9055fee203f549439660f12f83b7afb9b2bd047d 100644 (file)
@@ -220,55 +220,52 @@ static void do_adjust_relative(timelib_time* time)
 
 static void do_adjust_special_weekday(timelib_time* time)
 {
-       timelib_sll current_dow, count;
+       timelib_sll count, dow, rem;
 
        count = time->relative.special.amount;
+       dow = timelib_day_of_week(time->y, time->m, time->d);
 
-       current_dow = timelib_day_of_week(time->y, time->m, time->d);
-       if (count == 0) {
-               /* skip over saturday and sunday */
-               if (current_dow == 6) {
-                       time->d += 2;
-               }
-               /* skip over sunday */
-               if (current_dow == 0) {
-                       time->d += 1;
-               }
-       } else if (count > 0) {
-               /* skip over saturday and sunday */
-               if (current_dow == 5) {
-                       time->d += 2;
-               }
-               /* skip over sunday */
-               if (current_dow == 6) {
+       /* Add increments of 5 weekdays as a week, leaving the DOW unchanged. */
+       time->d += (count / 5) * 7;
+
+       /* Deal with the remainder. */
+       rem = (count % 5);
+
+       if (count > 0) {
+               if (rem == 0) {
+                       /* Head back to Friday if we stop on the weekend. */
+                       if (dow == 0) {
+                               time->d -= 2;
+                       } else if (dow == 6) {
+                               time->d -= 1;
+                       }
+               } else if (dow == 6) {
+                       /* We ended up on Saturday, but there's still work to do, so move
+                        * to Sunday and continue from there. */
                        time->d += 1;
-               }
-               /* add increments of 5 weekdays as a week */
-               time->d += (count / 5) * 7;
-               /* if current DOW plus the remainder > 5, add two days */
-               current_dow = timelib_day_of_week(time->y, time->m, time->d);
-               time->d += (count % 5);
-               if ((count % 5) + current_dow > 5) {
+               } else if (dow + rem > 5) {
+                       /* We're on a weekday, but we're going past Friday, so skip right
+                        * over the weekend. */
                        time->d += 2;
                }
-       } else if (count < 0) {
-               /* skip over sunday and saturday */
-               if (current_dow == 1) {
-                       time->d -= 2;
-               }
-               /* skip over satruday */
-               if (current_dow == 0 ) {
+       } else {
+               /* Completely mirror the forward direction. This also covers the 0
+                * case, since if we start on the weekend, we want to move forward as
+                * if we stopped there while going backwards. */
+               if (rem == 0) {
+                       if (dow == 6) {
+                               time->d += 2;
+                       } else if (dow == 0) {
+                               time->d += 1;
+                       }
+               } else if (dow == 0) {
                        time->d -= 1;
-               }
-               /* subtract increments of 5 weekdays as a week */
-               time->d += (count / 5) * 7;
-               /* if current DOW minus the remainder < 0, subtract two days */
-               current_dow = timelib_day_of_week(time->y, time->m, time->d);
-               time->d += (count % 5);
-               if ((count % 5) + current_dow < 1) {
+               } else if (dow + rem < 1) {
                        time->d -= 2;
                }
        }
+
+       time->d += rem;
 }
 
 static void do_adjust_special(timelib_time* time)
diff --git a/ext/date/tests/bug61642.phpt b/ext/date/tests/bug61642.phpt
new file mode 100644 (file)
index 0000000..d03a814
--- /dev/null
@@ -0,0 +1,62 @@
+--TEST--
+Bug #61642 (modify("+5 weekdays") returns Sunday)
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+// ±5 and ±10 (and any non-zero multiple of 5) is broken, but everything else
+// should already work correctly.
+$weekdays = range(-11, 11);
+$dates = array('2012-03-29', '2012-03-30', '2012-03-31', '2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05');
+
+$header = array();
+
+foreach ($dates as $startdate) {
+       $date = new DateTime($startdate);
+
+       $header[] = $date->format('Y-m-d D');
+}
+
+echo '###  ', implode('  ', $header), "\n\n";
+
+foreach ($weekdays as $days) {
+       $line = array();
+
+       printf('%+3d  ', $days);
+
+       foreach ($dates as $startdate) {
+               $date = new DateTime($startdate);
+               $date->modify("{$days} weekdays");
+
+               $line[] = $date->format('Y-m-d D');
+       }
+
+       echo implode('  ', $line), "\n";
+}
+?>
+--EXPECTF--
+###  2012-03-29 Thu  2012-03-30 Fri  2012-03-31 Sat  2012-04-01 Sun  2012-04-02 Mon  2012-04-03 Tue  2012-04-04 Wed  2012-04-05 Thu
+
+-11  2012-03-14 Wed  2012-03-15 Thu  2012-03-16 Fri  2012-03-16 Fri  2012-03-16 Fri  2012-03-19 Mon  2012-03-20 Tue  2012-03-21 Wed
+-10  2012-03-15 Thu  2012-03-16 Fri  2012-03-19 Mon  2012-03-19 Mon  2012-03-19 Mon  2012-03-20 Tue  2012-03-21 Wed  2012-03-22 Thu
+ -9  2012-03-16 Fri  2012-03-19 Mon  2012-03-20 Tue  2012-03-20 Tue  2012-03-20 Tue  2012-03-21 Wed  2012-03-22 Thu  2012-03-23 Fri
+ -8  2012-03-19 Mon  2012-03-20 Tue  2012-03-21 Wed  2012-03-21 Wed  2012-03-21 Wed  2012-03-22 Thu  2012-03-23 Fri  2012-03-26 Mon
+ -7  2012-03-20 Tue  2012-03-21 Wed  2012-03-22 Thu  2012-03-22 Thu  2012-03-22 Thu  2012-03-23 Fri  2012-03-26 Mon  2012-03-27 Tue
+ -6  2012-03-21 Wed  2012-03-22 Thu  2012-03-23 Fri  2012-03-23 Fri  2012-03-23 Fri  2012-03-26 Mon  2012-03-27 Tue  2012-03-28 Wed
+ -5  2012-03-22 Thu  2012-03-23 Fri  2012-03-26 Mon  2012-03-26 Mon  2012-03-26 Mon  2012-03-27 Tue  2012-03-28 Wed  2012-03-29 Thu
+ -4  2012-03-23 Fri  2012-03-26 Mon  2012-03-27 Tue  2012-03-27 Tue  2012-03-27 Tue  2012-03-28 Wed  2012-03-29 Thu  2012-03-30 Fri
+ -3  2012-03-26 Mon  2012-03-27 Tue  2012-03-28 Wed  2012-03-28 Wed  2012-03-28 Wed  2012-03-29 Thu  2012-03-30 Fri  2012-04-02 Mon
+ -2  2012-03-27 Tue  2012-03-28 Wed  2012-03-29 Thu  2012-03-29 Thu  2012-03-29 Thu  2012-03-30 Fri  2012-04-02 Mon  2012-04-03 Tue
+ -1  2012-03-28 Wed  2012-03-29 Thu  2012-03-30 Fri  2012-03-30 Fri  2012-03-30 Fri  2012-04-02 Mon  2012-04-03 Tue  2012-04-04 Wed
+ +0  2012-03-29 Thu  2012-03-30 Fri  2012-04-02 Mon  2012-04-02 Mon  2012-04-02 Mon  2012-04-03 Tue  2012-04-04 Wed  2012-04-05 Thu
+ +1  2012-03-30 Fri  2012-04-02 Mon  2012-04-02 Mon  2012-04-02 Mon  2012-04-03 Tue  2012-04-04 Wed  2012-04-05 Thu  2012-04-06 Fri
+ +2  2012-04-02 Mon  2012-04-03 Tue  2012-04-03 Tue  2012-04-03 Tue  2012-04-04 Wed  2012-04-05 Thu  2012-04-06 Fri  2012-04-09 Mon
+ +3  2012-04-03 Tue  2012-04-04 Wed  2012-04-04 Wed  2012-04-04 Wed  2012-04-05 Thu  2012-04-06 Fri  2012-04-09 Mon  2012-04-10 Tue
+ +4  2012-04-04 Wed  2012-04-05 Thu  2012-04-05 Thu  2012-04-05 Thu  2012-04-06 Fri  2012-04-09 Mon  2012-04-10 Tue  2012-04-11 Wed
+ +5  2012-04-05 Thu  2012-04-06 Fri  2012-04-06 Fri  2012-04-06 Fri  2012-04-09 Mon  2012-04-10 Tue  2012-04-11 Wed  2012-04-12 Thu
+ +6  2012-04-06 Fri  2012-04-09 Mon  2012-04-09 Mon  2012-04-09 Mon  2012-04-10 Tue  2012-04-11 Wed  2012-04-12 Thu  2012-04-13 Fri
+ +7  2012-04-09 Mon  2012-04-10 Tue  2012-04-10 Tue  2012-04-10 Tue  2012-04-11 Wed  2012-04-12 Thu  2012-04-13 Fri  2012-04-16 Mon
+ +8  2012-04-10 Tue  2012-04-11 Wed  2012-04-11 Wed  2012-04-11 Wed  2012-04-12 Thu  2012-04-13 Fri  2012-04-16 Mon  2012-04-17 Tue
+ +9  2012-04-11 Wed  2012-04-12 Thu  2012-04-12 Thu  2012-04-12 Thu  2012-04-13 Fri  2012-04-16 Mon  2012-04-17 Tue  2012-04-18 Wed
++10  2012-04-12 Thu  2012-04-13 Fri  2012-04-13 Fri  2012-04-13 Fri  2012-04-16 Mon  2012-04-17 Tue  2012-04-18 Wed  2012-04-19 Thu
++11  2012-04-13 Fri  2012-04-16 Mon  2012-04-16 Mon  2012-04-16 Mon  2012-04-17 Tue  2012-04-18 Wed  2012-04-19 Thu  2012-04-20 Fri