From 9a4fd029e0cfd7ed2d89b54659df5e2d6b8b5d9d Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Sun, 17 Feb 2013 21:37:02 -0500 Subject: [PATCH] factor out Dygraph.setDateSameTZ --- auto_tests/tests/utils_test.js | 24 +++++++++++++++++++----- dygraph-tickers.js | 32 ++++++++------------------------ dygraph-utils.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/auto_tests/tests/utils_test.js b/auto_tests/tests/utils_test.js index 62b2c63..39e60d2 100644 --- a/auto_tests/tests/utils_test.js +++ b/auto_tests/tests/utils_test.js @@ -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()); +}; diff --git a/dygraph-tickers.js b/dygraph-tickers.js index e57e8bd..4b4d9dd 100644 --- a/dygraph-tickers.js +++ b/dygraph-tickers.js @@ -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]); diff --git a/dygraph-utils.js b/dygraph-utils.js index ffedb27..aee272c 100644 --- a/dygraph-utils.js +++ b/dygraph-utils.js @@ -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.)} + */ +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); + } + } +}; -- 2.7.4