Time.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. * A third-party license is embeded for some of the code in this file:
  21. * The "scaleLevels" was originally copied from "d3.js" with some
  22. * modifications made for this project.
  23. * (See more details in the comment on the definition of "scaleLevels" below.)
  24. * The use of the source code of this file is also subject to the terms
  25. * and consitions of the license of "d3.js" (BSD-3Clause, see
  26. * </licenses/LICENSE-d3>).
  27. */
  28. // [About UTC and local time zone]:
  29. // In most cases, `number.parseDate` will treat input data string as local time
  30. // (except time zone is specified in time string). And `format.formateTime` returns
  31. // local time by default. option.useUTC is false by default. This design have
  32. // concidered these common case:
  33. // (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
  34. // in local time by default.
  35. // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
  36. // as its original time, without any time difference.
  37. import * as zrUtil from 'zrender/src/core/util';
  38. import * as numberUtil from '../util/number';
  39. import * as formatUtil from '../util/format';
  40. import * as scaleHelper from './helper';
  41. import IntervalScale from './Interval';
  42. var intervalScaleProto = IntervalScale.prototype;
  43. var mathCeil = Math.ceil;
  44. var mathFloor = Math.floor;
  45. var ONE_SECOND = 1000;
  46. var ONE_MINUTE = ONE_SECOND * 60;
  47. var ONE_HOUR = ONE_MINUTE * 60;
  48. var ONE_DAY = ONE_HOUR * 24;
  49. // FIXME 公用?
  50. var bisect = function (a, x, lo, hi) {
  51. while (lo < hi) {
  52. var mid = lo + hi >>> 1;
  53. if (a[mid][1] < x) {
  54. lo = mid + 1;
  55. }
  56. else {
  57. hi = mid;
  58. }
  59. }
  60. return lo;
  61. };
  62. /**
  63. * @alias module:echarts/coord/scale/Time
  64. * @constructor
  65. */
  66. var TimeScale = IntervalScale.extend({
  67. type: 'time',
  68. /**
  69. * @override
  70. */
  71. getLabel: function (val) {
  72. var stepLvl = this._stepLvl;
  73. var date = new Date(val);
  74. return formatUtil.formatTime(stepLvl[0], date, this.getSetting('useUTC'));
  75. },
  76. /**
  77. * @override
  78. */
  79. niceExtent: function (opt) {
  80. var extent = this._extent;
  81. // If extent start and end are same, expand them
  82. if (extent[0] === extent[1]) {
  83. // Expand extent
  84. extent[0] -= ONE_DAY;
  85. extent[1] += ONE_DAY;
  86. }
  87. // If there are no data and extent are [Infinity, -Infinity]
  88. if (extent[1] === -Infinity && extent[0] === Infinity) {
  89. var d = new Date();
  90. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  91. extent[0] = extent[1] - ONE_DAY;
  92. }
  93. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  94. // var extent = this._extent;
  95. var interval = this._interval;
  96. if (!opt.fixMin) {
  97. extent[0] = numberUtil.round(mathFloor(extent[0] / interval) * interval);
  98. }
  99. if (!opt.fixMax) {
  100. extent[1] = numberUtil.round(mathCeil(extent[1] / interval) * interval);
  101. }
  102. },
  103. /**
  104. * @override
  105. */
  106. niceTicks: function (approxTickNum, minInterval, maxInterval) {
  107. approxTickNum = approxTickNum || 10;
  108. var extent = this._extent;
  109. var span = extent[1] - extent[0];
  110. var approxInterval = span / approxTickNum;
  111. if (minInterval != null && approxInterval < minInterval) {
  112. approxInterval = minInterval;
  113. }
  114. if (maxInterval != null && approxInterval > maxInterval) {
  115. approxInterval = maxInterval;
  116. }
  117. var scaleLevelsLen = scaleLevels.length;
  118. var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
  119. var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
  120. var interval = level[1];
  121. // Same with interval scale if span is much larger than 1 year
  122. if (level[0] === 'year') {
  123. var yearSpan = span / interval;
  124. // From "Nice Numbers for Graph Labels" of Graphic Gems
  125. // var niceYearSpan = numberUtil.nice(yearSpan, false);
  126. var yearStep = numberUtil.nice(yearSpan / approxTickNum, true);
  127. interval *= yearStep;
  128. }
  129. var timezoneOffset = this.getSetting('useUTC')
  130. ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000;
  131. var niceExtent = [
  132. Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
  133. Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)
  134. ];
  135. scaleHelper.fixExtent(niceExtent, extent);
  136. this._stepLvl = level;
  137. // Interval will be used in getTicks
  138. this._interval = interval;
  139. this._niceExtent = niceExtent;
  140. },
  141. parse: function (val) {
  142. // val might be float.
  143. return +numberUtil.parseDate(val);
  144. }
  145. });
  146. zrUtil.each(['contain', 'normalize'], function (methodName) {
  147. TimeScale.prototype[methodName] = function (val) {
  148. return intervalScaleProto[methodName].call(this, this.parse(val));
  149. };
  150. });
  151. /**
  152. * This implementation was originally copied from "d3.js"
  153. * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>
  154. * with some modifications made for this program.
  155. * See the license statement at the head of this file.
  156. */
  157. var scaleLevels = [
  158. // Format interval
  159. ['hh:mm:ss', ONE_SECOND], // 1s
  160. ['hh:mm:ss', ONE_SECOND * 5], // 5s
  161. ['hh:mm:ss', ONE_SECOND * 10], // 10s
  162. ['hh:mm:ss', ONE_SECOND * 15], // 15s
  163. ['hh:mm:ss', ONE_SECOND * 30], // 30s
  164. ['hh:mm\nMM-dd', ONE_MINUTE], // 1m
  165. ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m
  166. ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
  167. ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
  168. ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
  169. ['hh:mm\nMM-dd', ONE_HOUR], // 1h
  170. ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h
  171. ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h
  172. ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h
  173. ['MM-dd\nyyyy', ONE_DAY], // 1d
  174. ['MM-dd\nyyyy', ONE_DAY * 2], // 2d
  175. ['MM-dd\nyyyy', ONE_DAY * 3], // 3d
  176. ['MM-dd\nyyyy', ONE_DAY * 4], // 4d
  177. ['MM-dd\nyyyy', ONE_DAY * 5], // 5d
  178. ['MM-dd\nyyyy', ONE_DAY * 6], // 6d
  179. ['week', ONE_DAY * 7], // 7d
  180. ['MM-dd\nyyyy', ONE_DAY * 10], // 10d
  181. ['week', ONE_DAY * 14], // 2w
  182. ['week', ONE_DAY * 21], // 3w
  183. ['month', ONE_DAY * 31], // 1M
  184. ['week', ONE_DAY * 42], // 6w
  185. ['month', ONE_DAY * 62], // 2M
  186. ['week', ONE_DAY * 70], // 10w
  187. ['quarter', ONE_DAY * 95], // 3M
  188. ['month', ONE_DAY * 31 * 4], // 4M
  189. ['month', ONE_DAY * 31 * 5], // 5M
  190. ['half-year', ONE_DAY * 380 / 2], // 6M
  191. ['month', ONE_DAY * 31 * 8], // 8M
  192. ['month', ONE_DAY * 31 * 10], // 10M
  193. ['year', ONE_DAY * 380] // 1Y
  194. ];
  195. /**
  196. * @param {module:echarts/model/Model}
  197. * @return {module:echarts/scale/Time}
  198. */
  199. TimeScale.create = function (model) {
  200. return new TimeScale({useUTC: model.ecModel.get('useUTC')});
  201. };
  202. export default TimeScale;