| /* |
| * 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 * as zrUtil from 'zrender/src/core/util'; |
| import {TimeAxisLabelFormatterOption} from './../coord/axisCommonTypes'; |
| import * as numberUtil from './number'; |
| import {TimeScaleTick} from './types'; |
| import { getDefaultLocaleModel, getLocaleModel, SYSTEM_LANG, LocaleOption } from '../core/locale'; |
| import Model from '../model/Model'; |
| |
| export const ONE_SECOND = 1000; |
| export const ONE_MINUTE = ONE_SECOND * 60; |
| export const ONE_HOUR = ONE_MINUTE * 60; |
| export const ONE_DAY = ONE_HOUR * 24; |
| export const ONE_YEAR = ONE_DAY * 365; |
| |
| export const defaultLeveledFormatter = { |
| year: '{yyyy}', |
| month: '{MMM}', |
| day: '{d}', |
| hour: '{HH}:{mm}', |
| minute: '{HH}:{mm}', |
| second: '{HH}:{mm}:{ss}', |
| millisecond: '{HH}:{mm}:{ss} {SSS}', |
| none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}' |
| }; |
| |
| const fullDayFormatter = '{yyyy}-{MM}-{dd}'; |
| |
| export const fullLeveledFormatter = { |
| year: '{yyyy}', |
| month: '{yyyy}-{MM}', |
| day: fullDayFormatter, |
| hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour, |
| minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute, |
| second: fullDayFormatter + ' ' + defaultLeveledFormatter.second, |
| millisecond: defaultLeveledFormatter.none |
| }; |
| |
| export type PrimaryTimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' |
| | 'day' | 'month' | 'year'; |
| export type TimeUnit = PrimaryTimeUnit | 'half-year' | 'quarter' | 'week' |
| | 'half-week' | 'half-day' | 'quarter-day'; |
| |
| export const primaryTimeUnits: PrimaryTimeUnit[] = [ |
| 'year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond' |
| ]; |
| export const timeUnits: TimeUnit[] = [ |
| 'year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', |
| 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond' |
| ]; |
| |
| export function pad(str: string | number, len: number): string { |
| str += ''; |
| return '0000'.substr(0, len - (str as string).length) + str; |
| } |
| |
| export function getPrimaryTimeUnit(timeUnit: TimeUnit): PrimaryTimeUnit { |
| switch (timeUnit) { |
| case 'half-year': |
| case 'quarter': |
| return 'month'; |
| case 'week': |
| case 'half-week': |
| return 'day'; |
| case 'half-day': |
| case 'quarter-day': |
| return 'hour'; |
| default: |
| // year, minutes, second, milliseconds |
| return timeUnit; |
| } |
| } |
| |
| export function isPrimaryTimeUnit(timeUnit: TimeUnit): boolean { |
| return timeUnit === getPrimaryTimeUnit(timeUnit); |
| } |
| |
| export function getDefaultFormatPrecisionOfInterval(timeUnit: PrimaryTimeUnit): PrimaryTimeUnit { |
| switch (timeUnit) { |
| case 'year': |
| case 'month': |
| return 'day'; |
| case 'millisecond': |
| return 'millisecond'; |
| default: |
| // Also for day, hour, minute, second |
| return 'second'; |
| } |
| } |
| |
| export function format( |
| // Note: The result based on `isUTC` are totally different, which can not be just simply |
| // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory. |
| time: unknown, template: string, isUTC: boolean, lang?: string | Model<LocaleOption> |
| ): string { |
| const date = numberUtil.parseDate(time); |
| const y = date[fullYearGetterName(isUTC)](); |
| const M = date[monthGetterName(isUTC)]() + 1; |
| const q = Math.floor((M - 1) / 4) + 1; |
| const d = date[dateGetterName(isUTC)](); |
| const e = date['get' + (isUTC ? 'UTC' : '') + 'Day' as 'getDay' | 'getUTCDay'](); |
| const H = date[hoursGetterName(isUTC)](); |
| const h = (H - 1) % 12 + 1; |
| const m = date[minutesGetterName(isUTC)](); |
| const s = date[secondsGetterName(isUTC)](); |
| const S = date[millisecondsGetterName(isUTC)](); |
| |
| |
| const localeModel = lang instanceof Model ? lang |
| : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel(); |
| const timeModel = localeModel.getModel('time'); |
| const month = timeModel.get('month'); |
| const monthAbbr = timeModel.get('monthAbbr'); |
| const dayOfWeek = timeModel.get('dayOfWeek'); |
| const dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr'); |
| |
| return (template || '') |
| .replace(/{yyyy}/g, y + '') |
| .replace(/{yy}/g, y % 100 + '') |
| .replace(/{Q}/g, q + '') |
| .replace(/{MMMM}/g, month[M - 1]) |
| .replace(/{MMM}/g, monthAbbr[M - 1]) |
| .replace(/{MM}/g, pad(M, 2)) |
| .replace(/{M}/g, M + '') |
| .replace(/{dd}/g, pad(d, 2)) |
| .replace(/{d}/g, d + '') |
| .replace(/{eeee}/g, dayOfWeek[e]) |
| .replace(/{ee}/g, dayOfWeekAbbr[e]) |
| .replace(/{e}/g, e + '') |
| .replace(/{HH}/g, pad(H, 2)) |
| .replace(/{H}/g, H + '') |
| .replace(/{hh}/g, pad(h + '', 2)) |
| .replace(/{h}/g, h + '') |
| .replace(/{mm}/g, pad(m, 2)) |
| .replace(/{m}/g, m + '') |
| .replace(/{ss}/g, pad(s, 2)) |
| .replace(/{s}/g, s + '') |
| .replace(/{SSS}/g, pad(S, 3)) |
| .replace(/{S}/g, S + ''); |
| } |
| |
| export function leveledFormat( |
| tick: TimeScaleTick, |
| idx: number, |
| formatter: TimeAxisLabelFormatterOption, |
| lang: string | Model<LocaleOption>, |
| isUTC: boolean |
| ) { |
| let template = null; |
| if (typeof formatter === 'string') { |
| // Single formatter for all units at all levels |
| template = formatter; |
| } |
| else if (typeof formatter === 'function') { |
| // Callback formatter |
| template = formatter(tick.value, idx, { |
| level: tick.level |
| }); |
| } |
| else { |
| const defaults = zrUtil.extend({}, defaultLeveledFormatter); |
| if (tick.level > 0) { |
| for (let i = 0; i < primaryTimeUnits.length; ++i) { |
| defaults[primaryTimeUnits[i]] = `{primary|${defaults[primaryTimeUnits[i]]}}`; |
| } |
| } |
| |
| const mergedFormatter = (formatter |
| ? (formatter.inherit === false |
| ? formatter // Use formatter with bigger units |
| : zrUtil.defaults(formatter, defaults) |
| ) |
| : defaults) as any; |
| |
| const unit = getUnitFromValue(tick.value, isUTC); |
| if (mergedFormatter[unit]) { |
| template = mergedFormatter[unit]; |
| } |
| else if (mergedFormatter.inherit) { |
| // Unit formatter is not defined and should inherit from bigger units |
| const targetId = timeUnits.indexOf(unit); |
| for (let i = targetId - 1; i >= 0; --i) { |
| if (mergedFormatter[unit]) { |
| template = mergedFormatter[unit]; |
| break; |
| } |
| } |
| template = template || defaults.none; |
| } |
| |
| if (zrUtil.isArray(template)) { |
| let levelId = tick.level == null |
| ? 0 |
| : (tick.level >= 0 ? tick.level : template.length + tick.level); |
| levelId = Math.min(levelId, template.length - 1); |
| template = template[levelId]; |
| } |
| } |
| |
| return format(new Date(tick.value), template, isUTC, lang); |
| } |
| |
| export function getUnitFromValue( |
| value: number | string | Date, |
| isUTC: boolean |
| ): PrimaryTimeUnit { |
| const date = numberUtil.parseDate(value); |
| const M = (date as any)[monthGetterName(isUTC)]() + 1; |
| const d = (date as any)[dateGetterName(isUTC)](); |
| const h = (date as any)[hoursGetterName(isUTC)](); |
| const m = (date as any)[minutesGetterName(isUTC)](); |
| const s = (date as any)[secondsGetterName(isUTC)](); |
| const S = (date as any)[millisecondsGetterName(isUTC)](); |
| |
| const isSecond = S === 0; |
| const isMinute = isSecond && s === 0; |
| const isHour = isMinute && m === 0; |
| const isDay = isHour && h === 0; |
| const isMonth = isDay && d === 1; |
| const isYear = isMonth && M === 1; |
| |
| if (isYear) { |
| return 'year'; |
| } |
| else if (isMonth) { |
| return 'month'; |
| } |
| else if (isDay) { |
| return 'day'; |
| } |
| else if (isHour) { |
| return 'hour'; |
| } |
| else if (isMinute) { |
| return 'minute'; |
| } |
| else if (isSecond) { |
| return 'second'; |
| } |
| else { |
| return 'millisecond'; |
| } |
| } |
| |
| export function getUnitValue( |
| value: number | Date, |
| unit: TimeUnit, |
| isUTC: boolean |
| ) : number { |
| const date = typeof value === 'number' |
| ? numberUtil.parseDate(value) |
| : value; |
| unit = unit || getUnitFromValue(value, isUTC); |
| |
| switch (unit) { |
| case 'year': |
| return date[fullYearGetterName(isUTC)](); |
| case 'half-year': |
| return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0; |
| case 'quarter': |
| return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4); |
| case 'month': |
| return date[monthGetterName(isUTC)](); |
| case 'day': |
| return date[dateGetterName(isUTC)](); |
| case 'half-day': |
| return date[hoursGetterName(isUTC)]() / 24; |
| case 'hour': |
| return date[hoursGetterName(isUTC)](); |
| case 'minute': |
| return date[minutesGetterName(isUTC)](); |
| case 'second': |
| return date[secondsGetterName(isUTC)](); |
| case 'millisecond': |
| return date[millisecondsGetterName(isUTC)](); |
| } |
| } |
| |
| export function fullYearGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCFullYear' : 'getFullYear'; |
| } |
| |
| export function monthGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCMonth' : 'getMonth'; |
| } |
| |
| export function dateGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCDate' : 'getDate'; |
| } |
| |
| export function hoursGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCHours' : 'getHours'; |
| } |
| |
| export function minutesGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCMinutes' : 'getMinutes'; |
| } |
| |
| export function secondsGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCSeconds' : 'getSeconds'; |
| } |
| |
| export function millisecondsGetterName(isUTC: boolean) { |
| return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds'; |
| } |
| |
| export function fullYearSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCFullYear' : 'setFullYear'; |
| } |
| |
| export function monthSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCMonth' : 'setMonth'; |
| } |
| |
| export function dateSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCDate' : 'setDate'; |
| } |
| |
| export function hoursSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCHours' : 'setHours'; |
| } |
| |
| export function minutesSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCMinutes' : 'setMinutes'; |
| } |
| |
| export function secondsSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCSeconds' : 'setSeconds'; |
| } |
| |
| export function millisecondsSetterName(isUTC: boolean) { |
| return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds'; |
| } |