From 899866b922aec16f1408599d0d9ab54f901c5ed7 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 16 Nov 2016 23:25:00 -0500 Subject: [PATCH 1/9] accept ISO-8601 dates, and rework dateTime2ms --- src/lib/dates.js | 127 ++++++++++------------------ test/jasmine/tests/lib_date_test.js | 18 +++- 2 files changed, 59 insertions(+), 86 deletions(-) diff --git a/src/lib/dates.js b/src/lib/dates.js index 72dfecd5f72..43de12f5719 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -10,7 +10,6 @@ 'use strict'; var d3 = require('d3'); -var isNumeric = require('fast-isnumeric'); var logError = require('./loggers').error; @@ -21,6 +20,11 @@ var ONEHOUR = constants.ONEHOUR; var ONEMIN = constants.ONEMIN; var ONESEC = constants.ONESEC; +var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(0?[1-9]|1[012])(-([0-3]?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m; + +// for 2-digit years, the first year we map them onto +var YFIRST = new Date().getFullYear() - 70; + // is an object a javascript date? exports.isJSDate = function(v) { return typeof v === 'object' && v !== null && typeof v.getTime === 'function'; @@ -32,12 +36,25 @@ exports.isJSDate = function(v) { var MIN_MS, MAX_MS; /** - * dateTime2ms - turn a date object or string s of the form - * YYYY-mm-dd HH:MM:SS.sss into milliseconds (relative to 1970-01-01, - * per javascript standard) - * may truncate after any full field, and sss can be any length - * even >3 digits, though javascript dates truncate to milliseconds - * returns BADNUM if it doesn't find a date + * dateTime2ms - turn a date object or string s into milliseconds + * (relative to 1970-01-01, per javascript standard) + * + * Returns BADNUM if it doesn't find a date + * + * strings should have the form: + * + * -?YYYY-mm-ddHH:MM:SS.sss? + * + * : space (our normal standard) or T or t (ISO-8601) + * : Z, z, or [+\-]HH:?MM and we THROW IT AWAY + * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6 + * but we allow it even with a space as the separator + * + * May truncate after any full field, and sss can be any length + * even >3 digits, though javascript dates truncate to milliseconds, + * we keep as much as javascript numeric precision can hold, but we only + * report back up to 100 microsecond precision, because most dates support + * this precision (close to 1970 support more, very far away support less) * * Expanded to support negative years to -9999 but you must always * give 4 digits, except for 2-digit positive years which we assume are @@ -45,7 +62,7 @@ var MIN_MS, MAX_MS; * Note that we follow ISO 8601:2004: there *is* a year 0, which * is 1BC/BCE, and -1===2BC etc. * - * 2-digit to 4-digit year conversion, where to cut off? + * Where to cut off 2-digit years between 1900s and 2000s? * from http://support.microsoft.com/kb/244664: * 1930-2029 (the most retro of all...) * but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')): @@ -77,89 +94,31 @@ exports.dateTime2ms = function(s) { // otherwise only accept strings and numbers if(typeof s !== 'string' && typeof s !== 'number') return BADNUM; - var y, m, d, h; - // split date and time parts - // TODO: we strip leading/trailing whitespace but not other - // characters like we do for numbers - do we want to? - var datetime = String(s).trim().split(' '); - if(datetime.length > 2) return BADNUM; - - var p = datetime[0].split('-'); // date part - - var CE = true; // common era, ie positive year - if(p[0] === '') { - // first part is blank: year starts with a minus sign - CE = false; - p.splice(0, 1); - } - - var plen = p.length; - if(plen > 3 || (plen !== 3 && datetime[1]) || !plen) return BADNUM; - - // year - if(p[0].length === 4) y = Number(p[0]); - else if(p[0].length === 2) { - if(!CE) return BADNUM; - var yNow = new Date().getFullYear(); - y = ((Number(p[0]) - yNow + 70) % 100 + 200) % 100 + yNow - 70; + var match = String(s).match(DATETIME_REGEXP); + if(!match) return BADNUM; + var y = match[1], + m = Number(match[3] || 1), + d = Number(match[5] || 1), + H = Number(match[7] || 0), + M = Number(match[9] || 0), + S = Number(match[11] || 0); + if(y.length === 2) { + y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST; } - else return BADNUM; - if(!isNumeric(y)) return BADNUM; + else y = Number(y); // javascript takes new Date(0..99,m,d) to mean 1900-1999, so // to support years 0-99 we need to use setFullYear explicitly - var baseDate = new Date(0, 0, 1); - baseDate.setFullYear(CE ? y : -y); - if(p.length > 1) { + var date = new Date(2000, m - 1, d, H, M); + date.setFullYear(y); - // month - may be 1 or 2 digits - m = Number(p[1]) - 1; // new Date() uses zero-based months - if(p[1].length > 2 || !(m >= 0 && m <= 11)) return BADNUM; - baseDate.setMonth(m); + if(date.getDate() !== d) return BADNUM; - if(p.length > 2) { + // does that hour exist in this day? (Daylight time!) + // (TODO: remove this check when we move to UTC) + if(date.getHours() !== H) return BADNUM; - // day - may be 1 or 2 digits - d = Number(p[2]); - if(p[2].length > 2 || !(d >= 1 && d <= 31)) return BADNUM; - baseDate.setDate(d); - - // does that date exist in this month? - if(baseDate.getDate() !== d) return BADNUM; - - if(datetime[1]) { - - p = datetime[1].split(':'); - if(p.length > 3) return BADNUM; - - // hour - may be 1 or 2 digits - h = Number(p[0]); - if(p[0].length > 2 || !p[0].length || !(h >= 0 && h <= 23)) return BADNUM; - baseDate.setHours(h); - - // does that hour exist in this day? (Daylight time!) - // (TODO: remove this check when we move to UTC) - if(baseDate.getHours() !== h) return BADNUM; - - if(p.length > 1) { - d = baseDate.getTime(); - - // minute - must be 2 digits - m = Number(p[1]); - if(p[1].length !== 2 || !(m >= 0 && m <= 59)) return BADNUM; - d += ONEMIN * m; - if(p.length === 2) return d; - - // second (and milliseconds) - must have 2-digit seconds - if(p[2].split('.')[0].length !== 2) return BADNUM; - s = Number(p[2]); - if(!(s >= 0 && s < 60)) return BADNUM; - return d + s * ONESEC; - } - } - } - } - return baseDate.getTime(); + return date.getTime() + S * ONESEC; }; MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999'); diff --git a/test/jasmine/tests/lib_date_test.js b/test/jasmine/tests/lib_date_test.js index f4771c539a4..d0d5c46bb54 100644 --- a/test/jasmine/tests/lib_date_test.js +++ b/test/jasmine/tests/lib_date_test.js @@ -29,6 +29,8 @@ describe('dates', function() { ['0122-04-08 08:22', new Date(122, 3, 8, 8, 22)], ['-0098-11-19 23:59:59', new Date(-98, 10, 19, 23, 59, 59)], ['-9730-12-01 12:34:56.789', new Date(-9730, 11, 1, 12, 34, 56, 789)], + // random whitespace before and after gets stripped + ['\r\n\t -9730-12-01 12:34:56.789\r\n\t ', new Date(-9730, 11, 1, 12, 34, 56, 789)], // first century, also allow month, day, and hour to be 1-digit, and not all // three digits of milliseconds ['0013-1-1 1:00:00.6', d1c], @@ -40,9 +42,19 @@ describe('dates', function() { // 2-digit years get mapped to now-70 -> now+29 [thisYear_2 + '-05', new Date(thisYear, 4, 1)], [nowMinus70_2 + '-10-18', new Date(nowMinus70, 9, 18)], - [nowPlus29_2 + '-02-12 14:29:32', new Date(nowPlus29, 1, 12, 14, 29, 32)] + [nowPlus29_2 + '-02-12 14:29:32', new Date(nowPlus29, 1, 12, 14, 29, 32)], + + // including timezone info (that we discard) + ['2014-03-04 08:15Z', new Date(2014, 2, 4, 8, 15)], + ['2014-03-04 08:15:00.00z', new Date(2014, 2, 4, 8, 15)], + ['2014-03-04 08:15:34+1200', new Date(2014, 2, 4, 8, 15, 34)], + ['2014-03-04 08:15:34.567-05:45', new Date(2014, 2, 4, 8, 15, 34, 567)], ].forEach(function(v) { expect(Lib.dateTime2ms(v[0])).toBe(+v[1], v[0]); + + // ISO-8601: all the same stuff with t or T as the separator + expect(Lib.dateTime2ms(v[0].trim().replace(' ', 't'))).toBe(+v[1], v[0].trim().replace(' ', 't')); + expect(Lib.dateTime2ms('\r\n\t ' + v[0].trim().replace(' ', 'T') + '\r\n\t ')).toBe(+v[1], v[0].trim().replace(' ', 'T')); }); }); @@ -105,7 +117,9 @@ describe('dates', function() { '2015-01-00', '2015-01-32', '2015-02-29', '2015-04-31', '2015-01-001', // bad day (incl non-leap year) '2015-01-01 24:00', '2015-01-01 -1:00', '2015-01-01 001:00', // bad hour '2015-01-01 12:60', '2015-01-01 12:-1', '2015-01-01 12:001', '2015-01-01 12:1', // bad minute - '2015-01-01 12:00:60', '2015-01-01 12:00:-1', '2015-01-01 12:00:001', '2015-01-01 12:00:1' // bad second + '2015-01-01 12:00:60', '2015-01-01 12:00:-1', '2015-01-01 12:00:001', '2015-01-01 12:00:1', // bad second + '2015-01-01T', '2015-01-01TT12:34', // bad ISO separators + '2015-01-01Z', '2015-01-01T12Z', '2015-01-01T12:34Z05:00', '2015-01-01 12:34+500', '2015-01-01 12:34-5:00' // bad TZ info ].forEach(function(v) { expect(Lib.dateTime2ms(v)).toBeUndefined(v); }); From bd34ec2b6eff3ad2e315df326651c52cb23f45ea Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 18 Nov 2016 21:46:57 -0500 Subject: [PATCH 2/9] use UTC milliseconds for internal representation of dates We still interpret JS date objects according to the date and hour they have in the local timezone, and for backward compatibility we shift milliseconds (in ranges etc) by the local/UTC offset. --- src/lib/dates.js | 55 +++++++++++++++++-------- src/plots/cartesian/axes.js | 25 +++++------ src/plots/cartesian/dragbox.js | 10 +++-- src/plots/plots.js | 2 +- test/jasmine/tests/lib_date_test.js | 64 +++++++++++++++++++++-------- 5 files changed, 107 insertions(+), 49 deletions(-) diff --git a/src/lib/dates.js b/src/lib/dates.js index 43de12f5719..a486bc797ee 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -87,7 +87,9 @@ var MIN_MS, MAX_MS; exports.dateTime2ms = function(s) { // first check if s is a date object if(exports.isJSDate(s)) { - s = Number(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; if(s >= MIN_MS && s <= MAX_MS) return s; return BADNUM; } @@ -109,14 +111,10 @@ exports.dateTime2ms = function(s) { // javascript takes new Date(0..99,m,d) to mean 1900-1999, so // to support years 0-99 we need to use setFullYear explicitly - var date = new Date(2000, m - 1, d, H, M); - date.setFullYear(y); + var date = new Date(Date.UTC(2000, m - 1, d, H, M)); + date.setUTCFullYear(y); - if(date.getDate() !== d) return BADNUM; - - // does that hour exist in this day? (Daylight time!) - // (TODO: remove this check when we move to UTC) - if(date.getHours() !== H) return BADNUM; + if(date.getUTCDate() !== d) return BADNUM; return date.getTime() + S * ONESEC; }; @@ -150,16 +148,41 @@ exports.ms2DateTime = function(ms, r) { if(!r) r = 0; - var d = new Date(Math.floor(ms)), - dateStr = d3.time.format('%Y-%m-%d')(d), + var msecTenths = Math.round(((ms % 1) + 1) * 10) % 10, + d = new Date(Math.round(ms - msecTenths / 10)), + dateStr = d3.time.format.utc('%Y-%m-%d')(d), // <90 days: add hours and minutes - never *only* add hours - h = (r < NINETYDAYS) ? d.getHours() : 0, - m = (r < NINETYDAYS) ? d.getMinutes() : 0, + h = (r < NINETYDAYS) ? d.getUTCHours() : 0, + m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0, // <3 hours: add seconds - s = (r < THREEHOURS) ? d.getSeconds() : 0, + s = (r < THREEHOURS) ? d.getUTCSeconds() : 0, // <5 minutes: add ms (plus one extra digit, this is msec*10) - msec10 = (r < FIVEMIN) ? Math.round((d.getMilliseconds() + (((ms % 1) + 1) % 1)) * 10) : 0; + msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0; + + return includeTime(dateStr, h, m, s, msec10); +}; + +// For converting old-style milliseconds to date strings, +// we use the local timezone rather than UTC like we use +// everywhere else, both for backward compatibility and +// because that's how people mostly use javasript date objects. +// Clip one extra day off our date range though so we can't get +// thrown beyond the range by the timezone shift. +exports.ms2DateTimeLocal = function(ms) { + if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM; + + var msecTenths = Math.round(((ms % 1) + 1) * 10) % 10, + d = new Date(Math.round(ms - msecTenths / 10)), + dateStr = d3.time.format('%Y-%m-%d')(d), + h = d.getHours(), + m = d.getMinutes(), + s = d.getSeconds(), + msec10 = d.getUTCMilliseconds() * 10 + msecTenths; + + return includeTime(dateStr, h, m, s, msec10); +}; +function includeTime(dateStr, h, m, s, msec10) { // include each part that has nonzero data in or after it if(h || m || s || msec10) { dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2); @@ -176,7 +199,7 @@ exports.ms2DateTime = function(ms, r) { } } return dateStr; -}; +} // normalize date format to date string, in case it starts as // a Date object or milliseconds @@ -186,7 +209,7 @@ exports.cleanDate = function(v, dflt) { // NOTE: if someone puts in a year as a number rather than a string, // this will mistakenly convert it thinking it's milliseconds from 1970 // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds - v = exports.ms2DateTime(+v); + v = exports.ms2DateTimeLocal(+v); if(!v && dflt !== undefined) return dflt; } else if(!exports.isDateTime(v)) { diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 27359e06df9..4026a806b55 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -917,7 +917,7 @@ axes.tickIncrement = function(x, dtick, axrev) { var y = new Date(x); // is this browser consistent? setMonth edits a date but // returns that date's milliseconds - return y.setMonth(y.getMonth() + dtSigned); + return y.setMonth(y.getUTCMonth() + dtSigned); } // Log scales: Linear, Digits @@ -968,9 +968,9 @@ axes.tickFirst = function(ax) { if(tType === 'M') { t0 = new Date(tick0); r0 = new Date(r0); - mdif = (r0.getFullYear() - t0.getFullYear()) * 12 + - r0.getMonth() - t0.getMonth(); - t1 = t0.setMonth(t0.getMonth() + + mdif = (r0.getUTCFullYear() - t0.getUTCFullYear()) * 12 + + r0.getUTCMonth() - t0.getUTCMonth(); + t1 = t0.setMonth(t0.getUTCMonth() + (Math.round(mdif / dtNum) + (axrev ? 1 : -1)) * dtNum); while(axrev ? t1 > r0 : t1 < r0) { @@ -994,12 +994,13 @@ axes.tickFirst = function(ax) { else throw 'unrecognized dtick ' + String(dtick); }; -var yearFormat = d3.time.format('%Y'), - monthFormat = d3.time.format('%b %Y'), - dayFormat = d3.time.format('%b %-d'), - yearMonthDayFormat = d3.time.format('%b %-d, %Y'), - minuteFormat = d3.time.format('%H:%M'), - secondFormat = d3.time.format(':%S'); +var utcFormat = d3.time.format.utc, + yearFormat = utcFormat('%Y'), + monthFormat = utcFormat('%b %Y'), + dayFormat = utcFormat('%b %-d'), + yearMonthDayFormat = utcFormat('%b %-d, %Y'), + minuteFormat = utcFormat('%H:%M'), + secondFormat = utcFormat(':%S'); // add one item to d3's vocabulary: // %{n}f where n is the max number of digits @@ -1012,10 +1013,10 @@ function modDateFormat(fmt, x) { var digits = Math.min(+fm[1] || 6, 6), fracSecs = String((x / 1000 % 1) + 2.0000005) .substr(2, digits).replace(/0+$/, '') || '0'; - return d3.time.format(fmt.replace(fracMatch, fracSecs))(d); + return utcFormat(fmt.replace(fracMatch, fracSecs))(d); } else { - return d3.time.format(fmt)(d); + return utcFormat(fmt)(d); } } diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 0df91d1ec77..2927becef5a 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -298,16 +298,18 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { function zoomAxRanges(axList, r0Fraction, r1Fraction) { var i, axi, - axRangeLinear; + axRangeLinear0, + axRangeLinearSpan; for(i = 0; i < axList.length; i++) { axi = axList[i]; if(axi.fixedrange) continue; - axRangeLinear = axi.range.map(axi.r2l); + axRangeLinear0 = axi._rl[0]; + axRangeLinearSpan = axi._rl[1] - axRangeLinear0; axi.range = [ - axi.l2r(axRangeLinear[0] + (axRangeLinear[1] - axRangeLinear[0]) * r0Fraction), - axi.l2r(axRangeLinear[0] + (axRangeLinear[1] - axRangeLinear[0]) * r1Fraction) + axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction), + axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction) ]; } } diff --git a/src/plots/plots.js b/src/plots/plots.js index b8003c66c81..3ad048addd5 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1390,7 +1390,7 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults) { // convert native dates to date strings... // mostly for external users exporting to plotly - if(Lib.isJSDate(d)) return Lib.ms2DateTime(+d); + if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d); return d; } diff --git a/test/jasmine/tests/lib_date_test.js b/test/jasmine/tests/lib_date_test.js index d0d5c46bb54..43cd2670e88 100644 --- a/test/jasmine/tests/lib_date_test.js +++ b/test/jasmine/tests/lib_date_test.js @@ -18,6 +18,7 @@ describe('dates', function() { describe('dateTime2ms', function() { it('should accept valid date strings', function() { + var tzOffset; [ ['2016', new Date(2016, 0, 1)], @@ -34,10 +35,11 @@ describe('dates', function() { // first century, also allow month, day, and hour to be 1-digit, and not all // three digits of milliseconds ['0013-1-1 1:00:00.6', d1c], - // we support more than 4 digits too, though Date objects don't. More than that + // we support tenths of msec too, though Date objects don't. Smaller than that // and we hit the precision limit of js numbers unless we're close to the epoch. // It won't break though. ['0013-1-1 1:00:00.6001', +d1c + 0.1], + ['0013-1-1 1:00:00.60011111111', +d1c + 0.11111111], // 2-digit years get mapped to now-70 -> now+29 [thisYear_2 + '-05', new Date(thisYear, 4, 1)], @@ -50,11 +52,16 @@ describe('dates', function() { ['2014-03-04 08:15:34+1200', new Date(2014, 2, 4, 8, 15, 34)], ['2014-03-04 08:15:34.567-05:45', new Date(2014, 2, 4, 8, 15, 34, 567)], ].forEach(function(v) { - expect(Lib.dateTime2ms(v[0])).toBe(+v[1], v[0]); + // just for sub-millisecond precision tests, use timezoneoffset + // from the previous date object + if(v[1].getTimezoneOffset) tzOffset = v[1].getTimezoneOffset(); + + var expected = +v[1] - (tzOffset * 60000); + expect(Lib.dateTime2ms(v[0])).toBe(expected, v[0]); // ISO-8601: all the same stuff with t or T as the separator - expect(Lib.dateTime2ms(v[0].trim().replace(' ', 't'))).toBe(+v[1], v[0].trim().replace(' ', 't')); - expect(Lib.dateTime2ms('\r\n\t ' + v[0].trim().replace(' ', 'T') + '\r\n\t ')).toBe(+v[1], v[0].trim().replace(' ', 'T')); + expect(Lib.dateTime2ms(v[0].trim().replace(' ', 't'))).toBe(expected, v[0].trim().replace(' ', 't')); + expect(Lib.dateTime2ms('\r\n\t ' + v[0].trim().replace(' ', 'T') + '\r\n\t ')).toBe(expected, v[0].trim().replace(' ', 'T')); }); }); @@ -69,7 +76,7 @@ describe('dates', function() { [ 1000, 9999, -1000, -9999 ].forEach(function(v) { - expect(Lib.dateTime2ms(v)).toBe(+(new Date(v, 0, 1)), v); + expect(Lib.dateTime2ms(v)).toBe(Date.UTC(v, 0, 1), v); }); [ @@ -78,7 +85,7 @@ describe('dates', function() { [nowMinus70_2, nowMinus70], [99, 1999] ].forEach(function(v) { - expect(Lib.dateTime2ms(v[0])).toBe(+(new Date(v[1], 0, 1)), v[0]); + expect(Lib.dateTime2ms(v[0])).toBe(Date.UTC(v[1], 0, 1), v[0]); }); }); @@ -93,7 +100,7 @@ describe('dates', function() { d1c, new Date(2015, 8, 7, 23, 34, 45, 567) ].forEach(function(v) { - expect(Lib.dateTime2ms(v)).toBe(+v); + expect(Lib.dateTime2ms(v)).toBe(+v - v.getTimezoneOffset() * 60000); }); }); @@ -124,6 +131,30 @@ describe('dates', function() { expect(Lib.dateTime2ms(v)).toBeUndefined(v); }); }); + + var JULY1MS = 181 * 24 * 3600 * 1000; + + it('should use UTC with no timezone offset or daylight saving time', function() { + expect(Lib.dateTime2ms('1970-01-01')).toBe(0); + + // 181 days (and no DST hours) between jan 1 and july 1 in a non-leap-year + // 31 + 28 + 31 + 30 + 31 + 30 + expect(Lib.dateTime2ms('1970-07-01')).toBe(JULY1MS); + }); + + it('should interpret JS dates by local time, not by its getTime()', function() { + // not really part of the test, just to make sure the test is meaningful + // the test should NOT be run in a UTC environment + expect([ + Number(new Date(1970, 0, 1)), + Number(new Date(1970, 6, 1)) + ]).not.toEqual([0, JULY1MS]); + + // now repeat the previous test and show that we throw away + // timezone info from js dates + expect(Lib.dateTime2ms(new Date(1970, 0, 1))).toBe(0); + expect(Lib.dateTime2ms(new Date(1970, 6, 1))).toBe(JULY1MS); + }); }); describe('ms2DateTime', function() { @@ -159,8 +190,8 @@ describe('dates', function() { it('should not accept Date objects beyond our limits or other objects', function() { [ - +(new Date(10000, 0, 1)), - +(new Date(-10000, 11, 31, 23, 59, 59, 999)), + Date.UTC(10000, 0, 1), + Date.UTC(-10000, 11, 31, 23, 59, 59, 999), '', '2016-01-01', '0', @@ -191,19 +222,20 @@ describe('dates', function() { }); describe('cleanDate', function() { - it('should convert any number or js Date within range to a date string', function() { + it('should convert numbers or js Dates to strings based on local TZ', function() { [ new Date(0), new Date(2000), new Date(2000, 0, 1), new Date(), - new Date(-9999, 0, 1), - new Date(9999, 11, 31, 23, 59, 59, 999) + new Date(-9999, 0, 3), // we lose one day of range +/- tzoffset this way + new Date(9999, 11, 29, 23, 59, 59, 999) ].forEach(function(v) { - expect(typeof Lib.ms2DateTime(+v)).toBe('string'); - expect(Lib.cleanDate(v)).toBe(Lib.ms2DateTime(+v)); - expect(Lib.cleanDate(+v)).toBe(Lib.ms2DateTime(+v)); - expect(Lib.cleanDate(v, '2000-01-01')).toBe(Lib.ms2DateTime(+v)); + var expected = Lib.ms2DateTime(Lib.dateTime2ms(v)); + expect(typeof expected).toBe('string'); + expect(Lib.cleanDate(v)).toBe(expected); + expect(Lib.cleanDate(+v)).toBe(expected); + expect(Lib.cleanDate(v, '2000-01-01')).toBe(expected); }); }); From 9c645e7b61058a908cc37b769fc9b12a01946461 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 18 Nov 2016 22:19:56 -0500 Subject: [PATCH 3/9] if it works in Alaska it'll work anywhere. --- circle.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/circle.yml b/circle.yml index 085c303b25c..584761eefcb 100644 --- a/circle.yml +++ b/circle.yml @@ -6,6 +6,8 @@ general: machine: node: version: 6.1.0 + timezone: + America/Anchorage services: - docker From 6d83c7bad91db900078be05d3ee7d81e91bca053 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 18 Nov 2016 22:24:45 -0500 Subject: [PATCH 4/9] verify our test environment has DST --- test/jasmine/tests/lib_date_test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/jasmine/tests/lib_date_test.js b/test/jasmine/tests/lib_date_test.js index 43cd2670e88..a73f79c7b25 100644 --- a/test/jasmine/tests/lib_date_test.js +++ b/test/jasmine/tests/lib_date_test.js @@ -145,10 +145,13 @@ describe('dates', function() { it('should interpret JS dates by local time, not by its getTime()', function() { // not really part of the test, just to make sure the test is meaningful // the test should NOT be run in a UTC environment - expect([ - Number(new Date(1970, 0, 1)), - Number(new Date(1970, 6, 1)) - ]).not.toEqual([0, JULY1MS]); + var local0 = Number(new Date(1970, 0, 1)), + localjuly1 = Number(new Date(1970, 6, 1)); + expect([local0, localjuly1]).not.toEqual([0, JULY1MS], + 'test must not run in UTC'); + // verify that there *is* daylight saving time in the test environment + expect(localjuly1 - local0).not.toEqual(JULY1MS - 0, + 'test must run in a timezone with DST'); // now repeat the previous test and show that we throw away // timezone info from js dates From 02e98a9380d32a00836f0d749a1f9cf14815f48f Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 19 Nov 2016 00:13:10 -0500 Subject: [PATCH 5/9] fix range selectors for UTC, and tests to run in arbitrary timezone --- src/components/rangeselector/get_update_object.js | 6 +++--- src/lib/index.js | 1 + test/jasmine/tests/annotations_test.js | 5 ++++- test/jasmine/tests/axes_test.js | 2 +- test/jasmine/tests/shapes_test.js | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/rangeselector/get_update_object.js b/src/components/rangeselector/get_update_object.js index fe6eacaa454..71978d0a310 100644 --- a/src/components/rangeselector/get_update_object.js +++ b/src/components/rangeselector/get_update_object.js @@ -42,13 +42,13 @@ function getXRange(axisLayout, buttonLayout) { switch(buttonLayout.stepmode) { case 'backward': - range0 = Lib.ms2DateTime(+d3.time[step].offset(base, -count)); + range0 = Lib.ms2DateTime(+d3.time[step].utc.offset(base, -count)); break; case 'todate': - var base2 = d3.time[step].offset(base, -count); + var base2 = d3.time[step].utc.offset(base, -count); - range0 = Lib.ms2DateTime(+d3.time[step].ceil(base2)); + range0 = Lib.ms2DateTime(+d3.time[step].utc.ceil(base2)); break; } diff --git a/src/lib/index.js b/src/lib/index.js index 3239693a4d7..023896b0e80 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -28,6 +28,7 @@ var datesModule = require('./dates'); lib.dateTime2ms = datesModule.dateTime2ms; lib.isDateTime = datesModule.isDateTime; lib.ms2DateTime = datesModule.ms2DateTime; +lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal; lib.cleanDate = datesModule.cleanDate; lib.isJSDate = datesModule.isJSDate; lib.MIN_MS = datesModule.MIN_MS; diff --git a/test/jasmine/tests/annotations_test.js b/test/jasmine/tests/annotations_test.js index f771fdcca6c..da3e6a04cb0 100644 --- a/test/jasmine/tests/annotations_test.js +++ b/test/jasmine/tests/annotations_test.js @@ -71,7 +71,10 @@ describe('Test annotations', function() { axref: 'x', ayref: 'y', x: '2008-07-01', - ax: Dates.dateTime2ms('2004-07-01'), + // note this is not portable: this generates ms in the local + // timezone, so will work correctly where it was created but + // not if the milliseconds number is moved to another TZ + ax: +(new Date(2004, 6, 1)), y: 0, ay: 50 }] diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 90433bd53af..442e3781782 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -678,7 +678,7 @@ describe('Test axes', function() { it('should handle tick0 and dtick for date axes', function() { var someMs = 123456789, - someMsDate = Lib.ms2DateTime(someMs), + someMsDate = Lib.ms2DateTimeLocal(someMs), oneDay = 24 * 3600 * 1000, axIn = {tick0: someMs, dtick: String(3 * oneDay)}, axOut = {}; diff --git a/test/jasmine/tests/shapes_test.js b/test/jasmine/tests/shapes_test.js index 006675bd490..43714ebd8df 100644 --- a/test/jasmine/tests/shapes_test.js +++ b/test/jasmine/tests/shapes_test.js @@ -489,7 +489,7 @@ describe('Test shapes', function() { title: 'linked to date and category axes', xaxis: { type: 'date', - range: ['2000-01-01', (new Date(2000, 1, 2)).getTime()] + range: ['2000-01-01', '2000-02-02'] }, yaxis: { type: 'category', range: ['a', 'b'] } } From c41a0b45790a6af99bd4b7d3b3997fbaebf662c0 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 19 Nov 2016 00:35:35 -0500 Subject: [PATCH 6/9] lint --- test/jasmine/tests/annotations_test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/jasmine/tests/annotations_test.js b/test/jasmine/tests/annotations_test.js index da3e6a04cb0..640614133e8 100644 --- a/test/jasmine/tests/annotations_test.js +++ b/test/jasmine/tests/annotations_test.js @@ -3,7 +3,6 @@ var Annotations = require('@src/components/annotations'); var Plotly = require('@lib/index'); var Plots = require('@src/plots/plots'); var Lib = require('@src/lib'); -var Dates = require('@src/lib/dates'); var Axes = require('@src/plots/cartesian/axes'); var d3 = require('d3'); From fa0dd3836bf9a7948aa4740aa2e3c781a4a4a247 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 23 Nov 2016 18:21:30 -0500 Subject: [PATCH 7/9] fix one more UTC bug and update new tests to UTC --- src/plots/cartesian/axes.js | 6 +++--- src/plots/cartesian/set_convert.js | 4 ++-- test/jasmine/tests/axes_test.js | 22 +++++++++++----------- test/jasmine/tests/histogram_test.js | 8 +++----- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 1c81b7ecf2e..559a5574f1c 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -931,9 +931,9 @@ axes.tickIncrement = function(x, dtick, axrev) { // Dates: months (or years) if(tType === 'M') { var y = new Date(x); - // is this browser consistent? setMonth edits a date but + // is this browser consistent? setUTCMonth edits a date but // returns that date's milliseconds - return y.setMonth(y.getUTCMonth() + dtSigned); + return y.setUTCMonth(y.getUTCMonth() + dtSigned); } // Log scales: Linear, Digits @@ -986,7 +986,7 @@ axes.tickFirst = function(ax) { r0 = new Date(r0); mdif = (r0.getUTCFullYear() - t0.getUTCFullYear()) * 12 + r0.getUTCMonth() - t0.getUTCMonth(); - t1 = t0.setMonth(t0.getUTCMonth() + + t1 = t0.setUTCMonth(t0.getUTCMonth() + (Math.round(mdif / dtNum) + (axrev ? 1 : -1)) * dtNum); while(axrev ? t1 > r0 : t1 < r0) { diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 69a24545b40..377c638ecf5 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -265,10 +265,10 @@ module.exports = function setConvert(ax) { // NOTE: Changed this behavior: previously we took any numeric value // to be a ms, even if it was a string that could be a bare year. // Now we convert it as a date if at all possible, and only try - // as ms if that fails. + // as (local) ms if that fails. var ms = Lib.dateTime2ms(v); if(ms === BADNUM) { - if(isNumeric(v)) ms = Number(v); + if(isNumeric(v)) ms = Lib.dateTime2ms(new Date(v)); else return BADNUM; } return Lib.constrain(ms, Lib.MIN_MS, Lib.MAX_MS); diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 724d02b0d03..7378c50b376 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -1402,7 +1402,7 @@ describe('Test axes', function() { } function mockHoverText(ax, x) { - var xCalc = (ax.d2l_noadd || ax.d2c)(x); + var xCalc = (ax.d2l_noadd || ax.d2l)(x); var tickTextObj = Axes.tickText(ax, xCalc, true); return tickTextObj.text; } @@ -1433,7 +1433,7 @@ describe('Test axes', function() { 'Feb 12' ]; expect(textOut).toEqual(expectedText); - expect(mockHoverText(ax, ax.d2c('1999-12-18 15:34:33.3'))) + expect(mockHoverText(ax, '1999-12-18 15:34:33.3')) .toBe('Dec 18, 1999, 15:34'); ax = { @@ -1454,7 +1454,7 @@ describe('Test axes', function() { '00:00
Jan 6, 2000' ]; expect(textOut).toEqual(expectedText); - expect(mockHoverText(ax, ax.d2c('2000-01-04 15:34:33.3'))) + expect(mockHoverText(ax, '2000-01-04 15:34:33.3')) .toBe('Jan 4, 2000, 15:34:33'); ax = { @@ -1475,9 +1475,9 @@ describe('Test axes', function() { '00:00:02' ]; expect(textOut).toEqual(expectedText); - expect(mockHoverText(ax, ax.d2c('2000-02-04 00:00:00.123456'))) + expect(mockHoverText(ax, '2000-02-04 00:00:00.123456')) .toBe('Feb 4, 2000, 00:00:00.1235'); - expect(mockHoverText(ax, ax.d2c('2000-02-04 00:00:00'))) + expect(mockHoverText(ax, '2000-02-04 00:00:00')) .toBe('Feb 4, 2000'); }); @@ -1500,9 +1500,9 @@ describe('Test axes', function() { '00:05
Feb 12, 2000' ]; expect(textOut).toEqual(expectedText); - expect(mockHoverText(ax, ax.d2c('2000-02-04 00:00:00.123456'))) + expect(mockHoverText(ax, '2000-02-04 00:00:00.123456')) .toBe('Feb 4, 2000'); - expect(mockHoverText(ax, ax.d2c('2000-02-04 00:00:05.123456'))) + expect(mockHoverText(ax, '2000-02-04 00:00:05.123456')) .toBe('Feb 4, 2000, 00:00:05'); }); @@ -1559,9 +1559,9 @@ describe('Test axes', function() { '00:00:01
Jan 1, 2013' ]; expect(textOut).toEqual(expectedText); - expect(mockHoverText(ax, ax.d2c('2012-01-01'))) + expect(mockHoverText(ax, '2012-01-01')) .toBe('New year'); - expect(mockHoverText(ax, ax.d2c('2012-01-01 12:34:56.1234'))) + expect(mockHoverText(ax, '2012-01-01 12:34:56.1234')) .toBe('Jan 1, 2012, 12:34:56'); }); @@ -1587,8 +1587,8 @@ describe('Test axes', function() { // 10 and 0.1 are off scale ]; expect(textOut).toEqual(expectedText, axType); - expect(mockHoverText(ax, ax.c2l(1))).toBe('One'); - expect(mockHoverText(ax, ax.c2l(19.999))).toBe('19.999'); + expect(mockHoverText(ax, 1)).toBe('One'); + expect(mockHoverText(ax, 19.999)).toBe('19.999'); }); }); diff --git a/test/jasmine/tests/histogram_test.js b/test/jasmine/tests/histogram_test.js index 5263835f67f..7305c8a1d37 100644 --- a/test/jasmine/tests/histogram_test.js +++ b/test/jasmine/tests/histogram_test.js @@ -146,9 +146,7 @@ describe('Test histogram', function() { return out; } - // remove tzOffset when we move to UTC - var tzOffset = (new Date(1970, 0, 1)).getTimezoneOffset() * 60000, - oneDay = 24 * 3600000; + var oneDay = 24 * 3600000; it('should handle auto dates with nonuniform (month) bins', function() { var out = _calc({ @@ -165,7 +163,7 @@ describe('Test histogram', function() { // bars. Now that we have explicit per-bar positioning, perhaps // we should fill the space, rather than insisting on equal-width // bars? - var x0 = tzOffset + 15768000000, + var x0 = 15768000000, x1 = x0 + oneDay * 365, x2 = x1 + oneDay * 365.5, x3 = x2 + oneDay * 365.5; @@ -186,7 +184,7 @@ describe('Test histogram', function() { nbinsx: 4 }); - var x0 = tzOffset, + var x0 = 0, x1 = x0 + oneDay, x2 = x1 + oneDay, x3 = x2 + oneDay; From c5c823be4dca28f6730b07760f4ff8a989d363c5 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 23 Nov 2016 18:47:52 -0500 Subject: [PATCH 8/9] one more test to update for UTC --- test/jasmine/tests/histogram2d_test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index 45620c68a92..51ad0c4ae4d 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -76,9 +76,7 @@ describe('Test histogram2d', function() { } // remove tzJan/tzJuly when we move to UTC - var oneDay = 24 * 3600000, - tzJan = (new Date(1970, 0, 1)).getTimezoneOffset(), - tzJuly = (new Date(1970, 6, 1)).getTimezoneOffset(); + var oneDay = 24 * 3600000; it('should handle both uniform and nonuniform date bins', function() { var out = _calc({ @@ -97,7 +95,7 @@ describe('Test histogram2d', function() { // lets also make it display the bins with nonuniform size, // and ensure we don't generate an extra bin on the end (see // first row of z below) - expect(out.y0).toBe(tzJan === tzJuly ? '1969-07-02 14:24' : '1969-07-02 15:24'); + expect(out.y0).toBe('1969-07-02 14:24'); expect(out.dy).toBe(365.2 * oneDay); expect(out.z).toEqual([ From 87786480858e8f54532ff2a9330697ff8a3bf076 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 25 Nov 2016 17:50:29 -0500 Subject: [PATCH 9/9] more thorough date_axes mock with a variety of scales --- test/image/baselines/date_axes.png | Bin 22085 -> 41269 bytes test/image/mocks/date_axes.json | 91 ++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/test/image/baselines/date_axes.png b/test/image/baselines/date_axes.png index 7d330eadcb2966c61ff384ed643af08b0d587679..bc7a8e367bc62f2328cda9e9a881051417f6af6f 100644 GIT binary patch literal 41269 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 literal 22085 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