Time.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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 * as scaleHelper from './helper.js';
  62. import IntervalScale from './Interval.js';
  63. import Scale from './Scale.js';
  64. import { warn } from '../util/log.js';
  65. import { each, filter, indexOf, isNumber, map } from 'zrender/lib/core/util.js';
  66. import { getScaleBreakHelper } from './break.js';
  67. // FIXME 公用?
  68. var bisect = function (a, x, lo, hi) {
  69. while (lo < hi) {
  70. var mid = lo + hi >>> 1;
  71. if (a[mid][1] < x) {
  72. lo = mid + 1;
  73. } else {
  74. hi = mid;
  75. }
  76. }
  77. return lo;
  78. };
  79. var TimeScale = /** @class */function (_super) {
  80. __extends(TimeScale, _super);
  81. function TimeScale(settings) {
  82. var _this = _super.call(this, settings) || this;
  83. _this.type = 'time';
  84. return _this;
  85. }
  86. /**
  87. * Get label is mainly for other components like dataZoom, tooltip.
  88. */
  89. TimeScale.prototype.getLabel = function (tick) {
  90. var useUTC = this.getSetting('useUTC');
  91. return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale'));
  92. };
  93. TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) {
  94. var isUTC = this.getSetting('useUTC');
  95. var lang = this.getSetting('locale');
  96. return leveledFormat(tick, idx, labelFormatter, lang, isUTC);
  97. };
  98. /**
  99. * @override
  100. */
  101. TimeScale.prototype.getTicks = function (opt) {
  102. opt = opt || {};
  103. var interval = this._interval;
  104. var extent = this._extent;
  105. var scaleBreakHelper = getScaleBreakHelper();
  106. var ticks = [];
  107. // If interval is 0, return [];
  108. if (!interval) {
  109. return ticks;
  110. }
  111. var useUTC = this.getSetting('useUTC');
  112. if (scaleBreakHelper && opt.breakTicks === 'only_break') {
  113. getScaleBreakHelper().addBreaksToTicks(ticks, this._brkCtx.breaks, this._extent);
  114. return ticks;
  115. }
  116. var extent0Unit = getUnitFromValue(extent[1], useUTC);
  117. ticks.push({
  118. value: extent[0],
  119. time: {
  120. level: 0,
  121. upperTimeUnit: extent0Unit,
  122. lowerTimeUnit: extent0Unit
  123. }
  124. });
  125. var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent, this._getExtentSpanWithBreaks(), this._brkCtx);
  126. ticks = ticks.concat(innerTicks);
  127. var extent1Unit = getUnitFromValue(extent[1], useUTC);
  128. ticks.push({
  129. value: extent[1],
  130. time: {
  131. level: 0,
  132. upperTimeUnit: extent1Unit,
  133. lowerTimeUnit: extent1Unit
  134. }
  135. });
  136. var isUTC = this.getSetting('useUTC');
  137. var upperUnitIndex = primaryTimeUnits.length - 1;
  138. var maxLevel = 0;
  139. each(ticks, function (tick) {
  140. upperUnitIndex = Math.min(upperUnitIndex, indexOf(primaryTimeUnits, tick.time.upperTimeUnit));
  141. maxLevel = Math.max(maxLevel, tick.time.level);
  142. });
  143. if (scaleBreakHelper) {
  144. getScaleBreakHelper().pruneTicksByBreak(opt.pruneByBreak, ticks, this._brkCtx.breaks, function (item) {
  145. return item.value;
  146. }, this._approxInterval, this._extent);
  147. }
  148. if (scaleBreakHelper && opt.breakTicks !== 'none') {
  149. getScaleBreakHelper().addBreaksToTicks(ticks, this._brkCtx.breaks, this._extent, function (trimmedBrk) {
  150. // @see `parseTimeAxisLabelFormatterDictionary`.
  151. var lowerBrkUnitIndex = Math.max(indexOf(primaryTimeUnits, getUnitFromValue(trimmedBrk.vmin, isUTC)), indexOf(primaryTimeUnits, getUnitFromValue(trimmedBrk.vmax, isUTC)));
  152. var upperBrkUnitIndex = 0;
  153. for (var unitIdx = 0; unitIdx < primaryTimeUnits.length; unitIdx++) {
  154. if (!isPrimaryUnitValueAndGreaterSame(primaryTimeUnits[unitIdx], trimmedBrk.vmin, trimmedBrk.vmax, isUTC)) {
  155. upperBrkUnitIndex = unitIdx;
  156. break;
  157. }
  158. }
  159. var upperIdx = Math.min(upperBrkUnitIndex, upperUnitIndex);
  160. var lowerIdx = Math.max(upperIdx, lowerBrkUnitIndex);
  161. return {
  162. level: maxLevel,
  163. lowerTimeUnit: primaryTimeUnits[lowerIdx],
  164. upperTimeUnit: primaryTimeUnits[upperIdx]
  165. };
  166. });
  167. }
  168. return ticks;
  169. };
  170. TimeScale.prototype.calcNiceExtent = function (opt) {
  171. var extent = this.getExtent();
  172. // If extent start and end are same, expand them
  173. if (extent[0] === extent[1]) {
  174. // Expand extent
  175. extent[0] -= ONE_DAY;
  176. extent[1] += ONE_DAY;
  177. }
  178. // If there are no data and extent are [Infinity, -Infinity]
  179. if (extent[1] === -Infinity && extent[0] === Infinity) {
  180. var d = new Date();
  181. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  182. extent[0] = extent[1] - ONE_DAY;
  183. }
  184. this._innerSetExtent(extent[0], extent[1]);
  185. this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  186. };
  187. TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) {
  188. approxTickNum = approxTickNum || 10;
  189. var span = this._getExtentSpanWithBreaks();
  190. this._approxInterval = span / approxTickNum;
  191. if (minInterval != null && this._approxInterval < minInterval) {
  192. this._approxInterval = minInterval;
  193. }
  194. if (maxInterval != null && this._approxInterval > maxInterval) {
  195. this._approxInterval = maxInterval;
  196. }
  197. var scaleIntervalsLen = scaleIntervals.length;
  198. var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1);
  199. // Interval that can be used to calculate ticks
  200. this._interval = scaleIntervals[idx][1];
  201. this._intervalPrecision = scaleHelper.getIntervalPrecision(this._interval);
  202. // Min level used when picking ticks from top down.
  203. // We check one more level to avoid the ticks are to sparse in some case.
  204. this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0];
  205. };
  206. TimeScale.prototype.parse = function (val) {
  207. // val might be float.
  208. return isNumber(val) ? val : +numberUtil.parseDate(val);
  209. };
  210. TimeScale.prototype.contain = function (val) {
  211. return scaleHelper.contain(val, this._extent);
  212. };
  213. TimeScale.prototype.normalize = function (val) {
  214. return this._calculator.normalize(val, this._extent);
  215. };
  216. TimeScale.prototype.scale = function (val) {
  217. return this._calculator.scale(val, this._extent);
  218. };
  219. TimeScale.type = 'time';
  220. return TimeScale;
  221. }(IntervalScale);
  222. /**
  223. * This implementation was originally copied from "d3.js"
  224. * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>
  225. * with some modifications made for this program.
  226. * See the license statement at the head of this file.
  227. */
  228. var scaleIntervals = [
  229. // Format interval
  230. ['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
  231. ];
  232. function isPrimaryUnitValueAndGreaterSame(unit, valueA, valueB, isUTC) {
  233. return roundTime(new Date(valueA), unit, isUTC).getTime() === roundTime(new Date(valueB), unit, isUTC).getTime();
  234. }
  235. // function isUnitValueSame(
  236. // unit: PrimaryTimeUnit,
  237. // valueA: number,
  238. // valueB: number,
  239. // isUTC: boolean
  240. // ): boolean {
  241. // const dateA = numberUtil.parseDate(valueA) as any;
  242. // const dateB = numberUtil.parseDate(valueB) as any;
  243. // const isSame = (unit: PrimaryTimeUnit) => {
  244. // return getUnitValue(dateA, unit, isUTC)
  245. // === getUnitValue(dateB, unit, isUTC);
  246. // };
  247. // const isSameYear = () => isSame('year');
  248. // // const isSameHalfYear = () => isSameYear() && isSame('half-year');
  249. // // const isSameQuater = () => isSameYear() && isSame('quarter');
  250. // const isSameMonth = () => isSameYear() && isSame('month');
  251. // const isSameDay = () => isSameMonth() && isSame('day');
  252. // // const isSameHalfDay = () => isSameDay() && isSame('half-day');
  253. // const isSameHour = () => isSameDay() && isSame('hour');
  254. // const isSameMinute = () => isSameHour() && isSame('minute');
  255. // const isSameSecond = () => isSameMinute() && isSame('second');
  256. // const isSameMilliSecond = () => isSameSecond() && isSame('millisecond');
  257. // switch (unit) {
  258. // case 'year':
  259. // return isSameYear();
  260. // case 'month':
  261. // return isSameMonth();
  262. // case 'day':
  263. // return isSameDay();
  264. // case 'hour':
  265. // return isSameHour();
  266. // case 'minute':
  267. // return isSameMinute();
  268. // case 'second':
  269. // return isSameSecond();
  270. // case 'millisecond':
  271. // return isSameMilliSecond();
  272. // }
  273. // }
  274. // const primaryUnitGetters = {
  275. // year: fullYearGetterName(),
  276. // month: monthGetterName(),
  277. // day: dateGetterName(),
  278. // hour: hoursGetterName(),
  279. // minute: minutesGetterName(),
  280. // second: secondsGetterName(),
  281. // millisecond: millisecondsGetterName()
  282. // };
  283. // const primaryUnitUTCGetters = {
  284. // year: fullYearGetterName(true),
  285. // month: monthGetterName(true),
  286. // day: dateGetterName(true),
  287. // hour: hoursGetterName(true),
  288. // minute: minutesGetterName(true),
  289. // second: secondsGetterName(true),
  290. // millisecond: millisecondsGetterName(true)
  291. // };
  292. // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {
  293. // step = step || 1;
  294. // switch (getPrimaryTimeUnit(unitName)) {
  295. // case 'year':
  296. // date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);
  297. // break;
  298. // case 'month':
  299. // date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);
  300. // break;
  301. // case 'day':
  302. // date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);
  303. // break;
  304. // case 'hour':
  305. // date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);
  306. // break;
  307. // case 'minute':
  308. // date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);
  309. // break;
  310. // case 'second':
  311. // date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);
  312. // break;
  313. // case 'millisecond':
  314. // date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);
  315. // break;
  316. // }
  317. // return date.getTime();
  318. // }
  319. // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];
  320. // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];
  321. // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];
  322. function getDateInterval(approxInterval, daysInMonth) {
  323. approxInterval /= ONE_DAY;
  324. return approxInterval > 16 ? 16
  325. // Math.floor(daysInMonth / 2) + 1 // In this case we only want one tick between two months.
  326. : approxInterval > 7.5 ? 7 // TODO week 7 or day 8?
  327. : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1;
  328. }
  329. function getMonthInterval(approxInterval) {
  330. var APPROX_ONE_MONTH = 30 * ONE_DAY;
  331. approxInterval /= APPROX_ONE_MONTH;
  332. return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1;
  333. }
  334. function getHourInterval(approxInterval) {
  335. approxInterval /= ONE_HOUR;
  336. return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1;
  337. }
  338. function getMinutesAndSecondsInterval(approxInterval, isMinutes) {
  339. approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND;
  340. return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1;
  341. }
  342. function getMillisecondsInterval(approxInterval) {
  343. return numberUtil.nice(approxInterval, true);
  344. }
  345. // e.g., if the input unit is 'day', start calculate ticks from the first day of
  346. // that month to make ticks "nice".
  347. function getFirstTimestampOfUnit(timestamp, unitName, isUTC) {
  348. var upperUnitIdx = Math.max(0, indexOf(primaryTimeUnits, unitName) - 1);
  349. return roundTime(new Date(timestamp), primaryTimeUnits[upperUnitIdx], isUTC).getTime();
  350. }
  351. function createEstimateNiceMultiple(setMethodName, dateMethodInterval) {
  352. var tmpDate = new Date(0);
  353. tmpDate[setMethodName](1);
  354. var tmpTime = tmpDate.getTime();
  355. tmpDate[setMethodName](1 + dateMethodInterval);
  356. var approxTimeInterval = tmpDate.getTime() - tmpTime;
  357. return function (tickVal, targetValue) {
  358. // Only in month that accurate result can not get by division of
  359. // timestamp interval, but no need accurate here.
  360. return Math.max(0, Math.round((targetValue - tickVal) / approxTimeInterval));
  361. };
  362. }
  363. function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent, extentSpanWithBreaks, brkCtx) {
  364. var safeLimit = 10000;
  365. var unitNames = timeUnits;
  366. var iter = 0;
  367. function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) {
  368. var estimateNiceMultiple = createEstimateNiceMultiple(setMethodName, interval);
  369. var dateTime = minTimestamp;
  370. var date = new Date(dateTime);
  371. // if (isDate) {
  372. // d -= 1; // Starts with 0; PENDING
  373. // }
  374. while (dateTime < maxTimestamp && dateTime <= extent[1]) {
  375. out.push({
  376. value: dateTime
  377. });
  378. if (iter++ > safeLimit) {
  379. if (process.env.NODE_ENV !== 'production') {
  380. warn('Exceed safe limit in time scale.');
  381. }
  382. break;
  383. }
  384. date[setMethodName](date[getMethodName]() + interval);
  385. dateTime = date.getTime();
  386. if (brkCtx) {
  387. var moreMultiple = brkCtx.calcNiceTickMultiple(dateTime, estimateNiceMultiple);
  388. if (moreMultiple > 0) {
  389. date[setMethodName](date[getMethodName]() + moreMultiple * interval);
  390. dateTime = date.getTime();
  391. }
  392. }
  393. }
  394. // This extra tick is for calcuating ticks of next level. Will not been added to the final result
  395. out.push({
  396. value: dateTime,
  397. notAdd: true
  398. });
  399. }
  400. function addLevelTicks(unitName, lastLevelTicks, levelTicks) {
  401. var newAddedTicks = [];
  402. var isFirstLevel = !lastLevelTicks.length;
  403. if (isPrimaryUnitValueAndGreaterSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) {
  404. return;
  405. }
  406. if (isFirstLevel) {
  407. lastLevelTicks = [{
  408. value: getFirstTimestampOfUnit(extent[0], unitName, isUTC)
  409. }, {
  410. value: extent[1]
  411. }];
  412. }
  413. for (var i = 0; i < lastLevelTicks.length - 1; i++) {
  414. var startTick = lastLevelTicks[i].value;
  415. var endTick = lastLevelTicks[i + 1].value;
  416. if (startTick === endTick) {
  417. continue;
  418. }
  419. var interval = void 0;
  420. var getterName = void 0;
  421. var setterName = void 0;
  422. var isDate = false;
  423. switch (unitName) {
  424. case 'year':
  425. interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365));
  426. getterName = fullYearGetterName(isUTC);
  427. setterName = fullYearSetterName(isUTC);
  428. break;
  429. case 'half-year':
  430. case 'quarter':
  431. case 'month':
  432. interval = getMonthInterval(approxInterval);
  433. getterName = monthGetterName(isUTC);
  434. setterName = monthSetterName(isUTC);
  435. break;
  436. case 'week': // PENDING If week is added. Ignore day.
  437. case 'half-week':
  438. case 'day':
  439. interval = getDateInterval(approxInterval, 31); // Use 32 days and let interval been 16
  440. getterName = dateGetterName(isUTC);
  441. setterName = dateSetterName(isUTC);
  442. isDate = true;
  443. break;
  444. case 'half-day':
  445. case 'quarter-day':
  446. case 'hour':
  447. interval = getHourInterval(approxInterval);
  448. getterName = hoursGetterName(isUTC);
  449. setterName = hoursSetterName(isUTC);
  450. break;
  451. case 'minute':
  452. interval = getMinutesAndSecondsInterval(approxInterval, true);
  453. getterName = minutesGetterName(isUTC);
  454. setterName = minutesSetterName(isUTC);
  455. break;
  456. case 'second':
  457. interval = getMinutesAndSecondsInterval(approxInterval, false);
  458. getterName = secondsGetterName(isUTC);
  459. setterName = secondsSetterName(isUTC);
  460. break;
  461. case 'millisecond':
  462. interval = getMillisecondsInterval(approxInterval);
  463. getterName = millisecondsGetterName(isUTC);
  464. setterName = millisecondsSetterName(isUTC);
  465. break;
  466. }
  467. // Notice: This expansion by `getFirstTimestampOfUnit` may cause too many ticks and
  468. // iteration. e.g., when three levels of ticks is displayed, which can be caused by
  469. // data zoom and axis breaks. Thus trim them here.
  470. if (endTick >= extent[0] && startTick <= extent[1]) {
  471. addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks);
  472. }
  473. if (unitName === 'year' && levelTicks.length > 1 && i === 0) {
  474. // Add nearest years to the left extent.
  475. levelTicks.unshift({
  476. value: levelTicks[0].value - interval
  477. });
  478. }
  479. }
  480. for (var i = 0; i < newAddedTicks.length; i++) {
  481. levelTicks.push(newAddedTicks[i]);
  482. }
  483. }
  484. var levelsTicks = [];
  485. var currentLevelTicks = [];
  486. var tickCount = 0;
  487. var lastLevelTickCount = 0;
  488. for (var i = 0; i < unitNames.length; ++i) {
  489. var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]);
  490. if (!isPrimaryTimeUnit(unitNames[i])) {
  491. // TODO
  492. continue;
  493. }
  494. addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks);
  495. var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null;
  496. if (primaryTimeUnit !== nextPrimaryTimeUnit) {
  497. if (currentLevelTicks.length) {
  498. lastLevelTickCount = tickCount;
  499. // Remove the duplicate so the tick count can be precisely.
  500. currentLevelTicks.sort(function (a, b) {
  501. return a.value - b.value;
  502. });
  503. var levelTicksRemoveDuplicated = [];
  504. for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {
  505. var tickValue = currentLevelTicks[i_1].value;
  506. if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) {
  507. levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]);
  508. if (tickValue >= extent[0] && tickValue <= extent[1]) {
  509. tickCount++;
  510. }
  511. }
  512. }
  513. var targetTickNum = extentSpanWithBreaks / approxInterval;
  514. // Added too much in this level and not too less in last level
  515. if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) {
  516. break;
  517. }
  518. // Only treat primary time unit as one level.
  519. levelsTicks.push(levelTicksRemoveDuplicated);
  520. if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) {
  521. break;
  522. }
  523. }
  524. // Reset if next unitName is primary
  525. currentLevelTicks = [];
  526. }
  527. }
  528. var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) {
  529. return filter(levelTicks, function (tick) {
  530. return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd;
  531. });
  532. }), function (levelTicks) {
  533. return levelTicks.length > 0;
  534. });
  535. var ticks = [];
  536. var maxLevel = levelsTicksInExtent.length - 1;
  537. for (var i = 0; i < levelsTicksInExtent.length; ++i) {
  538. var levelTicks = levelsTicksInExtent[i];
  539. for (var k = 0; k < levelTicks.length; ++k) {
  540. var unit = getUnitFromValue(levelTicks[k].value, isUTC);
  541. ticks.push({
  542. value: levelTicks[k].value,
  543. time: {
  544. level: maxLevel - i,
  545. upperTimeUnit: unit,
  546. lowerTimeUnit: unit
  547. }
  548. });
  549. }
  550. }
  551. ticks.sort(function (a, b) {
  552. return a.value - b.value;
  553. });
  554. // Remove duplicates
  555. var result = [];
  556. for (var i = 0; i < ticks.length; ++i) {
  557. if (i === 0 || ticks[i].value !== ticks[i - 1].value) {
  558. result.push(ticks[i]);
  559. }
  560. }
  561. return result;
  562. }
  563. Scale.registerClass(TimeScale);
  564. export default TimeScale;