From 4effeeb16b46e7e32eb643870609260e9c383f2c Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Tue, 19 Jun 2018 12:10:00 -0400 Subject: [PATCH] Fix #2743 - old date timezone precision in Chrome 67+ --- src/lib/dates.js | 11 ++++++++++- test/jasmine/tests/lib_date_test.js | 29 ++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/lib/dates.js b/src/lib/dates.js index 8d28045ce53..9bfdb77616e 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -138,7 +138,16 @@ exports.dateTime2ms = function(s, calendar) { if(exports.isJSDate(s)) { // Convert to the UTC milliseconds that give the same // hours as this date has in the local timezone - s = Number(s) - s.getTimezoneOffset() * ONEMIN; + var tzOffset = s.getTimezoneOffset() * ONEMIN; + var offsetTweak = (s.getUTCMinutes() - s.getMinutes()) * ONEMIN + + (s.getUTCSeconds() - s.getSeconds()) * ONESEC + + (s.getUTCMilliseconds() - s.getMilliseconds()); + + if(offsetTweak) { + var comb = 3 * ONEMIN; + tzOffset = tzOffset - comb / 2 + mod(offsetTweak - tzOffset + comb / 2, comb); + } + s = Number(s) - tzOffset; if(s >= MIN_MS && s <= MAX_MS) return s; return BADNUM; } diff --git a/test/jasmine/tests/lib_date_test.js b/test/jasmine/tests/lib_date_test.js index 2f4c86f9982..2a0971afd7b 100644 --- a/test/jasmine/tests/lib_date_test.js +++ b/test/jasmine/tests/lib_date_test.js @@ -24,6 +24,29 @@ describe('dates', function() { nowPlus29 = thisYear + 29, nowPlus29_2 = nowPlus29 % 100; + function tweakedTZOffset(d) { + var tzOffset = d.getTimezoneOffset() * 60000; + var offsetTweak = (d.getUTCMinutes() - d.getMinutes()) * 60000 + + (d.getUTCSeconds() - d.getSeconds()) * 1000 + + (d.getUTCMilliseconds() - d.getMilliseconds()); + + if(offsetTweak) { + var comb = 3 * 60000; + var tzOffset2 = tzOffset - comb / 2 + Lib.mod(offsetTweak - tzOffset + comb / 2, comb); + // this tweak logic just copies what's in dateTime2ms to account for + // Chrome's new handling of dates before there were timezones, see + // https://github.com/plotly/plotly.js/issues/2743 + // This logic has been validated manually using: + // Plotly.newPlot(gd,[{x:[new Date(1600,0,1),new Date(1600,0,1,0,1)],y:[1,2]}]) + // here just check that it's only happening for years before 1884, + // and only adjusting the result less than a minute. + expect(d.getFullYear()).toBeLessThan(1884); + expect(Math.abs(tzOffset2 - tzOffset)).toBeLessThan(60000); + return tzOffset2; + } + return tzOffset; + } + describe('dateTime2ms', function() { it('should accept valid date strings', function() { var tzOffset; @@ -62,9 +85,9 @@ describe('dates', function() { ].forEach(function(v) { // just for sub-millisecond precision tests, use timezoneoffset // from the previous date object - if(v[1].getTimezoneOffset) tzOffset = v[1].getTimezoneOffset(); + if(v[1].getTimezoneOffset) tzOffset = tweakedTZOffset(v[1]); - var expected = +v[1] - (tzOffset * 60000); + var expected = +v[1] - tzOffset; expect(Lib.dateTime2ms(v[0])).toBe(expected, v[0]); // ISO-8601: all the same stuff with t or T as the separator @@ -108,7 +131,7 @@ describe('dates', function() { d1c, new Date(2015, 8, 7, 23, 34, 45, 567) ].forEach(function(v) { - expect(Lib.dateTime2ms(v)).toBe(+v - v.getTimezoneOffset() * 60000); + expect(Lib.dateTime2ms(v)).toBe(+v - tweakedTZOffset(v), v.toString()); }); });