Time.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { __extends } from "tslib";
  41. /*
  42. * A third-party license is embedded for some of the code in this file:
  43. * The "scaleLevels" was originally copied from "d3.js" with some
  44. * modifications made for this project.
  45. * (See more details in the comment on the definition of "scaleLevels" below.)
  46. * The use of the source code of this file is also subject to the terms
  47. * and consitions of the license of "d3.js" (BSD-3Clause, see
  48. * </licenses/LICENSE-d3>).
  49. */
  50. // [About UTC and local time zone]:
  51. // In most cases, `number.parseDate` will treat input data string as local time
  52. // (except time zone is specified in time string). And `format.formateTime` returns
  53. // local time by default. option.useUTC is false by default. This design has
  54. // considered these common cases:
  55. // (1) Time that is persistent in server is in UTC, but it is needed to be displayed
  56. // in local time by default.
  57. // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
  58. // as its original time, without any time difference.
  59. import * as numberUtil from '../util/number.js';
  60. import { ONE_SECOND, ONE_MINUTE, ONE_HOUR, ONE_DAY, ONE_YEAR, format, leveledFormat, timeUnits, fullLeveledFormatter, getPrimaryTimeUnit, isPrimaryTimeUnit, getDefaultFormatPrecisionOfInterval, fullYearGetterName, monthSetterName, fullYearSetterName, dateSetterName, hoursGetterName, hoursSetterName, minutesSetterName, secondsSetterName, millisecondsSetterName, monthGetterName, dateGetterName, minutesGetterName, secondsGetterName, millisecondsGetterName, getUnitFromValue, primaryTimeUnits, roundTime } from '../util/time.js';
  61. import { ensureValidSplitNumber } from './helper.js';
  62. import Scale from './Scale.js';
  63. import { warn } from '../util/log.js';
  64. import { each, filter, indexOf, isNumber, map } from 'zrender/lib/core/util.js';
  65. import { getBreaksUnsafe, getScaleBreakHelper, simplyParseBreakOption } from './break.js';
  66. import { getMinorTicks } from './minorTicks.js';
  67. import { getScaleLinearSpanEffective, getScaleExtentForTickUnsafe, initBreakOrLinearMapper } from './scaleMapper.js';
  68. import { removeDuplicates, removeDuplicatesGetKeyFromValueProp } from '../util/model.js';
  69. // FIXME 公用?
  70. var bisect = function (a, x, lo, hi) {
  71. while (lo < hi) {
  72. var mid = lo + hi >>> 1;
  73. if (a[mid][1] < x) {
  74. lo = mid + 1;
  75. } else {
  76. hi = mid;
  77. }
  78. }
  79. return lo;
  80. };
  81. var TimeScale = /** @class */function (_super) {
  82. __extends(TimeScale, _super);
  83. function TimeScale(setting) {
  84. var _this = _super.call(this) || this;
  85. _this.type = 'time';
  86. _this.parse = TimeScale.parse;
  87. _this._locale = setting.locale;
  88. _this._useUTC = setting.useUTC;
  89. _this._interval = 0;
  90. var breakParsed = simplyParseBreakOption(_this, setting);
  91. var res = initBreakOrLinearMapper(_this, breakParsed, null);
  92. // @ts-ignore
  93. _this.brk = res.brk;
  94. return _this;
  95. }
  96. /**
  97. * Get label is mainly for other components like dataZoom, tooltip.
  98. */
  99. TimeScale.prototype.getLabel = function (tick) {
  100. return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, this._useUTC, this._locale);
  101. };
  102. TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) {
  103. return leveledFormat(tick, idx, labelFormatter, this._locale, this._useUTC);
  104. };
  105. TimeScale.prototype.getTicks = function (opt) {
  106. opt = opt || {};
  107. var interval = this._interval;
  108. var extent = getScaleExtentForTickUnsafe(this);
  109. var scaleBreakHelper = getScaleBreakHelper();
  110. var brk = this.brk;
  111. var brkAvailable = scaleBreakHelper && brk;
  112. var ticks = [];
  113. // If interval is 0, return [];
  114. if (!interval) {
  115. return ticks;
  116. }
  117. var useUTC = this._useUTC;
  118. if (brkAvailable && opt.breakTicks === 'only_break') {
  119. getScaleBreakHelper().addBreaksToTicks(ticks, brk.breaks, extent);
  120. return ticks;
  121. }
  122. ticks = createIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent, getScaleLinearSpanEffective(this), brk);
  123. var upperUnitIndex = primaryTimeUnits.length - 1;
  124. var maxLevel = 0;
  125. each(ticks, function (tick) {
  126. if (tick.time) {
  127. upperUnitIndex = Math.min(upperUnitIndex, indexOf(primaryTimeUnits, tick.time.upperTimeUnit));
  128. maxLevel = Math.max(maxLevel, tick.time.level);
  129. }
  130. });
  131. if (brkAvailable) {
  132. getScaleBreakHelper().pruneTicksByBreak(opt.pruneByBreak, ticks, brk.breaks, function (item) {
  133. return item.value;
  134. }, this._approxInterval, extent);
  135. }
  136. if (brkAvailable && opt.breakTicks !== 'none') {
  137. getScaleBreakHelper().addBreaksToTicks(ticks, brk.breaks, extent, function (trimmedBrk) {
  138. // @see `parseTimeAxisLabelFormatterDictionary`.
  139. var lowerBrkUnitIndex = Math.max(indexOf(primaryTimeUnits, getUnitFromValue(trimmedBrk.vmin, useUTC)), indexOf(primaryTimeUnits, getUnitFromValue(trimmedBrk.vmax, useUTC)));
  140. var upperBrkUnitIndex = 0;
  141. for (var unitIdx = 0; unitIdx < primaryTimeUnits.length; unitIdx++) {
  142. if (!isPrimaryUnitValueAndGreaterSame(primaryTimeUnits[unitIdx], trimmedBrk.vmin, trimmedBrk.vmax, useUTC)) {
  143. upperBrkUnitIndex = unitIdx;
  144. break;
  145. }
  146. }
  147. var upperIdx = Math.min(upperBrkUnitIndex, upperUnitIndex);
  148. var lowerIdx = Math.max(upperIdx, lowerBrkUnitIndex);
  149. return {
  150. level: maxLevel,
  151. lowerTimeUnit: primaryTimeUnits[lowerIdx],
  152. upperTimeUnit: primaryTimeUnits[upperIdx]
  153. };
  154. });
  155. }
  156. return ticks;
  157. };
  158. TimeScale.prototype.getMinorTicks = function (splitNumber) {
  159. return getMinorTicks(this, splitNumber, getBreaksUnsafe(this), this._interval);
  160. };
  161. TimeScale.prototype.setTimeInterval = function (opt) {
  162. this._interval = opt.interval;
  163. this._approxInterval = opt.approxInterval;
  164. this._minLevelUnit = opt.minLevelUnit;
  165. };
  166. TimeScale.parse = function (val) {
  167. // `val` might be a float (e.g., calculated from percent), so call `round`.
  168. return isNumber(val) ? Math.round(val) : +numberUtil.parseDate(val);
  169. };
  170. TimeScale.type = 'time';
  171. return TimeScale;
  172. }(Scale);
  173. /**
  174. * This implementation was originally copied from "d3.js"
  175. * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>
  176. * with some modifications made for this program.
  177. * See the license statement at the head of this file.
  178. */
  179. var scaleIntervals = [
  180. // Format interval
  181. ['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
  182. ];
  183. function isPrimaryUnitValueAndGreaterSame(unit, valueA, valueB, isUTC) {
  184. return roundTime(new Date(valueA), unit, isUTC).getTime() === roundTime(new Date(valueB), unit, isUTC).getTime();
  185. }
  186. // function isUnitValueSame(
  187. // unit: PrimaryTimeUnit,
  188. // valueA: number,
  189. // valueB: number,
  190. // isUTC: boolean
  191. // ): boolean {
  192. // const dateA = numberUtil.parseDate(valueA) as any;
  193. // const dateB = numberUtil.parseDate(valueB) as any;
  194. // const isSame = (unit: PrimaryTimeUnit) => {
  195. // return getUnitValue(dateA, unit, isUTC)
  196. // === getUnitValue(dateB, unit, isUTC);
  197. // };
  198. // const isSameYear = () => isSame('year');
  199. // // const isSameHalfYear = () => isSameYear() && isSame('half-year');
  200. // // const isSameQuater = () => isSameYear() && isSame('quarter');
  201. // const isSameMonth = () => isSameYear() && isSame('month');
  202. // const isSameDay = () => isSameMonth() && isSame('day');
  203. // // const isSameHalfDay = () => isSameDay() && isSame('half-day');
  204. // const isSameHour = () => isSameDay() && isSame('hour');
  205. // const isSameMinute = () => isSameHour() && isSame('minute');
  206. // const isSameSecond = () => isSameMinute() && isSame('second');
  207. // const isSameMilliSecond = () => isSameSecond() && isSame('millisecond');
  208. // switch (unit) {
  209. // case 'year':
  210. // return isSameYear();
  211. // case 'month':
  212. // return isSameMonth();
  213. // case 'day':
  214. // return isSameDay();
  215. // case 'hour':
  216. // return isSameHour();
  217. // case 'minute':
  218. // return isSameMinute();
  219. // case 'second':
  220. // return isSameSecond();
  221. // case 'millisecond':
  222. // return isSameMilliSecond();
  223. // }
  224. // }
  225. // const primaryUnitGetters = {
  226. // year: fullYearGetterName(),
  227. // month: monthGetterName(),
  228. // day: dateGetterName(),
  229. // hour: hoursGetterName(),
  230. // minute: minutesGetterName(),
  231. // second: secondsGetterName(),
  232. // millisecond: millisecondsGetterName()
  233. // };
  234. // const primaryUnitUTCGetters = {
  235. // year: fullYearGetterName(true),
  236. // month: monthGetterName(true),
  237. // day: dateGetterName(true),
  238. // hour: hoursGetterName(true),
  239. // minute: minutesGetterName(true),
  240. // second: secondsGetterName(true),
  241. // millisecond: millisecondsGetterName(true)
  242. // };
  243. // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {
  244. // step = step || 1;
  245. // switch (getPrimaryTimeUnit(unitName)) {
  246. // case 'year':
  247. // date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);
  248. // break;
  249. // case 'month':
  250. // date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);
  251. // break;
  252. // case 'day':
  253. // date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);
  254. // break;
  255. // case 'hour':
  256. // date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);
  257. // break;
  258. // case 'minute':
  259. // date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);
  260. // break;
  261. // case 'second':
  262. // date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);
  263. // break;
  264. // case 'millisecond':
  265. // date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);
  266. // break;
  267. // }
  268. // return date.getTime();
  269. // }
  270. // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];
  271. // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];
  272. // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];
  273. function getDateInterval(approxInterval, daysInMonth) {
  274. approxInterval /= ONE_DAY;
  275. return approxInterval > 16 ? 16
  276. // Math.floor(daysInMonth / 2) + 1 // In this case we only want one tick between two months.
  277. : approxInterval > 7.5 ? 7 // TODO week 7 or day 8?
  278. : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1;
  279. }
  280. function getMonthInterval(approxInterval) {
  281. var APPROX_ONE_MONTH = 30 * ONE_DAY;
  282. approxInterval /= APPROX_ONE_MONTH;
  283. return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1;
  284. }
  285. function getHourInterval(approxInterval) {
  286. approxInterval /= ONE_HOUR;
  287. return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1;
  288. }
  289. function getMinutesAndSecondsInterval(approxInterval, isMinutes) {
  290. approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND;
  291. return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1;
  292. }
  293. function getMillisecondsInterval(approxInterval) {
  294. // If less than 1, the getTicks loop will inevitably deed loop and read safeLimit.
  295. return numberUtil.mathMax(numberUtil.nice(approxInterval, true), 1);
  296. }
  297. // e.g., if the input unit is 'day', start calculate ticks from the first day of
  298. // that month to make ticks "nice".
  299. function getFirstTimestampOfUnit(timestamp, unitName, isUTC) {
  300. var upperUnitIdx = Math.max(0, indexOf(primaryTimeUnits, unitName) - 1);
  301. return roundTime(new Date(timestamp), primaryTimeUnits[upperUnitIdx], isUTC).getTime();
  302. }
  303. function createEstimateNiceMultiple(setMethodName, dateMethodInterval) {
  304. var tmpDate = new Date(0);
  305. tmpDate[setMethodName](1);
  306. var tmpTime = tmpDate.getTime();
  307. tmpDate[setMethodName](1 + dateMethodInterval);
  308. var approxTimeInterval = tmpDate.getTime() - tmpTime;
  309. return function (tickVal, targetValue) {
  310. // Only in month that accurate result can not get by division of
  311. // timestamp interval, but no need accurate here.
  312. return Math.max(0, Math.round((targetValue - tickVal) / approxTimeInterval));
  313. };
  314. }
  315. function createIntervalTicks(bottomUnitName, approxInterval, isUTC, extent, innermostSpan, brk) {
  316. // A fail-safe is required since `interval` can be user specified, or for the case
  317. // that using dataZoom toolbox and zoom repeatedly.
  318. var safeLimit = 3000;
  319. var unitNames = timeUnits;
  320. var iter = 0;
  321. function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) {
  322. var estimateNiceMultiple = createEstimateNiceMultiple(setMethodName, interval);
  323. var dateTime = minTimestamp;
  324. var date = new Date(dateTime);
  325. // if (isDate) {
  326. // d -= 1; // Starts with 0; PENDING
  327. // }
  328. while (dateTime < maxTimestamp && dateTime <= extent[1]) {
  329. out.push({
  330. value: dateTime
  331. });
  332. if (iter++ > safeLimit) {
  333. if (process.env.NODE_ENV !== 'production') {
  334. warn('Exceed safe limit in TimeScale["getTicks"].');
  335. }
  336. break;
  337. }
  338. date[setMethodName](date[getMethodName]() + interval);
  339. dateTime = date.getTime();
  340. if (brk) {
  341. var moreMultiple = brk.calcNiceTickMultiple(dateTime, estimateNiceMultiple);
  342. if (moreMultiple > 0) {
  343. date[setMethodName](date[getMethodName]() + moreMultiple * interval);
  344. dateTime = date.getTime();
  345. }
  346. }
  347. }
  348. // This extra tick is for calculating ticks of next level. Will not been added to the final result
  349. out.push({
  350. value: dateTime,
  351. // extent[1] should be added; deduplication will be performed later.
  352. notAdd: dateTime > extent[1]
  353. });
  354. }
  355. function addLevelTicks(unitName, lastLevelTicks, levelTicks) {
  356. var newAddedTicks = [];
  357. var isFirstLevel = !lastLevelTicks.length;
  358. if (isPrimaryUnitValueAndGreaterSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) {
  359. return;
  360. }
  361. if (isFirstLevel) {
  362. lastLevelTicks = [{
  363. value: getFirstTimestampOfUnit(extent[0], unitName, isUTC)
  364. }, {
  365. value: extent[1]
  366. }];
  367. }
  368. for (var i = 0; i < lastLevelTicks.length - 1; i++) {
  369. var startTick = lastLevelTicks[i].value;
  370. var endTick = lastLevelTicks[i + 1].value;
  371. if (startTick === endTick) {
  372. continue;
  373. }
  374. var interval = void 0;
  375. var getterName = void 0;
  376. var setterName = void 0;
  377. var isDate = false;
  378. switch (unitName) {
  379. case 'year':
  380. interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365));
  381. getterName = fullYearGetterName(isUTC);
  382. setterName = fullYearSetterName(isUTC);
  383. break;
  384. case 'half-year':
  385. case 'quarter':
  386. case 'month':
  387. interval = getMonthInterval(approxInterval);
  388. getterName = monthGetterName(isUTC);
  389. setterName = monthSetterName(isUTC);
  390. break;
  391. case 'week': // PENDING If week is added. Ignore day.
  392. case 'half-week':
  393. case 'day':
  394. interval = getDateInterval(approxInterval, 31); // Use 32 days and let interval been 16
  395. getterName = dateGetterName(isUTC);
  396. setterName = dateSetterName(isUTC);
  397. isDate = true;
  398. break;
  399. case 'half-day':
  400. case 'quarter-day':
  401. case 'hour':
  402. interval = getHourInterval(approxInterval);
  403. getterName = hoursGetterName(isUTC);
  404. setterName = hoursSetterName(isUTC);
  405. break;
  406. case 'minute':
  407. interval = getMinutesAndSecondsInterval(approxInterval, true);
  408. getterName = minutesGetterName(isUTC);
  409. setterName = minutesSetterName(isUTC);
  410. break;
  411. case 'second':
  412. interval = getMinutesAndSecondsInterval(approxInterval, false);
  413. getterName = secondsGetterName(isUTC);
  414. setterName = secondsSetterName(isUTC);
  415. break;
  416. case 'millisecond':
  417. interval = getMillisecondsInterval(approxInterval);
  418. getterName = millisecondsGetterName(isUTC);
  419. setterName = millisecondsSetterName(isUTC);
  420. break;
  421. }
  422. // Notice: This expansion by `getFirstTimestampOfUnit` may cause too many ticks and
  423. // iteration. e.g., when three levels of ticks is displayed, which can be caused by
  424. // data zoom and axis breaks. Thus trim them here.
  425. if (endTick >= extent[0] && startTick <= extent[1]) {
  426. addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks);
  427. }
  428. if (unitName === 'year' && levelTicks.length > 1 && i === 0) {
  429. // Add nearest years to the left extent.
  430. levelTicks.unshift({
  431. value: levelTicks[0].value - interval
  432. });
  433. }
  434. }
  435. for (var i = 0; i < newAddedTicks.length; i++) {
  436. levelTicks.push(newAddedTicks[i]);
  437. }
  438. }
  439. var levelsTicks = [];
  440. var currentLevelTicks = [];
  441. var tickCount = 0;
  442. var lastLevelTickCount = 0;
  443. for (var i = 0; i < unitNames.length; ++i) {
  444. var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]);
  445. if (!isPrimaryTimeUnit(unitNames[i])) {
  446. // TODO
  447. continue;
  448. }
  449. addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks);
  450. var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null;
  451. if (primaryTimeUnit !== nextPrimaryTimeUnit) {
  452. if (currentLevelTicks.length) {
  453. lastLevelTickCount = tickCount;
  454. // Remove the duplicate so the tick count can be precisely.
  455. currentLevelTicks.sort(function (a, b) {
  456. return a.value - b.value;
  457. });
  458. var levelTicksRemoveDuplicated = [];
  459. for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {
  460. var tickValue = currentLevelTicks[i_1].value;
  461. if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) {
  462. levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]);
  463. if (tickValue >= extent[0] && tickValue <= extent[1]) {
  464. tickCount++;
  465. }
  466. }
  467. }
  468. var targetTickNum = innermostSpan / approxInterval;
  469. // Added too much in this level and not too less in last level
  470. if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) {
  471. break;
  472. }
  473. // Only treat primary time unit as one level.
  474. levelsTicks.push(levelTicksRemoveDuplicated);
  475. if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) {
  476. break;
  477. }
  478. }
  479. // Reset if next unitName is primary
  480. currentLevelTicks = [];
  481. }
  482. }
  483. var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) {
  484. return filter(levelTicks, function (tick) {
  485. return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd;
  486. });
  487. }), function (levelTicks) {
  488. return levelTicks.length > 0;
  489. });
  490. var maxLevel = levelsTicksInExtent.length - 1;
  491. var ticks = [];
  492. for (var i = 0; i < levelsTicksInExtent.length; ++i) {
  493. var levelTicks = levelsTicksInExtent[i];
  494. for (var k = 0; k < levelTicks.length; ++k) {
  495. var unit = getUnitFromValue(levelTicks[k].value, isUTC);
  496. ticks.push({
  497. value: levelTicks[k].value,
  498. time: {
  499. level: maxLevel - i,
  500. upperTimeUnit: unit,
  501. lowerTimeUnit: unit
  502. }
  503. });
  504. }
  505. }
  506. // Remove duplicates, which may cause jitter of `splitArea` and other bad cases.
  507. removeDuplicates(ticks, removeDuplicatesGetKeyFromValueProp, null);
  508. ticks.sort(function (a, b) {
  509. return a.value - b.value;
  510. });
  511. var currMinTick = ticks[0];
  512. var currMaxTick = ticks[ticks.length - 1];
  513. var extent0Unit = getUnitFromValue(extent[0], isUTC);
  514. var extent1Unit = getUnitFromValue(extent[1], isUTC);
  515. if (!currMinTick || currMinTick.value > extent[0]) {
  516. ticks.unshift({
  517. value: extent[0],
  518. time: {
  519. level: 0,
  520. upperTimeUnit: extent0Unit,
  521. lowerTimeUnit: extent0Unit
  522. },
  523. notNice: true
  524. });
  525. }
  526. if (!currMaxTick || currMaxTick.value < extent[1]) {
  527. ticks.push({
  528. value: extent[1],
  529. time: {
  530. level: 0,
  531. upperTimeUnit: extent1Unit,
  532. lowerTimeUnit: extent1Unit
  533. },
  534. notNice: true
  535. });
  536. }
  537. return ticks;
  538. }
  539. export var calcNiceForTimeScale = function (scale, opt) {
  540. var extent = scale.getExtent();
  541. // If extent start and end are same, expand them
  542. if (extent[0] === extent[1]) {
  543. // Expand extent
  544. extent[0] -= ONE_DAY;
  545. extent[1] += ONE_DAY;
  546. }
  547. // If there are no data and extent are [Infinity, -Infinity]
  548. if (extent[1] === -Infinity && extent[0] === Infinity) {
  549. var d = new Date();
  550. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  551. extent[0] = extent[1] - ONE_DAY;
  552. }
  553. scale.setExtent(extent[0], extent[1]);
  554. var splitNumber = ensureValidSplitNumber(opt.splitNumber, 10);
  555. var approxInterval = getScaleLinearSpanEffective(scale) / splitNumber;
  556. var minInterval = opt.minInterval;
  557. var maxInterval = opt.maxInterval;
  558. if (minInterval != null && approxInterval < minInterval) {
  559. approxInterval = minInterval;
  560. }
  561. if (maxInterval != null && approxInterval > maxInterval) {
  562. approxInterval = maxInterval;
  563. }
  564. var scaleIntervalsLen = scaleIntervals.length;
  565. var idx = Math.min(bisect(scaleIntervals, approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1);
  566. // Interval that can be used to calculate ticks
  567. var interval = scaleIntervals[idx][1];
  568. // Min level used when picking ticks from top down.
  569. // We check one more level to avoid the ticks are to sparse in some case.
  570. var minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0];
  571. scale.setTimeInterval({
  572. approxInterval: approxInterval,
  573. interval: interval,
  574. minLevelUnit: minLevelUnit
  575. });
  576. };
  577. Scale.registerClass(TimeScale);
  578. export default TimeScale;