blob: e537ef0f3edc120a79835369965f79d4a5ca2d74 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* The `scaleLevels` references to d3.js. The use of the source
* code of this file is also subject to the terms and consitions
* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>).
*/
// [About UTC and local time zone]:
// In most cases, `number.parseDate` will treat input data string as local time
// (except time zone is specified in time string). And `format.formateTime` returns
// local time by default. option.useUTC is false by default. This design have
// concidered these common case:
// (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
// in local time by default.
// (2) By default, the input data string (e.g., '2011-01-02') should be displayed
// as its original time, without any time difference.
import * as zrUtil from 'zrender/src/core/util';
import * as numberUtil from '../util/number';
import * as formatUtil from '../util/format';
import * as scaleHelper from './helper';
import IntervalScale from './Interval';
var intervalScaleProto = IntervalScale.prototype;
var mathCeil = Math.ceil;
var mathFloor = Math.floor;
var ONE_SECOND = 1000;
var ONE_MINUTE = ONE_SECOND * 60;
var ONE_HOUR = ONE_MINUTE * 60;
var ONE_DAY = ONE_HOUR * 24; // FIXME 公用?
var bisect = function (a, x, lo, hi) {
while (lo < hi) {
var mid = lo + hi >>> 1;
if (a[mid][1] < x) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
};
/**
* @alias module:echarts/coord/scale/Time
* @constructor
*/
var TimeScale = IntervalScale.extend({
type: 'time',
/**
* @override
*/
getLabel: function (val) {
var stepLvl = this._stepLvl;
var date = new Date(val);
return formatUtil.formatTime(stepLvl[0], date, this.getSetting('useUTC'));
},
/**
* @override
*/
niceExtent: function (opt) {
var extent = this._extent; // If extent start and end are same, expand them
if (extent[0] === extent[1]) {
// Expand extent
extent[0] -= ONE_DAY;
extent[1] += ONE_DAY;
} // If there are no data and extent are [Infinity, -Infinity]
if (extent[1] === -Infinity && extent[0] === Infinity) {
var d = new Date();
extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
extent[0] = extent[1] - ONE_DAY;
}
this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // var extent = this._extent;
var interval = this._interval;
if (!opt.fixMin) {
extent[0] = numberUtil.round(mathFloor(extent[0] / interval) * interval);
}
if (!opt.fixMax) {
extent[1] = numberUtil.round(mathCeil(extent[1] / interval) * interval);
}
},
/**
* @override
*/
niceTicks: function (approxTickNum, minInterval, maxInterval) {
approxTickNum = approxTickNum || 10;
var extent = this._extent;
var span = extent[1] - extent[0];
var approxInterval = span / approxTickNum;
if (minInterval != null && approxInterval < minInterval) {
approxInterval = minInterval;
}
if (maxInterval != null && approxInterval > maxInterval) {
approxInterval = maxInterval;
}
var scaleLevelsLen = scaleLevels.length;
var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
var interval = level[1]; // Same with interval scale if span is much larger than 1 year
if (level[0] === 'year') {
var yearSpan = span / interval; // From "Nice Numbers for Graph Labels" of Graphic Gems
// var niceYearSpan = numberUtil.nice(yearSpan, false);
var yearStep = numberUtil.nice(yearSpan / approxTickNum, true);
interval *= yearStep;
}
var timezoneOffset = this.getSetting('useUTC') ? 0 : new Date(+extent[0] || +extent[1]).getTimezoneOffset() * 60 * 1000;
var niceExtent = [Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset), Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)];
scaleHelper.fixExtent(niceExtent, extent);
this._stepLvl = level; // Interval will be used in getTicks
this._interval = interval;
this._niceExtent = niceExtent;
},
parse: function (val) {
// val might be float.
return +numberUtil.parseDate(val);
}
});
zrUtil.each(['contain', 'normalize'], function (methodName) {
TimeScale.prototype[methodName] = function (val) {
return intervalScaleProto[methodName].call(this, this.parse(val));
};
}); // Steps from d3, see the license statement at the top of this file.
var scaleLevels = [// Format interval
['hh:mm:ss', ONE_SECOND], // 1s
['hh:mm:ss', ONE_SECOND * 5], // 5s
['hh:mm:ss', ONE_SECOND * 10], // 10s
['hh:mm:ss', ONE_SECOND * 15], // 15s
['hh:mm:ss', ONE_SECOND * 30], // 30s
['hh:mm\nMM-dd', ONE_MINUTE], // 1m
['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m
['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
['hh:mm\nMM-dd', ONE_HOUR], // 1h
['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h
['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h
['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h
['MM-dd\nyyyy', ONE_DAY], // 1d
['MM-dd\nyyyy', ONE_DAY * 2], // 2d
['MM-dd\nyyyy', ONE_DAY * 3], // 3d
['MM-dd\nyyyy', ONE_DAY * 4], // 4d
['MM-dd\nyyyy', ONE_DAY * 5], // 5d
['MM-dd\nyyyy', ONE_DAY * 6], // 6d
['week', ONE_DAY * 7], // 7d
['MM-dd\nyyyy', ONE_DAY * 10], // 10d
['week', ONE_DAY * 14], // 2w
['week', ONE_DAY * 21], // 3w
['month', ONE_DAY * 31], // 1M
['week', ONE_DAY * 42], // 6w
['month', ONE_DAY * 62], // 2M
['week', ONE_DAY * 70], // 10w
['quarter', ONE_DAY * 95], // 3M
['month', ONE_DAY * 31 * 4], // 4M
['month', ONE_DAY * 31 * 5], // 5M
['half-year', ONE_DAY * 380 / 2], // 6M
['month', ONE_DAY * 31 * 8], // 8M
['month', ONE_DAY * 31 * 10], // 10M
['year', ONE_DAY * 380] // 1Y
];
/**
* @param {module:echarts/model/Model}
* @return {module:echarts/scale/Time}
*/
TimeScale.create = function (model) {
return new TimeScale({
useUTC: model.ecModel.get('useUTC')
});
};
export default TimeScale;