mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Replaces moment with Grafanas DateTime (#16919)
* Wip: Initial commit * Refactor: Replaces moment.utc( * Refactor: replaces the last isMoment statements * Refactor: Removes almost all moment imports * Refactor: Moves moment_wrapper to grafana/ui * Refactor: Renames momentWrapper * Refactor: Removes one more moment import * Refactor: Removes unitOfTime import * Fix: Fixes Prettier error * Refactor: Renames DateTimeType to DateTime * Refactor: Renames isDateTimeType to isDateTime * Refactor: Renames dateTime to dateTime * Feature: Bans moment imports and types
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { GraphWithLegendProps } from './GraphWithLegend';
|
||||
import moment from 'moment';
|
||||
import { LegendDisplayMode } from '../Legend/Legend';
|
||||
import { dateTime } from '../../utils/moment_wrapper';
|
||||
// import { LegendList } from '../Legend/LegendList';
|
||||
|
||||
export const mockGraphWithLegendData = ({
|
||||
@@ -3287,8 +3287,8 @@ export const mockGraphWithLegendData = ({
|
||||
},
|
||||
],
|
||||
timeRange: {
|
||||
from: moment('2019-04-09T07:01:14.247Z'),
|
||||
to: moment('2019-04-09T13:01:14.247Z'),
|
||||
from: dateTime('2019-04-09T07:01:14.247Z'),
|
||||
to: dateTime('2019-04-09T13:01:14.247Z'),
|
||||
raw: {
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
|
||||
@@ -3,11 +3,11 @@ import _ from 'lodash';
|
||||
import React, { ReactElement } from 'react';
|
||||
import { GridCellProps } from 'react-virtualized';
|
||||
import { Table, Props } from './Table';
|
||||
import moment from 'moment';
|
||||
import { ValueFormatter, getValueFormat, getColorFromHexRgbOrName } from '../../utils/index';
|
||||
import { GrafanaTheme } from '../../types/theme';
|
||||
import { InterpolateFunction } from '../../types/panel';
|
||||
import { Field } from '../../types/data';
|
||||
import { dateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
export interface TableCellBuilderOptions {
|
||||
value: any;
|
||||
@@ -96,7 +96,7 @@ export function getCellBuilder(schema: Field, style: ColumnStyle | null, props:
|
||||
if (_.isArray(v)) {
|
||||
v = v[0];
|
||||
}
|
||||
let date = moment(v);
|
||||
let date = dateTime(v);
|
||||
if (false) {
|
||||
// TODO?????? this.props.isUTC) {
|
||||
date = date.utc();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import moment, { Moment } from 'moment';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { TimePicker } from './TimePicker';
|
||||
import { UseState } from '../../utils/storybook/UseState';
|
||||
import { withRighAlignedStory } from '../../utils/storybook/withRightAlignedStory';
|
||||
import { TimeFragment } from '../../types/time';
|
||||
import { dateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
const TimePickerStories = storiesOf('UI/TimePicker', module);
|
||||
export const popoverOptions = {
|
||||
@@ -177,9 +178,9 @@ TimePickerStories.add('default', () => {
|
||||
return (
|
||||
<UseState
|
||||
initialState={{
|
||||
from: moment(),
|
||||
to: moment(),
|
||||
raw: { from: 'now-6h' as string | Moment, to: 'now' as string | Moment },
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
|
||||
}}
|
||||
>
|
||||
{(value, updateValue) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import moment from 'moment';
|
||||
import { ButtonSelect } from '../Select/ButtonSelect';
|
||||
import { mapTimeOptionToTimeRange, mapTimeRangeToRangeString } from './time';
|
||||
import { Props as TimePickerPopoverProps } from './TimePickerPopover';
|
||||
@@ -8,6 +7,7 @@ import { PopperContent } from '../Tooltip/PopperController';
|
||||
import { Timezone } from '../../utils/datemath';
|
||||
import { TimeRange, TimeOption, TimeOptions } from '../../types/time';
|
||||
import { SelectOptionItem } from '../Select/Select';
|
||||
import { isDateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
export interface Props {
|
||||
value: TimeRange;
|
||||
@@ -266,7 +266,7 @@ export class TimePicker extends PureComponent<Props, State> {
|
||||
} = this.props;
|
||||
const options = this.mapTimeOptionsToSelectOptionItems(selectTimeOptions);
|
||||
const rangeString = mapTimeRangeToRangeString(value);
|
||||
const isAbsolute = moment.isMoment(value.raw.to);
|
||||
const isAbsolute = isDateTime(value.raw.to);
|
||||
|
||||
return (
|
||||
<div className="time-picker">
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Moment } from 'moment';
|
||||
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { TimePickerCalendar } from './TimePickerCalendar';
|
||||
import { UseState } from '../../utils/storybook/UseState';
|
||||
import { TimeFragment } from '../../types/time';
|
||||
|
||||
const TimePickerCalendarStories = storiesOf('UI/TimePicker/TimePickerCalendar', module);
|
||||
|
||||
TimePickerCalendarStories.addDecorator(withCenteredStory);
|
||||
|
||||
TimePickerCalendarStories.add('default', () => (
|
||||
<UseState initialState={'now-6h' as string | Moment}>
|
||||
<UseState initialState={'now-6h' as TimeFragment}>
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
<TimePickerCalendar
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import Calendar from 'react-calendar/dist/entry.nostyle';
|
||||
import moment, { Moment } from 'moment';
|
||||
import { TimeFragment } from '../../types/time';
|
||||
import { Timezone } from '../../utils/datemath';
|
||||
import { DateTime, dateTime, isDateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
import { stringToMoment } from './time';
|
||||
import { stringToDateTimeType } from './time';
|
||||
|
||||
export interface Props {
|
||||
value: TimeFragment;
|
||||
isTimezoneUtc: boolean;
|
||||
roundup?: boolean;
|
||||
timezone?: Timezone;
|
||||
onChange: (value: Moment) => void;
|
||||
onChange: (value: DateTime) => void;
|
||||
}
|
||||
|
||||
export class TimePickerCalendar extends PureComponent<Props> {
|
||||
@@ -22,15 +22,15 @@ export class TimePickerCalendar extends PureComponent<Props> {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(moment(date));
|
||||
onChange(dateTime(date));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, isTimezoneUtc, roundup, timezone } = this.props;
|
||||
const dateValue = moment.isMoment(value)
|
||||
const dateValue = isDateTime(value)
|
||||
? value.toDate()
|
||||
: stringToMoment(value, isTimezoneUtc, roundup, timezone).toDate();
|
||||
const calendarValue = dateValue instanceof Date && !isNaN(dateValue.getTime()) ? dateValue : moment().toDate();
|
||||
: stringToDateTimeType(value, isTimezoneUtc, roundup, timezone).toDate();
|
||||
const calendarValue = dateValue instanceof Date && !isNaN(dateValue.getTime()) ? dateValue : dateTime().toDate();
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { PureComponent, ChangeEvent } from 'react';
|
||||
import moment from 'moment';
|
||||
import { TimeFragment, TIME_FORMAT } from '../../types/time';
|
||||
|
||||
import { stringToMoment, isValidTimeString } from './time';
|
||||
import { Input } from '../Input/Input';
|
||||
import { stringToDateTimeType, isValidTimeString } from './time';
|
||||
import { isDateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
export interface Props {
|
||||
value: TimeFragment;
|
||||
@@ -22,7 +21,7 @@ export class TimePickerInput extends PureComponent<Props> {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
const parsed = stringToMoment(value, isTimezoneUtc);
|
||||
const parsed = stringToDateTimeType(value, isTimezoneUtc);
|
||||
const isValid = parsed.isValid();
|
||||
return isValid;
|
||||
};
|
||||
@@ -35,7 +34,7 @@ export class TimePickerInput extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
valueToString = (value: TimeFragment) => {
|
||||
if (moment.isMoment(value)) {
|
||||
if (isDateTime(value)) {
|
||||
return value.format(TIME_FORMAT);
|
||||
} else {
|
||||
return value;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { ComponentType } from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import moment from 'moment';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { TimePickerOptionGroup } from './TimePickerOptionGroup';
|
||||
import { TimeRange } from '../../types/time';
|
||||
import { withRighAlignedStory } from '../../utils/storybook/withRightAlignedStory';
|
||||
import { popoverOptions } from './TimePicker.story';
|
||||
import { dateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
const TimePickerOptionGroupStories = storiesOf('UI/TimePicker/TimePickerOptionGroup', module);
|
||||
|
||||
@@ -21,7 +21,7 @@ const data = {
|
||||
action('onPopoverClose fired')(timeRange);
|
||||
},
|
||||
popoverProps: {
|
||||
value: { from: moment(), to: moment(), raw: { from: 'now/d', to: 'now/d' } },
|
||||
value: { from: dateTime(), to: dateTime(), raw: { from: 'now/d', to: 'now/d' } },
|
||||
options: popoverOptions,
|
||||
isTimezoneUtc: false,
|
||||
onChange: (timeRange: TimeRange) => {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import moment, { Moment } from 'moment';
|
||||
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { TimePickerPopover } from './TimePickerPopover';
|
||||
import { UseState } from '../../utils/storybook/UseState';
|
||||
import { popoverOptions } from './TimePicker.story';
|
||||
import { dateTime, DateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
const TimePickerPopoverStories = storiesOf('UI/TimePicker/TimePickerPopover', module);
|
||||
|
||||
@@ -15,9 +15,9 @@ TimePickerPopoverStories.addDecorator(withCenteredStory);
|
||||
TimePickerPopoverStories.add('default', () => (
|
||||
<UseState
|
||||
initialState={{
|
||||
from: moment(),
|
||||
to: moment(),
|
||||
raw: { from: 'now-6h' as string | Moment, to: 'now' as string | Moment },
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now-6h' as string | DateTime, to: 'now' as string | DateTime },
|
||||
}}
|
||||
>
|
||||
{(value, updateValue) => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { Component, SyntheticEvent } from 'react';
|
||||
import { TimeRange, TimeOptions, TimeOption } from '../../types/time';
|
||||
import { Moment } from 'moment';
|
||||
|
||||
import { TimePickerCalendar } from './TimePickerCalendar';
|
||||
import { TimePickerInput } from './TimePickerInput';
|
||||
import { mapTimeOptionToTimeRange } from './time';
|
||||
import { Timezone } from '../../utils/datemath';
|
||||
import { DateTime } from '../../utils/moment_wrapper';
|
||||
|
||||
export interface Props {
|
||||
value: TimeRange;
|
||||
@@ -42,13 +42,13 @@ export class TimePickerPopover extends Component<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
onFromCalendarChanged = (value: Moment) => {
|
||||
onFromCalendarChanged = (value: DateTime) => {
|
||||
this.setState({
|
||||
value: { ...this.state.value, raw: { ...this.state.value.raw, from: value } },
|
||||
});
|
||||
};
|
||||
|
||||
onToCalendarChanged = (value: Moment) => {
|
||||
onToCalendarChanged = (value: DateTime) => {
|
||||
this.setState({
|
||||
value: { ...this.state.value, raw: { ...this.state.value.raw, to: value } },
|
||||
});
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import moment, { Moment } from 'moment';
|
||||
import { TimeOption, TimeRange, TIME_FORMAT } from '../../types/time';
|
||||
import { describeTimeRange } from '../../utils/rangeutil';
|
||||
import * as dateMath from '../../utils/datemath';
|
||||
import { dateTime, DateTime, toUtc } from '../../utils/moment_wrapper';
|
||||
|
||||
export const mapTimeOptionToTimeRange = (
|
||||
timeOption: TimeOption,
|
||||
isTimezoneUtc: boolean,
|
||||
timezone?: dateMath.Timezone
|
||||
): TimeRange => {
|
||||
const fromMoment = stringToMoment(timeOption.from, isTimezoneUtc, false, timezone);
|
||||
const toMoment = stringToMoment(timeOption.to, isTimezoneUtc, true, timezone);
|
||||
const fromMoment = stringToDateTimeType(timeOption.from, isTimezoneUtc, false, timezone);
|
||||
const toMoment = stringToDateTimeType(timeOption.to, isTimezoneUtc, true, timezone);
|
||||
|
||||
return { from: fromMoment, to: toMoment, raw: { from: timeOption.from, to: timeOption.to } };
|
||||
};
|
||||
|
||||
export const stringToMoment = (
|
||||
export const stringToDateTimeType = (
|
||||
value: string,
|
||||
isTimezoneUtc: boolean,
|
||||
roundUp?: boolean,
|
||||
timezone?: dateMath.Timezone
|
||||
): Moment => {
|
||||
): DateTime => {
|
||||
if (value.indexOf('now') !== -1) {
|
||||
if (!dateMath.isValid(value)) {
|
||||
return moment();
|
||||
return dateTime();
|
||||
}
|
||||
|
||||
const parsed = dateMath.parse(value, roundUp, timezone);
|
||||
return parsed || moment();
|
||||
return parsed || dateTime();
|
||||
}
|
||||
|
||||
if (isTimezoneUtc) {
|
||||
return moment.utc(value, TIME_FORMAT);
|
||||
return toUtc(value, TIME_FORMAT);
|
||||
}
|
||||
|
||||
return moment(value, TIME_FORMAT);
|
||||
return dateTime(value, TIME_FORMAT);
|
||||
};
|
||||
|
||||
export const mapTimeRangeToRangeString = (timeRange: TimeRange): string => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Moment } from 'moment';
|
||||
import { DateTime } from '../utils/moment_wrapper';
|
||||
|
||||
export interface RawTimeRange {
|
||||
from: Moment | string;
|
||||
to: Moment | string;
|
||||
from: DateTime | string;
|
||||
to: DateTime | string;
|
||||
}
|
||||
|
||||
export interface TimeRange {
|
||||
from: Moment;
|
||||
to: Moment;
|
||||
from: DateTime;
|
||||
to: DateTime;
|
||||
raw: RawTimeRange;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,6 @@ export interface TimeOptions {
|
||||
[key: string]: TimeOption[];
|
||||
}
|
||||
|
||||
export type TimeFragment = string | Moment;
|
||||
export type TimeFragment = string | DateTime;
|
||||
|
||||
export const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import sinon, { SinonFakeTimers } from 'sinon';
|
||||
|
||||
import * as dateMath from './datemath';
|
||||
import moment, { Moment, unitOfTime } from 'moment';
|
||||
import each from 'lodash/each';
|
||||
|
||||
import * as dateMath from './datemath';
|
||||
import { dateTime, DurationUnit, DateTime } from '../utils/moment_wrapper';
|
||||
|
||||
describe('DateMath', () => {
|
||||
const spans: unitOfTime.Base[] = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
|
||||
const spans: DurationUnit[] = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
|
||||
const anchor = '2014-01-01T06:06:06.666Z';
|
||||
const unix = moment(anchor).valueOf();
|
||||
const unix = dateTime(anchor).valueOf();
|
||||
const format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
||||
let clock: SinonFakeTimers;
|
||||
|
||||
@@ -55,13 +55,13 @@ describe('DateMath', () => {
|
||||
});
|
||||
|
||||
describe('subtraction', () => {
|
||||
let now: Moment;
|
||||
let anchored: Moment;
|
||||
let now: DateTime;
|
||||
let anchored: DateTime;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers(unix);
|
||||
now = moment();
|
||||
anchored = moment(anchor);
|
||||
now = dateTime();
|
||||
anchored = dateTime(anchor);
|
||||
});
|
||||
|
||||
each(spans, span => {
|
||||
@@ -83,11 +83,11 @@ describe('DateMath', () => {
|
||||
});
|
||||
|
||||
describe('rounding', () => {
|
||||
let now: Moment;
|
||||
let now: DateTime;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers(unix);
|
||||
now = moment();
|
||||
now = dateTime();
|
||||
});
|
||||
|
||||
each(spans, span => {
|
||||
@@ -116,17 +116,17 @@ describe('DateMath', () => {
|
||||
|
||||
describe('relative time to date parsing', () => {
|
||||
it('should handle negative time', () => {
|
||||
const date = dateMath.parseDateMath('-2d', moment([2014, 1, 5]));
|
||||
expect(date!.valueOf()).toEqual(moment([2014, 1, 3]).valueOf());
|
||||
const date = dateMath.parseDateMath('-2d', dateTime([2014, 1, 5]));
|
||||
expect(date!.valueOf()).toEqual(dateTime([2014, 1, 3]).valueOf());
|
||||
});
|
||||
|
||||
it('should handle multiple math expressions', () => {
|
||||
const date = dateMath.parseDateMath('-2d-6h', moment([2014, 1, 5]));
|
||||
expect(date!.valueOf()).toEqual(moment([2014, 1, 2, 18]).valueOf());
|
||||
const date = dateMath.parseDateMath('-2d-6h', dateTime([2014, 1, 5]));
|
||||
expect(date!.valueOf()).toEqual(dateTime([2014, 1, 2, 18]).valueOf());
|
||||
});
|
||||
|
||||
it('should return false when invalid expression', () => {
|
||||
const date = dateMath.parseDateMath('2', moment([2014, 1, 5]));
|
||||
const date = dateMath.parseDateMath('2', dateTime([2014, 1, 5]));
|
||||
expect(date).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import includes from 'lodash/includes';
|
||||
import isDate from 'lodash/isDate';
|
||||
import moment, { unitOfTime } from 'moment';
|
||||
import { DateTime, dateTime, toUtc, ISO_8601, isDateTime, DurationUnit } from '../utils/moment_wrapper';
|
||||
|
||||
const units: unitOfTime.Base[] = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
|
||||
const units: DurationUnit[] = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
|
||||
|
||||
export type Timezone = 'utc';
|
||||
|
||||
@@ -13,21 +13,17 @@ export type Timezone = 'utc';
|
||||
* @param roundUp See parseDateMath function.
|
||||
* @param timezone Only string 'utc' is acceptable here, for anything else, local timezone is used.
|
||||
*/
|
||||
export function parse(
|
||||
text: string | moment.Moment | Date,
|
||||
roundUp?: boolean,
|
||||
timezone?: Timezone
|
||||
): moment.Moment | undefined {
|
||||
export function parse(text: string | DateTime | Date, roundUp?: boolean, timezone?: Timezone): DateTime | undefined {
|
||||
if (!text) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof text !== 'string') {
|
||||
if (moment.isMoment(text)) {
|
||||
if (isDateTime(text)) {
|
||||
return text;
|
||||
}
|
||||
if (isDate(text)) {
|
||||
return moment(text);
|
||||
return dateTime(text);
|
||||
}
|
||||
// We got some non string which is not a moment nor Date. TS should be able to check for that but not always.
|
||||
return undefined;
|
||||
@@ -39,9 +35,9 @@ export function parse(
|
||||
|
||||
if (text.substring(0, 3) === 'now') {
|
||||
if (timezone === 'utc') {
|
||||
time = moment.utc();
|
||||
time = toUtc();
|
||||
} else {
|
||||
time = moment();
|
||||
time = dateTime();
|
||||
}
|
||||
mathString = text.substring('now'.length);
|
||||
} else {
|
||||
@@ -54,7 +50,7 @@ export function parse(
|
||||
mathString = text.substring(index + 2);
|
||||
}
|
||||
// We're going to just require ISO8601 timestamps, k?
|
||||
time = moment(parseString, moment.ISO_8601);
|
||||
time = dateTime(parseString, ISO_8601);
|
||||
}
|
||||
|
||||
if (!mathString.length) {
|
||||
@@ -70,13 +66,13 @@ export function parse(
|
||||
* by parse function. See parse function to see what is considered acceptable.
|
||||
* @param text
|
||||
*/
|
||||
export function isValid(text: string | moment.Moment): boolean {
|
||||
export function isValid(text: string | DateTime): boolean {
|
||||
const date = parse(text);
|
||||
if (!date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (moment.isMoment(date)) {
|
||||
if (isDateTime(date)) {
|
||||
return date.isValid();
|
||||
}
|
||||
|
||||
@@ -90,7 +86,7 @@ export function isValid(text: string | moment.Moment): boolean {
|
||||
* @param roundUp If true it will round the time to endOf time unit, otherwise to startOf time unit.
|
||||
*/
|
||||
// TODO: Had to revert Andrejs `time: moment.Moment` to `time: any`
|
||||
export function parseDateMath(mathString: string, time: any, roundUp?: boolean): moment.Moment | undefined {
|
||||
export function parseDateMath(mathString: string, time: any, roundUp?: boolean): DateTime | undefined {
|
||||
const dateTime = time;
|
||||
let i = 0;
|
||||
const len = mathString.length;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Libraries
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
// Utils
|
||||
import { getValueFormat } from './valueFormats/valueFormats';
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
DecimalCount,
|
||||
Field,
|
||||
} from '../types';
|
||||
import { DateTime, dateTime } from './moment_wrapper';
|
||||
|
||||
export type DisplayProcessor = (value: any) => DisplayValue;
|
||||
|
||||
@@ -91,18 +91,18 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
|
||||
return toStringProcessor;
|
||||
}
|
||||
|
||||
function toMoment(value: any, numeric: number, format: string): moment.Moment {
|
||||
function toMoment(value: any, numeric: number, format: string): DateTime {
|
||||
if (!isNaN(numeric)) {
|
||||
const v = moment(numeric);
|
||||
const v = dateTime(numeric);
|
||||
if (v.isValid()) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
const v = moment(value, format);
|
||||
const v = dateTime(value, format);
|
||||
if (v.isValid) {
|
||||
return v;
|
||||
}
|
||||
return moment(value); // moment will try to parse the format
|
||||
return dateTime(value); // moment will try to parse the format
|
||||
}
|
||||
|
||||
/** Will return any value as a number or NaN */
|
||||
|
||||
@@ -16,6 +16,7 @@ export * from './validate';
|
||||
export { getFlotPairs } from './flotPairs';
|
||||
export * from './object';
|
||||
export * from './fieldCache';
|
||||
export * from './moment_wrapper';
|
||||
|
||||
// Names are too general to export
|
||||
// rangeutils, datemath
|
||||
|
||||
89
packages/grafana-ui/src/utils/moment_wrapper.ts
Normal file
89
packages/grafana-ui/src/utils/moment_wrapper.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/* tslint:disable:import-blacklist ban ban-types */
|
||||
import moment, { MomentInput, DurationInputArg1 } from 'moment';
|
||||
|
||||
export interface DateTimeBuiltinFormat {
|
||||
__momentBuiltinFormatBrand: any;
|
||||
}
|
||||
export const ISO_8601: DateTimeBuiltinFormat = moment.ISO_8601;
|
||||
export type DateTimeInput = Date | string | number | Array<string | number> | DateTime; // null | undefined
|
||||
export type FormatInput = string | DateTimeBuiltinFormat | undefined;
|
||||
export type DurationInput = string | number | DateTimeDuration;
|
||||
export type DurationUnit =
|
||||
| 'year'
|
||||
| 'years'
|
||||
| 'y'
|
||||
| 'month'
|
||||
| 'months'
|
||||
| 'M'
|
||||
| 'week'
|
||||
| 'weeks'
|
||||
| 'w'
|
||||
| 'day'
|
||||
| 'days'
|
||||
| 'd'
|
||||
| 'hour'
|
||||
| 'hours'
|
||||
| 'h'
|
||||
| 'minute'
|
||||
| 'minutes'
|
||||
| 'm'
|
||||
| 'second'
|
||||
| 'seconds'
|
||||
| 's'
|
||||
| 'millisecond'
|
||||
| 'milliseconds'
|
||||
| 'ms'
|
||||
| 'quarter'
|
||||
| 'quarters'
|
||||
| 'Q';
|
||||
|
||||
export interface DateTimeLocale {
|
||||
firstDayOfWeek: () => number;
|
||||
}
|
||||
|
||||
export interface DateTimeDuration {
|
||||
asHours: () => number;
|
||||
}
|
||||
|
||||
export interface DateTime extends Object {
|
||||
add: (amount?: DateTimeInput, unit?: DurationUnit) => DateTime;
|
||||
endOf: (unitOfTime: DurationUnit) => DateTime;
|
||||
format: (formatInput?: FormatInput) => string;
|
||||
fromNow: (withoutSuffix?: boolean) => string;
|
||||
from: (formaInput: DateTimeInput) => string;
|
||||
isSame: (input?: DateTimeInput, granularity?: DurationUnit) => boolean;
|
||||
isValid: () => boolean;
|
||||
local: () => DateTime;
|
||||
locale: (locale: string) => DateTime;
|
||||
startOf: (unitOfTime: DurationUnit) => DateTime;
|
||||
subtract: (amount?: DateTimeInput, unit?: DurationUnit) => DateTime;
|
||||
toDate: () => Date;
|
||||
toISOString: () => string;
|
||||
valueOf: () => number;
|
||||
unix: () => number;
|
||||
utc: () => DateTime;
|
||||
}
|
||||
|
||||
export const setLocale = (language: string) => {
|
||||
moment.locale(language);
|
||||
};
|
||||
|
||||
export const getLocaleData = (): DateTimeLocale => {
|
||||
return moment.localeData();
|
||||
};
|
||||
|
||||
export const isDateTime = (value: any): value is DateTime => {
|
||||
return moment.isMoment(value);
|
||||
};
|
||||
|
||||
export const toUtc = (input?: DateTimeInput, formatInput?: FormatInput): DateTime => {
|
||||
return moment.utc(input as MomentInput, formatInput) as DateTime;
|
||||
};
|
||||
|
||||
export const toDuration = (input?: DurationInput, unit?: DurationUnit): DateTimeDuration => {
|
||||
return moment.duration(input as DurationInputArg1, unit) as DateTimeDuration;
|
||||
};
|
||||
|
||||
export const dateTime = (input?: DateTimeInput, formatInput?: FormatInput): DateTime => {
|
||||
return moment(input as MomentInput, formatInput) as DateTime;
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
guessFieldTypeFromValue,
|
||||
} from './processSeriesData';
|
||||
import { FieldType, TimeSeries } from '../types/data';
|
||||
import moment from 'moment';
|
||||
import { dateTime } from './moment_wrapper';
|
||||
|
||||
describe('toSeriesData', () => {
|
||||
it('converts timeseries to series', () => {
|
||||
@@ -45,7 +45,7 @@ describe('toSeriesData', () => {
|
||||
expect(guessFieldTypeFromValue(true)).toBe(FieldType.boolean);
|
||||
expect(guessFieldTypeFromValue(false)).toBe(FieldType.boolean);
|
||||
expect(guessFieldTypeFromValue(new Date())).toBe(FieldType.time);
|
||||
expect(guessFieldTypeFromValue(moment())).toBe(FieldType.time);
|
||||
expect(guessFieldTypeFromValue(dateTime())).toBe(FieldType.time);
|
||||
});
|
||||
|
||||
it('Guess Colum Types from strings', () => {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import isString from 'lodash/isString';
|
||||
import isBoolean from 'lodash/isBoolean';
|
||||
import moment from 'moment';
|
||||
|
||||
// Types
|
||||
import { SeriesData, Field, TimeSeries, FieldType, TableData } from '../types/index';
|
||||
import { isDateTime } from './moment_wrapper';
|
||||
|
||||
function convertTableToSeriesData(table: TableData): SeriesData {
|
||||
return {
|
||||
@@ -73,7 +73,7 @@ export function guessFieldTypeFromValue(v: any): FieldType {
|
||||
return FieldType.boolean;
|
||||
}
|
||||
|
||||
if (v instanceof Date || v instanceof moment) {
|
||||
if (v instanceof Date || isDateTime(v)) {
|
||||
return FieldType.time;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// @ts-ignore
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import { RawTimeRange } from '../types/time';
|
||||
|
||||
import * as dateMath from './datemath';
|
||||
import { isDateTime, DateTime } from '../utils/moment_wrapper';
|
||||
|
||||
const spans: { [key: string]: { display: string; section?: number } } = {
|
||||
s: { display: 'second' },
|
||||
@@ -85,7 +85,7 @@ export function getRelativeTimesList(timepickerSettings: any, currentDisplay: an
|
||||
return groups;
|
||||
}
|
||||
|
||||
function formatDate(date: any) {
|
||||
function formatDate(date: DateTime) {
|
||||
return date.format(absoluteFormat);
|
||||
}
|
||||
|
||||
@@ -139,16 +139,16 @@ export function describeTimeRange(range: RawTimeRange): string {
|
||||
return option.display;
|
||||
}
|
||||
|
||||
if (moment.isMoment(range.from) && moment.isMoment(range.to)) {
|
||||
if (isDateTime(range.from) && isDateTime(range.to)) {
|
||||
return formatDate(range.from) + ' to ' + formatDate(range.to);
|
||||
}
|
||||
|
||||
if (moment.isMoment(range.from)) {
|
||||
if (isDateTime(range.from)) {
|
||||
const toMoment = dateMath.parse(range.to, true);
|
||||
return toMoment ? formatDate(range.from) + ' to ' + toMoment.fromNow() : '';
|
||||
}
|
||||
|
||||
if (moment.isMoment(range.to)) {
|
||||
if (isDateTime(range.to)) {
|
||||
const from = dateMath.parse(range.from, false);
|
||||
return from ? from.fromNow() + ' to ' + formatDate(range.to) : '';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import moment from 'moment';
|
||||
import {
|
||||
dateTimeAsIso,
|
||||
dateTimeAsUS,
|
||||
@@ -9,11 +8,12 @@ import {
|
||||
toDurationInMilliseconds,
|
||||
toDurationInSeconds,
|
||||
} from './dateTimeFormatters';
|
||||
import { toUtc, dateTime } from '../moment_wrapper';
|
||||
|
||||
describe('date time formats', () => {
|
||||
const epoch = 1505634997920;
|
||||
const utcTime = moment.utc(epoch);
|
||||
const browserTime = moment(epoch);
|
||||
const utcTime = toUtc(epoch);
|
||||
const browserTime = dateTime(epoch);
|
||||
|
||||
it('should format as iso date', () => {
|
||||
const expected = browserTime.format('YYYY-MM-DD HH:mm:ss');
|
||||
@@ -28,14 +28,14 @@ describe('date time formats', () => {
|
||||
});
|
||||
|
||||
it('should format as iso date and skip date when today', () => {
|
||||
const now = moment();
|
||||
const now = dateTime();
|
||||
const expected = now.format('HH:mm:ss');
|
||||
const actual = dateTimeAsIso(now.valueOf(), 0, 0, false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as iso date (in UTC) and skip date when today', () => {
|
||||
const now = moment.utc();
|
||||
const now = toUtc();
|
||||
const expected = now.format('HH:mm:ss');
|
||||
const actual = dateTimeAsIso(now.valueOf(), 0, 0, true);
|
||||
expect(actual).toBe(expected);
|
||||
@@ -54,42 +54,42 @@ describe('date time formats', () => {
|
||||
});
|
||||
|
||||
it('should format as US date and skip date when today', () => {
|
||||
const now = moment();
|
||||
const now = dateTime();
|
||||
const expected = now.format('h:mm:ss a');
|
||||
const actual = dateTimeAsUS(now.valueOf(), 0, 0, false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as US date (in UTC) and skip date when today', () => {
|
||||
const now = moment.utc();
|
||||
const now = toUtc();
|
||||
const expected = now.format('h:mm:ss a');
|
||||
const actual = dateTimeAsUS(now.valueOf(), 0, 0, true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with days', () => {
|
||||
const daysAgo = moment().add(-7, 'd');
|
||||
const daysAgo = dateTime().add(-7, 'd');
|
||||
const expected = '7 days ago';
|
||||
const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with days (in UTC)', () => {
|
||||
const daysAgo = moment.utc().add(-7, 'd');
|
||||
const daysAgo = toUtc().add(-7, 'd');
|
||||
const expected = '7 days ago';
|
||||
const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, true);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with minutes', () => {
|
||||
const daysAgo = moment().add(-2, 'm');
|
||||
const daysAgo = dateTime().add(-2, 'm');
|
||||
const expected = '2 minutes ago';
|
||||
const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, false);
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should format as from now with minutes (in UTC)', () => {
|
||||
const daysAgo = moment.utc().add(-2, 'm');
|
||||
const daysAgo = toUtc().add(-2, 'm');
|
||||
const expected = '2 minutes ago';
|
||||
const actual = dateTimeFromNow(daysAgo.valueOf(), 0, 0, true);
|
||||
expect(actual).toBe(expected);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { toFixed, toFixedScaled } from './valueFormats';
|
||||
import { DecimalCount } from '../../types';
|
||||
import moment from 'moment';
|
||||
import { toUtc, toDuration as duration, dateTime } from '../moment_wrapper';
|
||||
|
||||
interface IntervalsInSeconds {
|
||||
[interval: string]: number;
|
||||
@@ -237,7 +237,7 @@ export function toClock(size: number, decimals?: DecimalCount) {
|
||||
|
||||
// < 1 second
|
||||
if (size < 1000) {
|
||||
return moment.utc(size).format('SSS\\m\\s');
|
||||
return toUtc(size).format('SSS\\m\\s');
|
||||
}
|
||||
|
||||
// < 1 minute
|
||||
@@ -246,7 +246,7 @@ export function toClock(size: number, decimals?: DecimalCount) {
|
||||
if (decimals === 0) {
|
||||
format = 'ss\\s';
|
||||
}
|
||||
return moment.utc(size).format(format);
|
||||
return toUtc(size).format(format);
|
||||
}
|
||||
|
||||
// < 1 hour
|
||||
@@ -257,12 +257,12 @@ export function toClock(size: number, decimals?: DecimalCount) {
|
||||
} else if (decimals === 1) {
|
||||
format = 'mm\\m:ss\\s';
|
||||
}
|
||||
return moment.utc(size).format(format);
|
||||
return toUtc(size).format(format);
|
||||
}
|
||||
|
||||
let format = 'mm\\m:ss\\s:SSS\\m\\s';
|
||||
|
||||
const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`;
|
||||
const hours = `${('0' + Math.floor(duration(size, 'milliseconds').asHours())).slice(-2)}h`;
|
||||
|
||||
if (decimals === 0) {
|
||||
format = '';
|
||||
@@ -272,7 +272,7 @@ export function toClock(size: number, decimals?: DecimalCount) {
|
||||
format = 'mm\\m:ss\\s';
|
||||
}
|
||||
|
||||
return format ? `${hours}:${moment.utc(size).format(format)}` : hours;
|
||||
return format ? `${hours}:${toUtc(size).format(format)}` : hours;
|
||||
}
|
||||
|
||||
export function toDurationInMilliseconds(size: number, decimals: DecimalCount) {
|
||||
@@ -307,24 +307,24 @@ export function toClockSeconds(size: number, decimals: DecimalCount) {
|
||||
}
|
||||
|
||||
export function dateTimeAsIso(value: number, decimals: DecimalCount, scaledDecimals: DecimalCount, isUtc?: boolean) {
|
||||
const time = isUtc ? moment.utc(value) : moment(value);
|
||||
const time = isUtc ? toUtc(value) : dateTime(value);
|
||||
|
||||
if (moment().isSame(value, 'day')) {
|
||||
if (dateTime().isSame(value, 'day')) {
|
||||
return time.format('HH:mm:ss');
|
||||
}
|
||||
return time.format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
export function dateTimeAsUS(value: number, decimals: DecimalCount, scaledDecimals: DecimalCount, isUtc?: boolean) {
|
||||
const time = isUtc ? moment.utc(value) : moment(value);
|
||||
const time = isUtc ? toUtc(value) : dateTime(value);
|
||||
|
||||
if (moment().isSame(value, 'day')) {
|
||||
if (dateTime().isSame(value, 'day')) {
|
||||
return time.format('h:mm:ss a');
|
||||
}
|
||||
return time.format('MM/DD/YYYY h:mm:ss a');
|
||||
}
|
||||
|
||||
export function dateTimeFromNow(value: number, decimals: DecimalCount, scaledDecimals: DecimalCount, isUtc?: boolean) {
|
||||
const time = isUtc ? moment.utc(value) : moment(value);
|
||||
const time = isUtc ? toUtc(value) : dateTime(value);
|
||||
return time.fromNow();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"import-blacklist": [true, ["^@grafana/ui.*"]]
|
||||
"import-blacklist": [true, "moment", ["^@grafana/ui.*"]]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user