|  | /* | 
|  | * 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) / 3) + 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 (zrUtil.isString(formatter)) { | 
|  | // Single formatter for all units at all levels | 
|  | template = formatter; | 
|  | } | 
|  | else if (zrUtil.isFunction(formatter)) { | 
|  | // 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 = zrUtil.isNumber(value) | 
|  | ? 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'; | 
|  | } |