factor out Dygraph.setDateSameTZ
authorDan Vanderkam <danvdk@gmail.com>
Mon, 18 Feb 2013 02:37:02 +0000 (21:37 -0500)
committerDan Vanderkam <danvdk@gmail.com>
Mon, 18 Feb 2013 02:37:02 +0000 (21:37 -0500)
auto_tests/tests/utils_test.js
dygraph-tickers.js
dygraph-utils.js

index 62b2c63..39e60d2 100644 (file)
@@ -95,7 +95,7 @@ UtilsTestCase.prototype.testIterator_nopredicate = function() {
   assertEquals('e', iter.next());
 
   assertFalse(iter.hasNext);
-}
+};
 
 UtilsTestCase.prototype.testIterator_predicate = function() {
   var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
@@ -119,7 +119,7 @@ UtilsTestCase.prototype.testIterator_empty = function() {
   var array = [];
   var iter = Dygraph.createIterator([], 0, 0);
   assertFalse(iter.hasNext);
-}
+};
 
 UtilsTestCase.prototype.testIterator_outOfRange = function() {
   var array = ['a', 'b', 'c'];
@@ -134,7 +134,7 @@ UtilsTestCase.prototype.testIterator_outOfRange = function() {
   assertEquals('c', iter.next());
 
   assertFalse(iter.hasNext);
-}
+};
 
 // Makes sure full array is tested, and that the predicate isn't called
 // with invalid boundaries.
@@ -156,7 +156,7 @@ UtilsTestCase.prototype.testIterator_whole_array = function() {
   assertEquals('c', iter.next());
   assertFalse(iter.hasNext);
   assertNull(iter.next());
-}
+};
 
 UtilsTestCase.prototype.testIterator_no_args = function() {
   var array = ['a', 'b', 'c'];
@@ -169,4 +169,18 @@ UtilsTestCase.prototype.testIterator_no_args = function() {
   assertEquals('c', iter.next());
   assertFalse(iter.hasNext);
   assertNull(iter.next());
-}
+};
+
+UtilsTestCase.prototype.testDateSet = function() {
+  var base = new Date(1383455100000);
+  var d = new Date(base);
+
+  // A one hour shift -- this is surprising behavior!
+  d.setMilliseconds(10);
+  assertEquals(3600010, d.getTime() - base.getTime());
+
+  // setDateSameTZ compensates for this surprise.
+  d = new Date(base);
+  Dygraph.setDateSameTZ(d, {ms: 10});
+  assertEquals(10, d.getTime() - base.getTime());
+};
index e57e8bd..4b4d9dd 100644 (file)
@@ -340,25 +340,6 @@ Dygraph.getDateAxis = function(start_time, end_time, granularity, opts, dg) {
   var ticks = [];
   var t;
 
-  var setters = {
-    ms: Date.prototype.setMilliseconds,
-    s: Date.prototype.setSeconds,
-    m: Date.prototype.setMinutes,
-    h: Date.prototype.setHours
-  };
-  var safeSet = function(d, parts) {
-    var tz = d.getTimezoneOffset();
-    for (var k in parts) {
-      if (!parts.hasOwnProperty(k)) continue;
-      var setter = setters[k];
-      if (!setter) throw "Invalid setter: " + k;
-      setter.call(d, parts[k]);
-      if (d.getTimezoneOffset() != tz) {
-        d.setTime(d.getTime() + (tz - d.getTimezoneOffset()) * 60 * 1000);
-      }
-    }
-  };
-
   if (granularity < Dygraph.MONTHLY) {
     // Generate one tick mark for every fixed interval of time.
     var spacing = Dygraph.SHORT_SPACINGS[granularity];
@@ -367,20 +348,20 @@ Dygraph.getDateAxis = function(start_time, end_time, granularity, opts, dg) {
     // for this granularity.
     var g = spacing / 1000;
     var d = new Date(start_time);
-    safeSet(d, {ms: 0});
+    Dygraph.setDateSameTZ(d, {ms: 0});
 
     var x;
     if (g <= 60) {  // seconds
       x = d.getSeconds();
-      safeSet(d, {s: x - x % g});
+      Dygraph.setDateSameTZ(d, {s: x - x % g});
     } else {
-      safeSet(d, {s: 0});
+      Dygraph.setDateSameTZ(d, {s: 0});
       g /= 60;
       if (g <= 60) {  // minutes
         x = d.getMinutes();
-        safeSet(d, {m: x - x % g});
+        Dygraph.setDateSameTZ(d, {m: x - x % g});
       } else {
-        safeSet(d, {m: 0});
+        Dygraph.setDateSameTZ(d, {m: 0});
         g /= 60;
 
         if (g <= 24) {  // days
@@ -398,6 +379,9 @@ Dygraph.getDateAxis = function(start_time, end_time, granularity, opts, dg) {
     }
     start_time = d.getTime();
 
+    // For spacings coarser than two-hourly, we want to ignore daylight
+    // savings transitions to get consistent ticks. For finer-grained ticks,
+    // it's essential to show the DST transition in all its messiness.
     var start_offset_min = new Date(start_time).getTimezoneOffset();
     var check_dst = (spacing >= Dygraph.SHORT_SPACINGS[Dygraph.TWO_HOURLY]);
 
index ffedb27..aee272c 100644 (file)
@@ -1245,3 +1245,33 @@ Dygraph.pow = function(base, exp) {
   return Math.pow(base, exp);
 };
 
+// For Dygraph.setDateSameTZ, below.
+Dygraph.dateSetters = {
+  ms: Date.prototype.setMilliseconds,
+  s: Date.prototype.setSeconds,
+  m: Date.prototype.setMinutes,
+  h: Date.prototype.setHours
+};
+
+/**
+ * This is like calling d.setSeconds(), d.setMinutes(), etc, except that it
+ * adjusts for time zone changes to keep the date/time parts consistent.
+ *
+ * For example, d.getSeconds(), d.getMinutes() and d.getHours() will all be
+ * the same before/after you call setDateSameTZ(d, {ms: 0}). The same is not
+ * true if you call d.setMilliseconds(0).
+ *
+ * @type {function(!Date, Object.<number>)}
+ */
+Dygraph.setDateSameTZ = function(d, parts) {
+  var tz = d.getTimezoneOffset();
+  for (var k in parts) {
+    if (!parts.hasOwnProperty(k)) continue;
+    var setter = Dygraph.dateSetters[k];
+    if (!setter) throw "Invalid setter: " + k;
+    setter.call(d, parts[k]);
+    if (d.getTimezoneOffset() != tz) {
+      d.setTime(d.getTime() + (tz - d.getTimezoneOffset()) * 60 * 1000);
+    }
+  }
+};