/* * 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. */ /** * AUTO-GENERATED FILE. DO NOT MODIFY. */ /* * 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. */ import { __extends } from "tslib"; /* * A third-party license is embedded for some of the code in this file: * The "scaleLevels" was originally copied from "d3.js" with some * modifications made for this project. * (See more details in the comment on the definition of "scaleLevels" below.) * The use of the source code of this file is also subject to the terms * and consitions of the license of "d3.js" (BSD-3Clause, see * ). */ // [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 has // considered these common cases: // (1) Time that is persistent in server is in UTC, but it is needed to be displayed // 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 numberUtil from '../util/number.js'; import { ONE_SECOND, ONE_MINUTE, ONE_HOUR, ONE_DAY, ONE_YEAR, format, leveledFormat, getUnitValue, timeUnits, fullLeveledFormatter, getPrimaryTimeUnit, isPrimaryTimeUnit, getDefaultFormatPrecisionOfInterval, fullYearGetterName, monthSetterName, fullYearSetterName, dateSetterName, hoursGetterName, hoursSetterName, minutesSetterName, secondsSetterName, millisecondsSetterName, monthGetterName, dateGetterName, minutesGetterName, secondsGetterName, millisecondsGetterName } from '../util/time.js'; import * as scaleHelper from './helper.js'; import IntervalScale from './Interval.js'; import Scale from './Scale.js'; import { warn } from '../util/log.js'; import { filter, isNumber, map } from 'zrender/lib/core/util.js'; // 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; }; var TimeScale = /** @class */ function (_super) { __extends(TimeScale, _super); function TimeScale(settings) { var _this = _super.call(this, settings) || this; _this.type = 'time'; return _this; } /** * Get label is mainly for other components like dataZoom, tooltip. */ TimeScale.prototype.getLabel = function (tick) { var useUTC = this.getSetting('useUTC'); return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale')); }; TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) { var isUTC = this.getSetting('useUTC'); var lang = this.getSetting('locale'); return leveledFormat(tick, idx, labelFormatter, lang, isUTC); }; /** * @override */ TimeScale.prototype.getTicks = function () { var interval = this._interval; var extent = this._extent; var ticks = []; // If interval is 0, return []; if (!interval) { return ticks; } ticks.push({ value: extent[0], level: 0 }); var useUTC = this.getSetting('useUTC'); var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent); ticks = ticks.concat(innerTicks); ticks.push({ value: extent[1], level: 0 }); return ticks; }; TimeScale.prototype.calcNiceExtent = 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.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); }; TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) { approxTickNum = approxTickNum || 10; var extent = this._extent; var span = extent[1] - extent[0]; this._approxInterval = span / approxTickNum; if (minInterval != null && this._approxInterval < minInterval) { this._approxInterval = minInterval; } if (maxInterval != null && this._approxInterval > maxInterval) { this._approxInterval = maxInterval; } var scaleIntervalsLen = scaleIntervals.length; var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); // Interval that can be used to calculate ticks this._interval = scaleIntervals[idx][1]; // Min level used when picking ticks from top down. // We check one more level to avoid the ticks are to sparse in some case. this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0]; }; TimeScale.prototype.parse = function (val) { // val might be float. return isNumber(val) ? val : +numberUtil.parseDate(val); }; TimeScale.prototype.contain = function (val) { return scaleHelper.contain(this.parse(val), this._extent); }; TimeScale.prototype.normalize = function (val) { return scaleHelper.normalize(this.parse(val), this._extent); }; TimeScale.prototype.scale = function (val) { return scaleHelper.scale(val, this._extent); }; TimeScale.type = 'time'; return TimeScale; }(IntervalScale); /** * This implementation was originally copied from "d3.js" * * with some modifications made for this program. * See the license statement at the head of this file. */ var scaleIntervals = [// Format interval ['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y ]; function isUnitValueSame(unit, valueA, valueB, isUTC) { var dateA = numberUtil.parseDate(valueA); var dateB = numberUtil.parseDate(valueB); var isSame = function (unit) { return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC); }; var isSameYear = function () { return isSame('year'); }; // const isSameHalfYear = () => isSameYear() && isSame('half-year'); // const isSameQuater = () => isSameYear() && isSame('quarter'); var isSameMonth = function () { return isSameYear() && isSame('month'); }; var isSameDay = function () { return isSameMonth() && isSame('day'); }; // const isSameHalfDay = () => isSameDay() && isSame('half-day'); var isSameHour = function () { return isSameDay() && isSame('hour'); }; var isSameMinute = function () { return isSameHour() && isSame('minute'); }; var isSameSecond = function () { return isSameMinute() && isSame('second'); }; var isSameMilliSecond = function () { return isSameSecond() && isSame('millisecond'); }; switch (unit) { case 'year': return isSameYear(); case 'month': return isSameMonth(); case 'day': return isSameDay(); case 'hour': return isSameHour(); case 'minute': return isSameMinute(); case 'second': return isSameSecond(); case 'millisecond': return isSameMilliSecond(); } } // const primaryUnitGetters = { // year: fullYearGetterName(), // month: monthGetterName(), // day: dateGetterName(), // hour: hoursGetterName(), // minute: minutesGetterName(), // second: secondsGetterName(), // millisecond: millisecondsGetterName() // }; // const primaryUnitUTCGetters = { // year: fullYearGetterName(true), // month: monthGetterName(true), // day: dateGetterName(true), // hour: hoursGetterName(true), // minute: minutesGetterName(true), // second: secondsGetterName(true), // millisecond: millisecondsGetterName(true) // }; // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) { // step = step || 1; // switch (getPrimaryTimeUnit(unitName)) { // case 'year': // date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step); // break; // case 'month': // date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step); // break; // case 'day': // date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step); // break; // case 'hour': // date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step); // break; // case 'minute': // date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step); // break; // case 'second': // date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step); // break; // case 'millisecond': // date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step); // break; // } // return date.getTime(); // } // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]]; // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]]; // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]]; function getDateInterval(approxInterval, daysInMonth) { approxInterval /= ONE_DAY; return approxInterval > 16 ? 16 // Math.floor(daysInMonth / 2) + 1 // In this case we only want one tick between two months. : approxInterval > 7.5 ? 7 // TODO week 7 or day 8? : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1; } function getMonthInterval(approxInterval) { var APPROX_ONE_MONTH = 30 * ONE_DAY; approxInterval /= APPROX_ONE_MONTH; return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1; } function getHourInterval(approxInterval) { approxInterval /= ONE_HOUR; return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1; } function getMinutesAndSecondsInterval(approxInterval, isMinutes) { approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND; return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1; } function getMillisecondsInterval(approxInterval) { return numberUtil.nice(approxInterval, true); } function getFirstTimestampOfUnit(date, unitName, isUTC) { var outDate = new Date(date); switch (getPrimaryTimeUnit(unitName)) { case 'year': case 'month': outDate[monthSetterName(isUTC)](0); case 'day': outDate[dateSetterName(isUTC)](1); case 'hour': outDate[hoursSetterName(isUTC)](0); case 'minute': outDate[minutesSetterName(isUTC)](0); case 'second': outDate[secondsSetterName(isUTC)](0); outDate[millisecondsSetterName(isUTC)](0); } return outDate.getTime(); } function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) { var safeLimit = 10000; var unitNames = timeUnits; var iter = 0; function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) { var date = new Date(minTimestamp); var dateTime = minTimestamp; var d = date[getMethodName](); // if (isDate) { // d -= 1; // Starts with 0; PENDING // } while (dateTime < maxTimestamp && dateTime <= extent[1]) { out.push({ value: dateTime }); d += interval; date[setMethodName](d); dateTime = date.getTime(); } // This extra tick is for calcuating ticks of next level. Will not been added to the final result out.push({ value: dateTime, notAdd: true }); } function addLevelTicks(unitName, lastLevelTicks, levelTicks) { var newAddedTicks = []; var isFirstLevel = !lastLevelTicks.length; if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) { return; } if (isFirstLevel) { lastLevelTicks = [{ // TODO Optimize. Not include so may ticks. value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC) }, { value: extent[1] }]; } for (var i = 0; i < lastLevelTicks.length - 1; i++) { var startTick = lastLevelTicks[i].value; var endTick = lastLevelTicks[i + 1].value; if (startTick === endTick) { continue; } var interval = void 0; var getterName = void 0; var setterName = void 0; var isDate = false; switch (unitName) { case 'year': interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365)); getterName = fullYearGetterName(isUTC); setterName = fullYearSetterName(isUTC); break; case 'half-year': case 'quarter': case 'month': interval = getMonthInterval(approxInterval); getterName = monthGetterName(isUTC); setterName = monthSetterName(isUTC); break; case 'week': // PENDING If week is added. Ignore day. case 'half-week': case 'day': interval = getDateInterval(approxInterval, 31); // Use 32 days and let interval been 16 getterName = dateGetterName(isUTC); setterName = dateSetterName(isUTC); isDate = true; break; case 'half-day': case 'quarter-day': case 'hour': interval = getHourInterval(approxInterval); getterName = hoursGetterName(isUTC); setterName = hoursSetterName(isUTC); break; case 'minute': interval = getMinutesAndSecondsInterval(approxInterval, true); getterName = minutesGetterName(isUTC); setterName = minutesSetterName(isUTC); break; case 'second': interval = getMinutesAndSecondsInterval(approxInterval, false); getterName = secondsGetterName(isUTC); setterName = secondsSetterName(isUTC); break; case 'millisecond': interval = getMillisecondsInterval(approxInterval); getterName = millisecondsGetterName(isUTC); setterName = millisecondsSetterName(isUTC); break; } addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks); if (unitName === 'year' && levelTicks.length > 1 && i === 0) { // Add nearest years to the left extent. levelTicks.unshift({ value: levelTicks[0].value - interval }); } } for (var i = 0; i < newAddedTicks.length; i++) { levelTicks.push(newAddedTicks[i]); } // newAddedTicks.length && console.log(unitName, newAddedTicks); return newAddedTicks; } var levelsTicks = []; var currentLevelTicks = []; var tickCount = 0; var lastLevelTickCount = 0; for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) { var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]); if (!isPrimaryTimeUnit(unitNames[i])) { // TODO continue; } addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks); var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null; if (primaryTimeUnit !== nextPrimaryTimeUnit) { if (currentLevelTicks.length) { lastLevelTickCount = tickCount; // Remove the duplicate so the tick count can be precisely. currentLevelTicks.sort(function (a, b) { return a.value - b.value; }); var levelTicksRemoveDuplicated = []; for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) { var tickValue = currentLevelTicks[i_1].value; if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) { levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]); if (tickValue >= extent[0] && tickValue <= extent[1]) { tickCount++; } } } var targetTickNum = (extent[1] - extent[0]) / approxInterval; // Added too much in this level and not too less in last level if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) { break; } // Only treat primary time unit as one level. levelsTicks.push(levelTicksRemoveDuplicated); if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) { break; } } // Reset if next unitName is primary currentLevelTicks = []; } } if (process.env.NODE_ENV !== 'production') { if (iter >= safeLimit) { warn('Exceed safe limit.'); } } var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) { return filter(levelTicks, function (tick) { return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd; }); }), function (levelTicks) { return levelTicks.length > 0; }); var ticks = []; var maxLevel = levelsTicksInExtent.length - 1; for (var i = 0; i < levelsTicksInExtent.length; ++i) { var levelTicks = levelsTicksInExtent[i]; for (var k = 0; k < levelTicks.length; ++k) { ticks.push({ value: levelTicks[k].value, level: maxLevel - i }); } } ticks.sort(function (a, b) { return a.value - b.value; }); // Remove duplicates var result = []; for (var i = 0; i < ticks.length; ++i) { if (i === 0 || ticks[i].value !== ticks[i - 1].value) { result.push(ticks[i]); } } return result; } Scale.registerClass(TimeScale); export default TimeScale;