diff --git a/src/component/axis/AxisBuilder.js b/src/component/axis/AxisBuilder.js index 94e799db0a921a8d8d31e3797fdcbcc3f0e8dd10..a38fa0dbda3d677fb11d6ec0202e399201f52448 100644 --- a/src/component/axis/AxisBuilder.js +++ b/src/component/axis/AxisBuilder.js @@ -284,7 +284,7 @@ define(function (require) { tickCoord, opt.labelOffset + opt.labelDirection * labelMargin ]; - var labelBeforeFormat = axis.scale.getLabel(tickVal); + var labelStr = axis.scale.getLabel(tickVal); var textEl = new graphic.Text({ @@ -296,7 +296,17 @@ define(function (require) { textAlign: itemTextStyleModel.get('align', true) || labelLayout.textAlign, textVerticalAlign: itemTextStyleModel.get('baseline', true) || labelLayout.verticalAlign, textFont: itemTextStyleModel.getFont(), - fill: typeof textColor === 'function' ? textColor(labelBeforeFormat) : textColor + fill: typeof textColor === 'function' + ? textColor( + // Compatible with previous version, which always returns labelStr. + // But in interval scale labelStr is like '223,445', which maked + // user repalce ','. So we modify it to return original val but remain + // it as 'string' to avoid error in replacing. + axis.type === 'category' ? labelStr : axis.type === 'value' ? tickVal + '' : tickVal, + index, + {value: tickVal, index: index, label: labelStr} + ) + : textColor }, position: pos, rotation: labelLayout.rotation, @@ -308,7 +318,7 @@ define(function (require) { if (triggerEvent) { textEl.eventData = makeAxisEventDataBase(axisModel); textEl.eventData.targetType = 'axisLabel'; - textEl.eventData.value = labelBeforeFormat; + textEl.eventData.value = labelStr; } // FIXME diff --git a/src/coord/axisHelper.js b/src/coord/axisHelper.js index c6e44c3b1b2d40aeb5a980f2f0cc0214aa546db5..6a25eef10d2e5a4b337fa5e156052e0dc671a8dc 100644 --- a/src/coord/axisHelper.js +++ b/src/coord/axisHelper.js @@ -228,9 +228,12 @@ define(function (require) { } else if (typeof labelFormatter === 'function') { return zrUtil.map(ticks, function (tick, idx) { + var labelStr = scale.getLabel(tick); return labelFormatter( - axis.type === 'category' ? scale.getLabel(tick) : tick, - idx + // Have to be compatible with previous version. + axis.type === 'category' ? labelStr : tick, + idx, + {value: tick, index: idx, label: labelStr} ); }, this); } diff --git a/src/layout/points.js b/src/layout/points.js index 04f82b952ff90c8a92996b7b401106d803a66506..22693e45c6b63634968c8e32d1076f07e7329535 100644 --- a/src/layout/points.js +++ b/src/layout/points.js @@ -1,12 +1,16 @@ define(function (require) { + var zrUtil = require('zrender/core/util'); + return function (seriesType, ecModel) { ecModel.eachSeriesByType(seriesType, function (seriesModel) { var data = seriesModel.getData(); var coordSys = seriesModel.coordinateSystem; if (coordSys) { - var dims = coordSys.dimensions; + var dims = zrUtil.map(coordSys.dimensions, function (dimItem) { + return zrUtil.isObject(dimItem) ? dimItem.name : dimItem; + }); if (dims.length === 1) { data.each(dims[0], function (x, idx) { diff --git a/src/util/format.js b/src/util/format.js index 437a1b9429f9662cabfdd6a28a78e4d993cf97a7..47a420e201ae5d05ed779b21f8d7e88483f30a65 100644 --- a/src/util/format.js +++ b/src/util/format.js @@ -123,9 +123,15 @@ define(function (require) { * ISO Date format * @param {string} tpl * @param {number} value + * @param {boolean} [isLocal=false] Default use UTC + * Why default UTC? In most case, time provided by user is + * understood in UTC. For example, new Date('2012-01-01') + * or a string '2012-01-01' or a timestamp. So it is + * recommended to format time in UTC. + * (see `echarts/util/number.js#parseDate`); * @inner */ - formatUtil.formatTime = function (tpl, value) { + formatUtil.formatTime = function (tpl, value, isLocal) { if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' @@ -136,12 +142,13 @@ define(function (require) { } var date = numberUtil.parseDate(value); - var y = date.getFullYear(); - var M = date.getMonth() + 1; - var d = date.getDate(); - var h = date.getHours(); - var m = date.getMinutes(); - var s = date.getSeconds(); + var utc = isLocal ? '' : 'UTC'; + var y = date['get' + utc + 'FullYear'](); + var M = date['get' + utc + 'Month']() + 1; + var d = date['get' + utc + 'Date'](); + var h = date['get' + utc + 'Hours'](); + var m = date['get' + utc + 'Minutes'](); + var s = date['get' + utc + 'Seconds'](); tpl = tpl.replace('MM', s2d(M)) .toLowerCase() diff --git a/src/util/model.js b/src/util/model.js index ee1bb7ac0de29fe2cd0cfa20e35f50c30e8ca2c9..f6f521f723947baed423d66b5e9cb9a773912132 100644 --- a/src/util/model.js +++ b/src/util/model.js @@ -92,7 +92,12 @@ define(function(require) { return value; } - if (dimType === 'time' && !isFinite(value) && value != null && value !== '-') { + if (dimType === 'time' + // spead up when using timestamp + && typeof value !== 'number' + && value != null + && value !== '-' + ) { value = +nubmerUtil.parseDate(value); } diff --git a/src/util/number.js b/src/util/number.js index 1b5bb4181d257fba26482742d54f11b0caf37cde..c5de8a1a6dcead1d9683a056f18df66947db1315 100644 --- a/src/util/number.js +++ b/src/util/number.js @@ -190,8 +190,22 @@ define(function (require) { return val > -RADIAN_EPSILON && val < RADIAN_EPSILON; }; + var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(?:Z|([\+\-]\d\d):?\d\d)?)?)?)?)?$/; // jshint ignore:line + var TIMEZONE_OFFSET = (new Date()).getTimezoneOffset(); + /** - * @param {string|Date|number} value + * @param {string|Date|number} value These values can be accepted: + * + An instance of Date, represent a time in its own time zone. + * + Or string in a subset of ISO 8601, only including: + * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06', + * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123', + * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00', + * all of which will be treated as they reperent a time in UTC + * if time zone is not specified. + * + Or other string format, including: + * '2012', '2012-3-1', '2012/3/1', '2012/03/01', + * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123' + * + a timestamp, which represent a time in UTC. * @return {Date} date */ number.parseDate = function (value) { @@ -199,13 +213,32 @@ define(function (require) { return value; } else if (typeof value === 'string') { - // Treat as ISO format. See issue #3623 - var ret = new Date(value); - if (isNaN(+ret)) { - // FIXME new Date('1970-01-01') is UTC, new Date('1970/01/01') is local - ret = new Date(new Date(value.replace(/-/g, '/')) - new Date('1970/01/01')); + // Different browsers parse date in different way, so we parse it manually. + // Some other issues: + // new Date('1970-01-01') is UTC, + // new Date('1970/01/01') and new Date('1970-1-01') is local. + // See issue #3623 + var match = TIME_REG.exec(value); + + if (!match) { + // return Invalid Date. + return new Date(NaN); } - return ret; + + // match[n] can only be string or undefined. + // But take care of '12' + 1 => '121'. + return new Date( + +match[1], + +(match[2] || 1) - 1, + +match[3] || 1, + +match[4] || 0, + +(match[5] || 0) - (match[8] || 0) * 60 - TIMEZONE_OFFSET, + +match[6] || 0, + +match[7] || 0 + ); + } + else if (value == null) { + return new Date(NaN); } return new Date(Math.round(value)); diff --git a/test/ut/spec/util/number.js b/test/ut/spec/util/number.js index b0ad95dc000160305c27e7bd3e21804f6597aa77..76b93d60caeae7c3acbf4ebf2f3ab2ab1e010e1b 100755 --- a/test/ut/spec/util/number.js +++ b/test/ut/spec/util/number.js @@ -193,8 +193,59 @@ describe('util/number', function () { var result = numberUtil.linearMap(40, [43.55454545, 43.55454545], range, clamp); expect(result).toEqual(1221212.1221372238); } - }) + }); + }); + describe('parseDate', function () { + testCase('parseDate', function (numberUtil) { + + // Invalid Date + expect('' + numberUtil.parseDate(null)).toEqual('Invalid Date'); + expect('' + numberUtil.parseDate(void 0)).toEqual('Invalid Date'); + expect('' + numberUtil.parseDate('asdf')).toEqual('Invalid Date'); + expect('' + numberUtil.parseDate(NaN)).toEqual('Invalid Date'); + expect('' + numberUtil.parseDate('-')).toEqual('Invalid Date'); + expect('' + numberUtil.parseDate('20120304')).toEqual('Invalid Date'); + + // Input instance of Date or timestamp + expect(+numberUtil.parseDate(new Date('2012-03-04'))).toEqual(1330819200000); + expect(+numberUtil.parseDate(1330819200000)).toEqual(1330819200000); + expect(+numberUtil.parseDate(1330819199999.99)).toEqual(1330819200000); + expect(+numberUtil.parseDate(1330819200000.01)).toEqual(1330819200000); + + // ISO string + expect(+numberUtil.parseDate('2012-03')).toEqual(1330560000000); + expect(+numberUtil.parseDate('2012-03-04')).toEqual(1330819200000); + expect(+numberUtil.parseDate('2012-03-04 05')).toEqual(1330837200000); + expect(+numberUtil.parseDate('2012-03-04T05')).toEqual(1330837200000); + expect(+numberUtil.parseDate('2012-03-04 05:06')).toEqual(1330837560000); + expect(+numberUtil.parseDate('2012-03-04T05:06')).toEqual(1330837560000); + expect(+numberUtil.parseDate('2012-03-04 05:06:07')).toEqual(1330837567000); + expect(+numberUtil.parseDate('2012-03-04T05:06:07')).toEqual(1330837567000); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.123')).toEqual(1330837567123); + expect(+numberUtil.parseDate('2012-03-04T05:06:07,123')).toEqual(1330837567123); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.12')).toEqual(1330837567012); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.1')).toEqual(1330837567001); + expect(+numberUtil.parseDate('2012-03-04T05:06:07,123Z')).toEqual(1330837567123); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.123+0800')).toEqual(1330808767123); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.123+08:00')).toEqual(1330808767123); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.123-0700')).toEqual(1330862767123); + expect(+numberUtil.parseDate('2012-03-04T05:06:07.123-07:00')).toEqual(1330862767123); + + // Other string + expect(+numberUtil.parseDate('2012')).toEqual(1325376000000); + expect(+numberUtil.parseDate('2012/03')).toEqual(1330560000000); + expect(+numberUtil.parseDate('2012/03/04')).toEqual(1330819200000); + expect(+numberUtil.parseDate('2012-3-4')).toEqual(1330819200000); + expect(+numberUtil.parseDate('2012/3')).toEqual(1330560000000); + expect(+numberUtil.parseDate('2012/3/4')).toEqual(1330819200000); + expect(+numberUtil.parseDate('2012/3/4 2:05')).toEqual(1330826700000); + expect(+numberUtil.parseDate('2012/03/04 2:05')).toEqual(1330826700000); + expect(+numberUtil.parseDate('2012/3/4 2:05:08')).toEqual(1330826708000); + expect(+numberUtil.parseDate('2012/03/04 2:05:08')).toEqual(1330826708000); + expect(+numberUtil.parseDate('2012/3/4 2:05:08.123')).toEqual(1330826708123); + expect(+numberUtil.parseDate('2012/03/04 2:05:08.123')).toEqual(1330826708123); + }); });