[california/wip/725783-time] WallTime.round_down() -> WallTime.round()



commit d4b47dbdc00b09bb6bad646f5eb39277fa56ed8e
Author: Jim Nelson <jim yorba org>
Date:   Tue Aug 5 14:10:55 2014 -0700

    WallTime.round_down() -> WallTime.round()

 src/calendar/calendar-wall-time.vala    |   77 +++++++++++++++++++++------
 src/host/host-event-time-settings.vala  |    3 +-
 src/tests/tests-calendar-wall-time.vala |   91 ++++++++++++++++++++++++-------
 src/view/week/week-day-pane.vala        |    2 +-
 src/view/week/week-grid.vala            |    4 +-
 5 files changed, 137 insertions(+), 40 deletions(-)
---
diff --git a/src/calendar/calendar-wall-time.vala b/src/calendar/calendar-wall-time.vala
index 57de06d..57d6d8e 100644
--- a/src/calendar/calendar-wall-time.vala
+++ b/src/calendar/calendar-wall-time.vala
@@ -348,60 +348,105 @@ public class WallTime : BaseObject, Gee.Comparable<WallTime>, Gee.Hashable<WallT
     /**
      * Round a unit of the { link WallTime} to a multiple of a supplied value.
      *
-     * By rounding wall-clock time, not only is the unit in question rounded down to a multiple of
+     * Supply a positive integer to round up, a negative integer to round down.
+     *
+     * By rounding wall-clock time, not only is the unit in question rounded to a multiple of
      * the supplied value, but the lesser units are truncated to zero.  Thus, 17:23:54 rounded down
      * to a multiple of 10 minutes returns 17:20:00.
      *
+     * rollover is set to true if rounding by the multiple rolls the WallTime over to the next day.
+     * Rolling back to the previous day isn't possible with this interface; rounding down any value
+     * earlier than midnight results in midnight.  Rollover can occur when rounding up.
+     *
+     * It's important to note that zero is treated as a multiple of all values.  Hence rounding
+     * 11:56:00 up to a multiple of 17 minutes will result in 12:00:00.  (In other words, don't
+     * confuse this method with { link adjust}.
+     *
      * If the { link TimeUnit} is already a multiple of the value, no change is made (although
      * there's no guarantee that the same WallTime instance will be returned, especially if the
      * lesser units are truncated).
      *
-     * A multiple of zero or a negative value is always rounded to the current WallTime.
-     *
-     * TODO: An interface to round up (which will need to deal with overflow).
+     * A multiple of zero is always rounded to the current WallTime.
      */
-    public WallTime round_down(int multiple, TimeUnit time_unit) {
-        if (multiple <= 0)
+    public WallTime round(int multiple, TimeUnit time_unit, out bool rollover) {
+        rollover = false;
+        
+        if (multiple == 0)
             return this;
         
-        // get value being manipulated
-        int current;
+        // get value being manipulated and its max value (min is always zero)
+        int current, max;
         switch (time_unit) {
             case TimeUnit.HOUR:
                 current = hour;
+                max = MAX_HOUR;
             break;
             
             case TimeUnit.MINUTE:
                 current = minute;
+                max = MAX_MINUTE;
             break;
             
             case TimeUnit.SECOND:
                 current = second;
+                max = MAX_SECOND;
             break;
             
             default:
                 assert_not_reached();
         }
         
-        // round down and watch for underflow (which shouldn't happen)
-        int rounded = current - (current % multiple.abs());
-        if (rounded < 0)
-            rounded = 0;
+        int rounded;
+        if (multiple < 0) {
+            // round down and watch for underflow (which shouldn't happen)
+            rounded = current - (current % multiple.abs());
+            assert(rounded >= 0);
+        } else {
+            assert(multiple > 0);
+            
+            // round up and watch for overflow (which can definitely happen)
+            int rem = current % multiple;
+            if (rem != 0) {
+                rounded = current + (multiple - rem);
+                if (rounded > max) {
+                    rounded = 0;
+                    rollover = true;
+                }
+            } else {
+                // no remainder then on the money
+                rounded = current;
+            }
+        }
         
-        // return new value
+        // construct new value and deal with rollover
+        Calendar.WallTime rounded_wall_time;
+        bool adjust_rollover = false;
         switch (time_unit) {
             case TimeUnit.HOUR:
-                return new WallTime(rounded, 0, 0);
+                // no adjust can be done, rollover is rollover here
+                rounded_wall_time = new WallTime(rounded, 0, 0);
+            break;
             
             case TimeUnit.MINUTE:
-                return new WallTime(hour, rounded, 0);
+                rounded_wall_time = new WallTime(hour, rounded, 0);
+                if (rollover)
+                    rounded_wall_time = rounded_wall_time.adjust(1, TimeUnit.HOUR, out adjust_rollover);
+            break;
             
             case TimeUnit.SECOND:
-                return new WallTime(hour, minute, rounded);
+                rounded_wall_time = new WallTime(hour, minute, rounded);
+                if (rollover)
+                    rounded_wall_time = rounded_wall_time.adjust(1, TimeUnit.MINUTE, out adjust_rollover);
+            break;
             
             default:
                 assert_not_reached();
         }
+        
+        // handle adjustment causing rollover
+        rollover = rollover || adjust_rollover;
+        
+        return rounded_wall_time;
     }
     
     /**
diff --git a/src/host/host-event-time-settings.vala b/src/host/host-event-time-settings.vala
index a2ea75b..9440ce2 100644
--- a/src/host/host-event-time-settings.vala
+++ b/src/host/host-event-time-settings.vala
@@ -124,7 +124,8 @@ public class EventTimeSettings : Gtk.Box, Toolkit.Card {
             to_widget.wall_time = time_span.end_exact_time.to_wall_time();
         } else {
             // set to defaults in case user wants to change from all-day to timed event
-            from_widget.wall_time = Calendar.System.now.to_wall_time().round_down(15, 
Calendar.TimeUnit.MINUTE);
+            from_widget.wall_time = Calendar.System.now.to_wall_time().round(15, Calendar.TimeUnit.MINUTE,
+                null);
             if (date_span.is_same_day) {
                 // one-hour event is default
                 to_widget.wall_time = from_widget.wall_time.adjust(1, Calendar.TimeUnit.HOUR, null);
diff --git a/src/tests/tests-calendar-wall-time.vala b/src/tests/tests-calendar-wall-time.vala
index ef974de..5844cfd 100644
--- a/src/tests/tests-calendar-wall-time.vala
+++ b/src/tests/tests-calendar-wall-time.vala
@@ -8,12 +8,17 @@ namespace California.Tests {
 
 internal class CalendarWallTime : UnitTest.Harness {
     public CalendarWallTime() {
-        add_case("round-down-perverse", round_down_perverse);
-        add_case("round-down-zero", round_down_zero);
+        add_case("round-zero", round_zero);
         add_case("round-down-hour-no-change", round_down_hour_no_change);
         add_case("round-down-hour-change", round_down_hour_change);
         add_case("round-down-minute", round_down_minute);
         add_case("round-down-second", round_down_second);
+        add_case("round-down-no-rollover", round_down_no_rollover);
+        add_case("round-up-hour-no-change", round_up_hour_no_change);
+        add_case("round-up-hour-change", round_up_hour_change);
+        add_case("round-up-minute", round_up_minute);
+        add_case("round-up-second", round_up_second);
+        add_case("round-up-rollover", round_up_rollover);
     }
     
     protected override void setup() throws Error {
@@ -24,46 +29,92 @@ internal class CalendarWallTime : UnitTest.Harness {
         Calendar.terminate();
     }
     
-    private bool round_down_perverse() throws Error {
+    private bool round_zero() throws Error {
         Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 14);
-        Calendar.WallTime round_down = wall_time.round_down(-1, Calendar.TimeUnit.MINUTE);
+        bool rollover;
+        Calendar.WallTime rounded = wall_time.round(0, Calendar.TimeUnit.HOUR, out rollover);
         
-        return wall_time.equal_to(round_down);
-    }
-    
-    private bool round_down_zero() throws Error {
-        Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 14);
-        Calendar.WallTime round_down = wall_time.round_down(0, Calendar.TimeUnit.HOUR);
-        
-        return wall_time.equal_to(round_down);
+        return !rollover && wall_time.equal_to(rounded);
     }
     
     private bool round_down_hour_no_change() throws Error {
         Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 14);
-        Calendar.WallTime round_down = wall_time.round_down(2, Calendar.TimeUnit.HOUR);
+        bool rollover;
+        Calendar.WallTime round_down = wall_time.round(-2, Calendar.TimeUnit.HOUR, out rollover);
         
-        return round_down.hour == 10 && round_down.minute == 0 && round_down.second == 0;
+        return !rollover && round_down.hour == 10 && round_down.minute == 0 && round_down.second == 0;
     }
     
     private bool round_down_hour_change() throws Error {
         Calendar.WallTime wall_time = new Calendar.WallTime(9, 12, 14);
-        Calendar.WallTime round_down = wall_time.round_down(2, Calendar.TimeUnit.HOUR);
+        bool rollover;
+        Calendar.WallTime round_down = wall_time.round(-2, Calendar.TimeUnit.HOUR, out rollover);
         
-        return round_down.hour == 8 && round_down.minute == 0 && round_down.second == 0;
+        return !rollover && round_down.hour == 8 && round_down.minute == 0 && round_down.second == 0;
     }
     
     private bool round_down_minute() throws Error {
         Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 14);
-        Calendar.WallTime round_down = wall_time.round_down(10, Calendar.TimeUnit.MINUTE);
+        bool rollover;
+        Calendar.WallTime round_down = wall_time.round(-10, Calendar.TimeUnit.MINUTE, out rollover);
         
-        return round_down.hour == 10 && round_down.minute == 10 && round_down.second == 0;
+        return !rollover && round_down.hour == 10 && round_down.minute == 10 && round_down.second == 0;
     }
     
     private bool round_down_second() throws Error {
         Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 16);
-        Calendar.WallTime round_down = wall_time.round_down(15, Calendar.TimeUnit.SECOND);
+        bool rollover;
+        Calendar.WallTime round_down = wall_time.round(-15, Calendar.TimeUnit.SECOND, out rollover);
+        
+        return !rollover && round_down.hour == 10 && round_down.minute == 12 && round_down.second == 15;
+    }
+    
+    private bool round_down_no_rollover() throws Error {
+        Calendar.WallTime wall_time = Calendar.WallTime.earliest;
+        bool rollover;
+        Calendar.WallTime round_down = wall_time.round(-15, Calendar.TimeUnit.SECOND, out rollover);
+        
+        return !rollover && round_down.equal_to(wall_time);
+    }
+    
+    private bool round_up_hour_no_change() throws Error {
+        Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 14);
+        bool rollover;
+        Calendar.WallTime round_up = wall_time.round(2, Calendar.TimeUnit.HOUR, out rollover);
+        
+        return !rollover && round_up.hour == 10 && round_up.minute == 0 && round_up.second == 0;
+    }
+    
+    private bool round_up_hour_change() throws Error {
+        Calendar.WallTime wall_time = new Calendar.WallTime(9, 12, 14);
+        bool rollover;
+        Calendar.WallTime round_up = wall_time.round(2, Calendar.TimeUnit.HOUR, out rollover);
+        
+        return !rollover && round_up.hour == 10 && round_up.minute == 0 && round_up.second == 0;
+    }
+    
+    private bool round_up_minute() throws Error {
+        Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 14);
+        bool rollover;
+        Calendar.WallTime round_up = wall_time.round(10, Calendar.TimeUnit.MINUTE, out rollover);
+        
+        return !rollover && round_up.hour == 10 && round_up.minute == 20 && round_up.second == 0;
+    }
+    
+    private bool round_up_second() throws Error {
+        Calendar.WallTime wall_time = new Calendar.WallTime(10, 12, 16);
+        bool rollover;
+        Calendar.WallTime round_up = wall_time.round(15, Calendar.TimeUnit.SECOND, out rollover);
+        
+        return !rollover && round_up.hour == 10 && round_up.minute == 12 && round_up.second == 30;
+    }
+    
+    private bool round_up_rollover() throws Error {
+        Calendar.WallTime wall_time = new Calendar.WallTime(23, 55, 16);
+        bool rollover;
+        Calendar.WallTime round_up = wall_time.round(15, Calendar.TimeUnit.MINUTE, out rollover);
         
-        return round_down.hour == 10 && round_down.minute == 12 && round_down.second == 15;
+        return rollover && round_up.hour == 0 && round_up.minute == 0 && round_up.second == 0;
     }
 }
 
diff --git a/src/view/week/week-day-pane.vala b/src/view/week/week-day-pane.vala
index 5090355..cf3a558 100644
--- a/src/view/week/week-day-pane.vala
+++ b/src/view/week/week-day-pane.vala
@@ -152,7 +152,7 @@ internal class DayPane : Pane, Common.InstanceContainer {
     
     public void update_selection(Calendar.WallTime wall_time) {
         // round down to the nearest 15-minute mark
-        Calendar.WallTime rounded_time = wall_time.round_down(15, Calendar.TimeUnit.MINUTE);
+        Calendar.WallTime rounded_time = wall_time.round(-15, Calendar.TimeUnit.MINUTE, null);
         
         // assign start first, end second (ordering doesn't matter, possible to select upwards)
         if (selection_start == null) {
diff --git a/src/view/week/week-grid.vala b/src/view/week/week-grid.vala
index ea82fdc..6556a02 100644
--- a/src/view/week/week-grid.vala
+++ b/src/view/week/week-grid.vala
@@ -345,8 +345,8 @@ internal class Grid : Gtk.Box {
         DayPane? day_pane = instance_container as DayPane;
         if (day_pane != null) {
             // convert click into starting time on the day pane rounded down to the nearest half-hour
-            Calendar.WallTime wall_time = day_pane.get_wall_time(details.press_point.y).round_down(
-                30, Calendar.TimeUnit.MINUTE);
+            Calendar.WallTime wall_time = day_pane.get_wall_time(details.press_point.y).round(-30,
+                Calendar.TimeUnit.MINUTE, null);
             
             Calendar.ExactTime start_time = new Calendar.ExactTime(Calendar.Timezone.local,
                 day_pane.date, wall_time);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]