Ver código fonte

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
Hugo Häggmark 6 anos atrás
pai
commit
ceb9f0855b
86 arquivos alterados com 582 adições e 483 exclusões
  1. 3 3
      packages/grafana-ui/src/components/Graph/mockGraphWithLegendData.ts
  2. 2 2
      packages/grafana-ui/src/components/Table/TableCellBuilder.tsx
  3. 5 4
      packages/grafana-ui/src/components/TimePicker/TimePicker.story.tsx
  4. 2 2
      packages/grafana-ui/src/components/TimePicker/TimePicker.tsx
  5. 2 2
      packages/grafana-ui/src/components/TimePicker/TimePickerCalendar.story.tsx
  6. 7 7
      packages/grafana-ui/src/components/TimePicker/TimePickerCalendar.tsx
  7. 4 5
      packages/grafana-ui/src/components/TimePicker/TimePickerInput.tsx
  8. 2 2
      packages/grafana-ui/src/components/TimePicker/TimePickerOptionGroup.story.tsx
  9. 4 4
      packages/grafana-ui/src/components/TimePicker/TimePickerPopover.story.tsx
  10. 3 3
      packages/grafana-ui/src/components/TimePicker/TimePickerPopover.tsx
  11. 9 9
      packages/grafana-ui/src/components/TimePicker/time.ts
  12. 6 6
      packages/grafana-ui/src/types/time.ts
  13. 15 15
      packages/grafana-ui/src/utils/datemath.test.ts
  14. 11 15
      packages/grafana-ui/src/utils/datemath.ts
  15. 5 5
      packages/grafana-ui/src/utils/displayValue.ts
  16. 1 0
      packages/grafana-ui/src/utils/index.ts
  17. 89 0
      packages/grafana-ui/src/utils/moment_wrapper.ts
  18. 2 2
      packages/grafana-ui/src/utils/processSeriesData.test.ts
  19. 2 2
      packages/grafana-ui/src/utils/processSeriesData.ts
  20. 5 5
      packages/grafana-ui/src/utils/rangeutil.ts
  21. 11 11
      packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.test.ts
  22. 11 11
      packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.ts
  23. 1 1
      packages/grafana-ui/tslint.json
  24. 2 2
      public/app/app.ts
  25. 3 3
      public/app/core/filters/filters.ts
  26. 2 2
      public/app/core/logs_model.ts
  27. 4 4
      public/app/core/specs/rangeutil.test.ts
  28. 7 6
      public/app/core/utils/explore.ts
  29. 3 3
      public/app/core/utils/file_export.ts
  30. 2 2
      public/app/features/alerting/state/reducers.ts
  31. 2 2
      public/app/features/annotations/event_editor.ts
  32. 2 2
      public/app/features/dashboard/components/ShareModal/ShareModalCtrl.ts
  33. 7 7
      public/app/features/dashboard/components/TimePicker/TimePickerCtrl.ts
  34. 4 4
      public/app/features/dashboard/components/TimePicker/validation.ts
  35. 3 3
      public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.ts
  36. 1 0
      public/app/features/dashboard/services/DashboardLoaderSrv.ts
  37. 5 5
      public/app/features/dashboard/services/TimeSrv.test.ts
  38. 12 12
      public/app/features/dashboard/services/TimeSrv.ts
  39. 7 7
      public/app/features/dashboard/state/DashboardModel.ts
  40. 3 3
      public/app/features/dashboard/state/PanelQueryRunner.test.ts
  41. 13 13
      public/app/features/dashboard/utils/panel.test.ts
  42. 3 3
      public/app/features/explore/GraphContainer.tsx
  43. 3 3
      public/app/features/explore/QueryEditor.tsx
  44. 39 39
      public/app/features/explore/TimePicker.test.tsx
  45. 9 9
      public/app/features/explore/TimePicker.tsx
  46. 2 2
      public/app/features/explore/state/actions.test.ts
  47. 3 3
      public/app/features/explore/state/actions.ts
  48. 1 0
      public/app/features/plugins/plugin_loader.ts
  49. 3 3
      public/app/features/templating/specs/variable_srv.test.ts
  50. 2 2
      public/app/plugins/datasource/elasticsearch/datasource.ts
  51. 4 4
      public/app/plugins/datasource/elasticsearch/index_pattern.ts
  52. 8 8
      public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts
  53. 2 2
      public/app/plugins/datasource/elasticsearch/specs/index_pattern.test.ts
  54. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts
  55. 2 2
      public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.test.ts
  56. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/response_parser.ts
  57. 5 5
      public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts
  58. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/response_parser.ts
  59. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts
  60. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.test.ts
  61. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/response_parser.ts
  62. 4 4
      public/app/plugins/datasource/grafana-azure-monitor-datasource/log_analytics/querystring_builder.test.ts
  63. 3 3
      public/app/plugins/datasource/grafana-azure-monitor-datasource/log_analytics/querystring_builder.ts
  64. 3 3
      public/app/plugins/datasource/grafana/specs/datasource.test.ts
  65. 3 3
      public/app/plugins/datasource/graphite/specs/datasource.test.ts
  66. 2 2
      public/app/plugins/datasource/loki/language_provider.ts
  67. 5 5
      public/app/plugins/datasource/mssql/specs/datasource.test.ts
  68. 5 5
      public/app/plugins/datasource/mysql/specs/datasource.test.ts
  69. 5 5
      public/app/plugins/datasource/postgres/specs/datasource.test.ts
  70. 2 2
      public/app/plugins/datasource/prometheus/language_provider.ts
  71. 7 7
      public/app/plugins/datasource/prometheus/specs/datasource.test.ts
  72. 3 3
      public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts
  73. 3 3
      public/app/plugins/datasource/stackdriver/specs/datasource.test.ts
  74. 3 3
      public/app/plugins/datasource/testdata/query_ctrl.ts
  75. 2 2
      public/app/plugins/panel/alertlist/module.ts
  76. 3 3
      public/app/plugins/panel/graph/graph.ts
  77. 9 9
      public/app/plugins/panel/graph/specs/graph.test.ts
  78. 4 4
      public/app/plugins/panel/graph/specs/graph_ctrl.test.ts
  79. 97 95
      public/app/plugins/panel/graph/specs/time_region_manager.test.ts
  80. 7 4
      public/app/plugins/panel/graph/time_region_manager.ts
  81. 3 3
      public/app/plugins/panel/heatmap/rendering.ts
  82. 4 4
      public/app/plugins/panel/heatmap/specs/heatmap_ctrl.test.ts
  83. 3 3
      public/app/plugins/panel/singlestat/specs/singlestat.test.ts
  84. 2 2
      public/app/plugins/panel/table/renderer.ts
  85. 2 2
      public/test/helpers/getQueryOptions.ts
  86. 10 5
      tslint.json

+ 3 - 3
packages/grafana-ui/src/components/Graph/mockGraphWithLegendData.ts

@@ -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',

+ 2 - 2
packages/grafana-ui/src/components/Table/TableCellBuilder.tsx

@@ -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();

+ 5 - 4
packages/grafana-ui/src/components/TimePicker/TimePicker.story.tsx

@@ -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) => {

+ 2 - 2
packages/grafana-ui/src/components/TimePicker/TimePicker.tsx

@@ -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">

+ 2 - 2
packages/grafana-ui/src/components/TimePicker/TimePickerCalendar.story.tsx

@@ -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

+ 7 - 7
packages/grafana-ui/src/components/TimePicker/TimePickerCalendar.tsx

@@ -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

+ 4 - 5
packages/grafana-ui/src/components/TimePicker/TimePickerInput.tsx

@@ -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;

+ 2 - 2
packages/grafana-ui/src/components/TimePicker/TimePickerOptionGroup.story.tsx

@@ -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) => {

+ 4 - 4
packages/grafana-ui/src/components/TimePicker/TimePickerPopover.story.tsx

@@ -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) => {

+ 3 - 3
packages/grafana-ui/src/components/TimePicker/TimePickerPopover.tsx

@@ -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 } },
     });

+ 9 - 9
packages/grafana-ui/src/components/TimePicker/time.ts

@@ -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 => {

+ 6 - 6
packages/grafana-ui/src/types/time.ts

@@ -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';

+ 15 - 15
packages/grafana-ui/src/utils/datemath.test.ts

@@ -1,13 +1,13 @@
 import sinon, { SinonFakeTimers } from 'sinon';
+import each from 'lodash/each';
 
 import * as dateMath from './datemath';
-import moment, { Moment, unitOfTime } from 'moment';
-import each from 'lodash/each';
+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);
     });
   });

+ 11 - 15
packages/grafana-ui/src/utils/datemath.ts

@@ -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;

+ 5 - 5
packages/grafana-ui/src/utils/displayValue.ts

@@ -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 */

+ 1 - 0
packages/grafana-ui/src/utils/index.ts

@@ -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 - 0
packages/grafana-ui/src/utils/moment_wrapper.ts

@@ -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;
+};

+ 2 - 2
packages/grafana-ui/src/utils/processSeriesData.test.ts

@@ -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 - 2
packages/grafana-ui/src/utils/processSeriesData.ts

@@ -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;
   }
 

+ 5 - 5
packages/grafana-ui/src/utils/rangeutil.ts

@@ -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) : '';
   }

+ 11 - 11
packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.test.ts

@@ -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);

+ 11 - 11
packages/grafana-ui/src/utils/valueFormats/dateTimeFormatters.ts

@@ -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 - 1
packages/grafana-ui/tslint.json

@@ -1,6 +1,6 @@
 {
   "extends": "../../tslint.json",
   "rules": {
-    "import-blacklist": [true, ["^@grafana/ui.*"]]
+    "import-blacklist": [true, "moment", ["^@grafana/ui.*"]]
   }
 }

+ 2 - 2
public/app/app.ts

@@ -19,7 +19,6 @@ import angular from 'angular';
 import config from 'app/core/config';
 // @ts-ignore ignoring this for now, otherwise we would have to extend _ interface with move
 import _ from 'lodash';
-import moment from 'moment';
 import { addClassIfNoOverlayScrollbar } from 'app/core/utils/scrollbar';
 import { importPluginModule } from 'app/features/plugins/plugin_loader';
 
@@ -36,6 +35,7 @@ import { setupAngularRoutes } from 'app/routes/routes';
 
 import 'app/routes/GrafanaCtrl';
 import 'app/features/all';
+import { setLocale } from '@grafana/ui/src/utils/moment_wrapper';
 
 // import symlinked extensions
 const extensionsIndex = (require as any).context('.', true, /extensions\/index.ts/);
@@ -68,7 +68,7 @@ export class GrafanaApp {
   init() {
     const app = angular.module('grafana', []);
 
-    moment.locale(config.bootData.user.locale);
+    setLocale(config.bootData.user.locale);
 
     app.config(
       (

+ 3 - 3
public/app/core/filters/filters.ts

@@ -1,8 +1,8 @@
 import _ from 'lodash';
 import angular from 'angular';
-import moment from 'moment';
 import coreModule from '../core_module';
 import { TemplateSrv } from 'app/features/templating/template_srv';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 coreModule.filter('stringSort', () => {
   return (input: any) => {
@@ -33,9 +33,9 @@ coreModule.filter('moment', () => {
   return (date: string, mode: string) => {
     switch (mode) {
       case 'ago':
-        return moment(date).fromNow();
+        return dateTime(date).fromNow();
     }
-    return moment(date).fromNow();
+    return dateTime(date).fromNow();
   };
 });
 

+ 2 - 2
public/app/core/logs_model.ts

@@ -1,5 +1,4 @@
 import _ from 'lodash';
-import moment from 'moment';
 import ansicolor from 'vendor/ansicolor/ansicolor';
 
 import {
@@ -17,6 +16,7 @@ import {
 } from '@grafana/ui';
 import { getThemeColor } from 'app/core/utils/colors';
 import { hasAnsiCodes } from 'app/core/utils/text';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export const LogLevelColor = {
   [LogLevel.critical]: colors[7],
@@ -435,7 +435,7 @@ export function processLogSeriesRow(
   const ts = row[timeFieldIndex];
   const stringFieldIndex = fieldCache.getFirstFieldOfType(FieldType.string).index;
   const message = row[stringFieldIndex];
-  const time = moment(ts);
+  const time = dateTime(ts);
   const timeEpochMs = time.valueOf();
   const timeFromNow = time.fromNow();
   const timeLocal = time.format('YYYY-MM-DD HH:mm:ss');

+ 4 - 4
public/app/core/specs/rangeutil.test.ts

@@ -1,6 +1,6 @@
 import * as rangeUtil from '@grafana/ui/src/utils/rangeutil';
 import _ from 'lodash';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('rangeUtil', () => {
   describe('Can get range grouped list of ranges', () => {
@@ -69,7 +69,7 @@ describe('rangeUtil', () => {
 
     it('Date range with absolute to now', () => {
       const text = rangeUtil.describeTimeRange({
-        from: moment([2014, 10, 10, 2, 3, 4]),
+        from: dateTime([2014, 10, 10, 2, 3, 4]),
         to: 'now',
       });
       expect(text).toBe('Nov 10, 2014 02:03:04 to a few seconds ago');
@@ -77,7 +77,7 @@ describe('rangeUtil', () => {
 
     it('Date range with absolute to relative', () => {
       const text = rangeUtil.describeTimeRange({
-        from: moment([2014, 10, 10, 2, 3, 4]),
+        from: dateTime([2014, 10, 10, 2, 3, 4]),
         to: 'now-1d',
       });
       expect(text).toBe('Nov 10, 2014 02:03:04 to a day ago');
@@ -86,7 +86,7 @@ describe('rangeUtil', () => {
     it('Date range with relative to absolute', () => {
       const text = rangeUtil.describeTimeRange({
         from: 'now-7d',
-        to: moment([2014, 10, 10, 2, 3, 4]),
+        to: dateTime([2014, 10, 10, 2, 3, 4]),
       });
       expect(text).toBe('7 days ago to Nov 10, 2014 02:03:04');
     });

+ 7 - 6
public/app/core/utils/explore.ts

@@ -1,6 +1,5 @@
 // Libraries
 import _ from 'lodash';
-import moment, { Moment } from 'moment';
 
 // Services & Utils
 import * as dateMath from '@grafana/ui/src/utils/datemath';
@@ -21,6 +20,7 @@ import {
   DataSourceApi,
   toSeriesData,
   guessFieldTypes,
+  TimeFragment,
 } from '@grafana/ui';
 import TimeSeries from 'app/core/time_series2';
 import {
@@ -33,6 +33,7 @@ import {
   ResultGetter,
 } from 'app/types/explore';
 import { LogsDedupStrategy, seriesDataToLogsModel } from 'app/core/logs_model';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 export const DEFAULT_RANGE = {
   from: 'now-6h',
@@ -401,7 +402,7 @@ export const getTimeRange = (timeZone: TimeZone, rawRange: RawTimeRange): TimeRa
   };
 };
 
-const parseRawTime = (value): Moment | string => {
+const parseRawTime = (value): TimeFragment => {
   if (value === null) {
     return null;
   }
@@ -410,19 +411,19 @@ const parseRawTime = (value): Moment | string => {
     return value;
   }
   if (value.length === 8) {
-    return moment.utc(value, 'YYYYMMDD');
+    return toUtc(value, 'YYYYMMDD');
   }
   if (value.length === 15) {
-    return moment.utc(value, 'YYYYMMDDTHHmmss');
+    return toUtc(value, 'YYYYMMDDTHHmmss');
   }
   // Backward compatibility
   if (value.length === 19) {
-    return moment.utc(value, 'YYYY-MM-DD HH:mm:ss');
+    return toUtc(value, 'YYYY-MM-DD HH:mm:ss');
   }
 
   if (!isNaN(value)) {
     const epoch = parseInt(value, 10);
-    return moment.utc(epoch);
+    return toUtc(epoch);
   }
 
   return null;

+ 3 - 3
public/app/core/utils/file_export.ts

@@ -1,7 +1,7 @@
 import { isBoolean, isNumber, sortedUniq, sortedIndexOf, unescape as htmlUnescaped } from 'lodash';
-import moment from 'moment';
 import { saveAs } from 'file-saver';
 import { isNullOrUndefined } from 'util';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const DEFAULT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ';
 const POINT_TIME_INDEX = 1;
@@ -61,7 +61,7 @@ export function convertSeriesListToCsv(seriesList, dateTimeFormat = DEFAULT_DATE
       text += formatRow(
         [
           seriesList[seriesIndex].alias,
-          moment(seriesList[seriesIndex].datapoints[i][POINT_TIME_INDEX]).format(dateTimeFormat),
+          dateTime(seriesList[seriesIndex].datapoints[i][POINT_TIME_INDEX]).format(dateTimeFormat),
           seriesList[seriesIndex].datapoints[i][POINT_VALUE_INDEX],
         ],
         i < seriesList[seriesIndex].datapoints.length - 1 || seriesIndex < seriesList.length - 1
@@ -92,7 +92,7 @@ export function convertSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAU
 
   // make text
   for (let i = 0; i < extendedDatapointsList[0].length; i += 1) {
-    const timestamp = moment(extendedDatapointsList[0][i][POINT_TIME_INDEX]).format(dateTimeFormat);
+    const timestamp = dateTime(extendedDatapointsList[0][i][POINT_TIME_INDEX]).format(dateTimeFormat);
     text += formatRow(
       [timestamp].concat(
         extendedDatapointsList.map(datapoints => {

+ 2 - 2
public/app/features/alerting/state/reducers.ts

@@ -1,7 +1,7 @@
-import moment from 'moment';
 import { AlertRuleDTO, AlertRule, AlertRulesState } from 'app/types';
 import { Action, ActionTypes } from './actions';
 import alertDef from './alertDef';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export const initialState: AlertRulesState = { items: [], searchQuery: '', isLoading: false };
 
@@ -10,7 +10,7 @@ function convertToAlertRule(rule, state): AlertRule {
   rule.stateText = stateModel.text;
   rule.stateIcon = stateModel.iconClass;
   rule.stateClass = stateModel.stateClass;
-  rule.stateAge = moment(rule.newStateDate)
+  rule.stateAge = dateTime(rule.newStateDate)
     .fromNow()
     .replace(' ago', '');
 

+ 2 - 2
public/app/features/annotations/event_editor.ts

@@ -1,8 +1,8 @@
 import _ from 'lodash';
-import moment from 'moment';
 import { coreModule } from 'app/core/core';
 import { MetricsPanelCtrl } from 'app/plugins/sdk';
 import { AnnotationEvent } from '@grafana/ui';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export class EventEditorCtrl {
   panelCtrl: MetricsPanelCtrl;
@@ -86,7 +86,7 @@ export class EventEditorCtrl {
 function tryEpochToMoment(timestamp) {
   if (timestamp && _.isNumber(timestamp)) {
     const epoch = Number(timestamp);
-    return moment(epoch);
+    return dateTime(epoch);
   } else {
     return timestamp;
   }

+ 2 - 2
public/app/features/dashboard/components/ShareModal/ShareModalCtrl.ts

@@ -1,6 +1,6 @@
 import angular from 'angular';
 import config from 'app/core/config';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 /** @ngInject */
 export function ShareModalCtrl($scope, $rootScope, $location, $timeout, timeSrv, templateSrv, linkSrv) {
@@ -93,7 +93,7 @@ export function ShareModalCtrl($scope, $rootScope, $location, $timeout, timeSrv,
   // This function will try to return the proper full name of the local timezone
   // Chrome does not handle the timezone offset (but phantomjs does)
   $scope.getLocalTimeZone = () => {
-    const utcOffset = '&tz=UTC' + encodeURIComponent(moment().format('Z'));
+    const utcOffset = '&tz=UTC' + encodeURIComponent(dateTime().format('Z'));
 
     // Older browser does not the internationalization API
     if (!(window as any).Intl) {

+ 7 - 7
public/app/features/dashboard/components/TimePicker/TimePickerCtrl.ts

@@ -1,6 +1,5 @@
 import _ from 'lodash';
 import angular from 'angular';
-import moment from 'moment';
 
 import * as rangeUtil from '@grafana/ui/src/utils/rangeutil';
 
@@ -38,7 +37,7 @@ export class TimePickerCtrl {
     // init options
     this.panel = this.dashboard.timepicker;
     _.defaults(this.panel, TimePickerCtrl.defaults);
-    this.firstDayOfWeek = moment.localeData().firstDayOfWeek();
+    this.firstDayOfWeek = getLocaleData().firstDayOfWeek();
 
     // init time stuff
     this.onRefresh();
@@ -51,10 +50,10 @@ export class TimePickerCtrl {
     if (!this.dashboard.isTimezoneUtc()) {
       time.from.local();
       time.to.local();
-      if (moment.isMoment(timeRaw.from)) {
+      if (isDateTime(timeRaw.from)) {
         timeRaw.from.local();
       }
-      if (moment.isMoment(timeRaw.to)) {
+      if (isDateTime(timeRaw.to)) {
         timeRaw.to.local();
       }
       this.isUtc = false;
@@ -67,7 +66,7 @@ export class TimePickerCtrl {
     this.tooltip = this.dashboard.formatDate(time.from) + ' <br>to<br>';
     this.tooltip += this.dashboard.formatDate(time.to);
     this.timeRaw = timeRaw;
-    this.isAbsolute = moment.isMoment(this.timeRaw.to);
+    this.isAbsolute = isDateTime(this.timeRaw.to);
   }
 
   zoom(factor) {
@@ -94,7 +93,7 @@ export class TimePickerCtrl {
       from = range.from.valueOf();
     }
 
-    this.timeSrv.setTime({ from: moment.utc(from), to: moment.utc(to) });
+    this.timeSrv.setTime({ from: toUtc(from), to: toUtc(to) });
   }
 
   openDropdown() {
@@ -141,7 +140,7 @@ export class TimePickerCtrl {
   }
 
   getAbsoluteMomentForTimezone(jsDate) {
-    return this.dashboard.isTimezoneUtc() ? moment(jsDate).utc() : moment(jsDate);
+    return this.dashboard.isTimezoneUtc() ? dateTime(jsDate).utc() : dateTime(jsDate);
   }
 
   setRelativeFilter(timespan) {
@@ -186,4 +185,5 @@ angular.module('grafana.directives').directive('gfTimePickerSettings', settingsD
 angular.module('grafana.directives').directive('gfTimePicker', timePickerDirective);
 
 import { inputDateDirective } from './validation';
+import { toUtc, getLocaleData, isDateTime, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 angular.module('grafana.directives').directive('inputDatetime', inputDateDirective);

+ 4 - 4
public/app/features/dashboard/components/TimePicker/validation.ts

@@ -1,5 +1,5 @@
-import moment from 'moment';
 import * as dateMath from '@grafana/ui/src/utils/datemath';
+import { toUtc, dateTime, isDateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export function inputDateDirective() {
   return {
@@ -20,9 +20,9 @@ export function inputDateDirective() {
 
         let parsed;
         if ($scope.ctrl.isUtc) {
-          parsed = moment.utc(text, format);
+          parsed = toUtc(text, format);
         } else {
-          parsed = moment(text, format);
+          parsed = dateTime(text, format);
         }
 
         if (!parsed.isValid()) {
@@ -35,7 +35,7 @@ export function inputDateDirective() {
       };
 
       const toUser = currentValue => {
-        if (moment.isMoment(currentValue)) {
+        if (isDateTime(currentValue)) {
           return currentValue.format(format);
         } else {
           return currentValue;

+ 3 - 3
public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.ts

@@ -1,10 +1,10 @@
 import _ from 'lodash';
 import angular from 'angular';
-import moment from 'moment';
 
 import locationUtil from 'app/core/utils/location_util';
 import { DashboardModel } from '../../state/DashboardModel';
 import { HistoryListOpts, RevisionsModel, CalculateDiffOptions, HistorySrv } from './HistorySrv';
+import { dateTime, toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 export class HistoryListCtrl {
   appending: boolean;
@@ -74,8 +74,8 @@ export class HistoryListCtrl {
   }
 
   formatBasicDate(date) {
-    const now = this.dashboard.timezone === 'browser' ? moment() : moment.utc();
-    const then = this.dashboard.timezone === 'browser' ? moment(date) : moment.utc(date);
+    const now = this.dashboard.timezone === 'browser' ? dateTime() : toUtc();
+    const then = this.dashboard.timezone === 'browser' ? dateTime(date) : toUtc(date);
     return then.from(now);
   }
 

+ 1 - 0
public/app/features/dashboard/services/DashboardLoaderSrv.ts

@@ -1,3 +1,4 @@
+/* tslint:disable:import-blacklist */
 import angular from 'angular';
 import moment from 'moment';
 import _ from 'lodash';

+ 5 - 5
public/app/features/dashboard/services/TimeSrv.test.ts

@@ -1,6 +1,6 @@
-import moment from 'moment';
 import { TimeSrv } from './TimeSrv';
 import { ContextSrvStub } from 'test/specs/helpers';
+import { isDateTime, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('timeSrv', () => {
   const rootScope = {
@@ -43,8 +43,8 @@ describe('timeSrv', () => {
     it('should return parsed when parse is true', () => {
       timeSrv.setTime({ from: 'now', to: 'now-1h' });
       const time = timeSrv.timeRange();
-      expect(moment.isMoment(time.from)).toBe(true);
-      expect(moment.isMoment(time.to)).toBe(true);
+      expect(isDateTime(time.from)).toBe(true);
+      expect(isDateTime(time.to)).toBe(true);
     });
   });
 
@@ -164,8 +164,8 @@ describe('timeSrv', () => {
     it('should restore refresh after relative time range is set', () => {
       _dashboard.refresh = '10s';
       timeSrv.setTime({
-        from: moment([2011, 1, 1]),
-        to: moment([2015, 1, 1]),
+        from: dateTime([2011, 1, 1]),
+        to: dateTime([2015, 1, 1]),
       });
       expect(_dashboard.refresh).toBe(false);
       timeSrv.setTime({ from: '2011-01-01', to: 'now' });

+ 12 - 12
public/app/features/dashboard/services/TimeSrv.ts

@@ -1,5 +1,4 @@
 // Libraries
-import moment from 'moment';
 import _ from 'lodash';
 
 // Utils
@@ -12,6 +11,7 @@ import { TimeRange, RawTimeRange } from '@grafana/ui';
 import { ITimeoutService, ILocationService } from 'angular';
 import { ContextSrv } from 'app/core/services/context_srv';
 import { DashboardModel } from '../state/DashboardModel';
+import { toUtc, dateTime, isDateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export class TimeSrv {
   time: any;
@@ -65,10 +65,10 @@ export class TimeSrv {
   private parseTime() {
     // when absolute time is saved in json it is turned to a string
     if (_.isString(this.time.from) && this.time.from.indexOf('Z') >= 0) {
-      this.time.from = moment(this.time.from).utc();
+      this.time.from = dateTime(this.time.from).utc();
     }
     if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) {
-      this.time.to = moment(this.time.to).utc();
+      this.time.to = dateTime(this.time.to).utc();
     }
   }
 
@@ -77,15 +77,15 @@ export class TimeSrv {
       return value;
     }
     if (value.length === 8) {
-      return moment.utc(value, 'YYYYMMDD');
+      return toUtc(value, 'YYYYMMDD');
     }
     if (value.length === 15) {
-      return moment.utc(value, 'YYYYMMDDTHHmmss');
+      return toUtc(value, 'YYYYMMDDTHHmmss');
     }
 
     if (!isNaN(value)) {
       const epoch = parseInt(value, 10);
-      return moment.utc(epoch);
+      return toUtc(epoch);
     }
 
     return null;
@@ -184,7 +184,7 @@ export class TimeSrv {
     _.extend(this.time, time);
 
     // disable refresh if zoom in or zoom out
-    if (moment.isMoment(time.to)) {
+    if (isDateTime(time.to)) {
       this.oldRefresh = this.dashboard.refresh || this.oldRefresh;
       this.setAutoRefresh(false);
     } else if (this.oldRefresh && this.oldRefresh !== this.dashboard.refresh) {
@@ -207,10 +207,10 @@ export class TimeSrv {
   timeRangeForUrl() {
     const range = this.timeRange().raw;
 
-    if (moment.isMoment(range.from)) {
+    if (isDateTime(range.from)) {
       range.from = range.from.valueOf().toString();
     }
-    if (moment.isMoment(range.to)) {
+    if (isDateTime(range.to)) {
       range.to = range.to.valueOf().toString();
     }
 
@@ -220,8 +220,8 @@ export class TimeSrv {
   timeRange(): TimeRange {
     // make copies if they are moment  (do not want to return out internal moment, because they are mutable!)
     const raw = {
-      from: moment.isMoment(this.time.from) ? moment(this.time.from) : this.time.from,
-      to: moment.isMoment(this.time.to) ? moment(this.time.to) : this.time.to,
+      from: isDateTime(this.time.from) ? dateTime(this.time.from) : this.time.from,
+      to: isDateTime(this.time.to) ? dateTime(this.time.to) : this.time.to,
     };
 
     const timezone = this.dashboard && this.dashboard.getTimezone();
@@ -242,7 +242,7 @@ export class TimeSrv {
     const to = center + (timespan * factor) / 2;
     const from = center - (timespan * factor) / 2;
 
-    this.setTime({ from: moment.utc(from), to: moment.utc(to) });
+    this.setTime({ from: toUtc(from), to: toUtc(to) });
   }
 }
 

+ 7 - 7
public/app/features/dashboard/state/DashboardModel.ts

@@ -1,5 +1,4 @@
 // Libaries
-import moment, { MomentInput } from 'moment';
 import _ from 'lodash';
 
 // Constants
@@ -16,6 +15,7 @@ import { PanelModel, GridPos } from './PanelModel';
 import { DashboardMigrator } from './DashboardMigrator';
 import { TimeRange } from '@grafana/ui/src';
 import { UrlQueryValue, KIOSK_MODE_TV, DashboardMeta } from 'app/types';
+import { toUtc, DateTimeInput, dateTime, isDateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export interface CloneOptions {
   saveVariables?: boolean;
@@ -698,12 +698,12 @@ export class DashboardModel {
     return newPanel;
   }
 
-  formatDate(date: MomentInput, format?: string) {
-    date = moment.isMoment(date) ? date : moment(date);
+  formatDate(date: DateTimeInput, format?: string) {
+    date = isDateTime(date) ? date : dateTime(date);
     format = format || 'YYYY-MM-DD HH:mm:ss';
     const timezone = this.getTimezone();
 
-    return timezone === 'browser' ? moment(date).format(format) : moment.utc(date).format(format);
+    return timezone === 'browser' ? dateTime(date).format(format) : toUtc(date).format(format);
   }
 
   destroy() {
@@ -817,10 +817,10 @@ export class DashboardModel {
     return this.graphTooltip === 1;
   }
 
-  getRelativeTime(date: MomentInput) {
-    date = moment.isMoment(date) ? date : moment(date);
+  getRelativeTime(date: DateTimeInput) {
+    date = isDateTime(date) ? date : dateTime(date);
 
-    return this.timezone === 'browser' ? moment(date).fromNow() : moment.utc(date).fromNow();
+    return this.timezone === 'browser' ? dateTime(date).fromNow() : toUtc(date).fromNow();
   }
 
   isTimezoneUtc() {

+ 3 - 3
public/app/features/dashboard/state/PanelQueryRunner.test.ts

@@ -7,7 +7,7 @@ import {
   LoadingState,
   ScopedVars,
 } from '@grafana/ui/src/types';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 jest.mock('app/core/services/backend_srv');
 
@@ -68,8 +68,8 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn
         widthPixels: ctx.widthPixels,
         maxDataPoints: ctx.maxDataPoints,
         timeRange: {
-          from: moment().subtract(1, 'days'),
-          to: moment(),
+          from: dateTime().subtract(1, 'days'),
+          to: dateTime(),
           raw: { from: '1h', to: 'now' },
         },
         panelId: 0,

+ 13 - 13
public/app/features/dashboard/utils/panel.test.ts

@@ -1,11 +1,11 @@
-import moment from 'moment';
 import { TimeRange } from '@grafana/ui';
 import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';
 import { advanceTo, clear } from 'jest-date-mock';
+import { dateTime, DateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const dashboardTimeRange: TimeRange = {
-  from: moment([2019, 1, 11, 12, 0]),
-  to: moment([2019, 1, 11, 18, 0]),
+  from: dateTime([2019, 1, 11, 12, 0]),
+  to: dateTime([2019, 1, 11, 18, 0]),
   raw: {
     from: 'now-6h',
     to: 'now',
@@ -13,7 +13,7 @@ const dashboardTimeRange: TimeRange = {
 };
 
 describe('applyPanelTimeOverrides', () => {
-  const fakeCurrentDate = moment([2019, 1, 11, 14, 0, 0]).toDate();
+  const fakeCurrentDate = dateTime([2019, 1, 11, 14, 0, 0]).toDate();
 
   beforeAll(() => {
     advanceTo(fakeCurrentDate);
@@ -31,7 +31,7 @@ describe('applyPanelTimeOverrides', () => {
     // @ts-ignore: PanelModel type incositency
     const overrides = applyPanelTimeOverrides(panelModel, dashboardTimeRange);
 
-    expect(overrides.timeRange.from.toISOString()).toBe(moment([2019, 1, 11, 12]).toISOString());
+    expect(overrides.timeRange.from.toISOString()).toBe(dateTime([2019, 1, 11, 12]).toISOString());
     expect(overrides.timeRange.to.toISOString()).toBe(fakeCurrentDate.toISOString());
     expect(overrides.timeRange.raw.from).toBe('now-2h');
     expect(overrides.timeRange.raw.to).toBe('now');
@@ -42,16 +42,16 @@ describe('applyPanelTimeOverrides', () => {
       timeShift: '2h',
     };
 
-    const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate();
-    const expectedToDate = moment([2019, 1, 11, 16, 0, 0]).toDate();
+    const expectedFromDate = dateTime([2019, 1, 11, 10, 0, 0]).toDate();
+    const expectedToDate = dateTime([2019, 1, 11, 16, 0, 0]).toDate();
 
     // @ts-ignore: PanelModel type incositency
     const overrides = applyPanelTimeOverrides(panelModel, dashboardTimeRange);
 
     expect(overrides.timeRange.from.toISOString()).toBe(expectedFromDate.toISOString());
     expect(overrides.timeRange.to.toISOString()).toBe(expectedToDate.toISOString());
-    expect((overrides.timeRange.raw.from as moment.Moment).toISOString()).toEqual(expectedFromDate.toISOString());
-    expect((overrides.timeRange.raw.to as moment.Moment).toISOString()).toEqual(expectedToDate.toISOString());
+    expect((overrides.timeRange.raw.from as DateTime).toISOString()).toEqual(expectedFromDate.toISOString());
+    expect((overrides.timeRange.raw.to as DateTime).toISOString()).toEqual(expectedToDate.toISOString());
   });
 
   it('should apply both relative time and time shift', () => {
@@ -60,15 +60,15 @@ describe('applyPanelTimeOverrides', () => {
       timeShift: '2h',
     };
 
-    const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate();
-    const expectedToDate = moment([2019, 1, 11, 12, 0, 0]).toDate();
+    const expectedFromDate = dateTime([2019, 1, 11, 10, 0, 0]).toDate();
+    const expectedToDate = dateTime([2019, 1, 11, 12, 0, 0]).toDate();
 
     // @ts-ignore: PanelModel type incositency
     const overrides = applyPanelTimeOverrides(panelModel, dashboardTimeRange);
 
     expect(overrides.timeRange.from.toISOString()).toBe(expectedFromDate.toISOString());
     expect(overrides.timeRange.to.toISOString()).toBe(expectedToDate.toISOString());
-    expect((overrides.timeRange.raw.from as moment.Moment).toISOString()).toEqual(expectedFromDate.toISOString());
-    expect((overrides.timeRange.raw.to as moment.Moment).toISOString()).toEqual(expectedToDate.toISOString());
+    expect((overrides.timeRange.raw.from as DateTime).toISOString()).toEqual(expectedFromDate.toISOString());
+    expect((overrides.timeRange.raw.to as DateTime).toISOString()).toEqual(expectedToDate.toISOString());
   });
 });

+ 3 - 3
public/app/features/explore/GraphContainer.tsx

@@ -1,7 +1,6 @@
 import React, { PureComponent } from 'react';
 import { hot } from 'react-hot-loader';
 import { connect } from 'react-redux';
-import moment from 'moment';
 import { TimeRange, TimeZone, AbsoluteTimeRange } from '@grafana/ui';
 
 import { ExploreId, ExploreItemState } from 'app/types/explore';
@@ -11,6 +10,7 @@ import { toggleGraph, changeTime } from './state/actions';
 import Graph from './Graph';
 import Panel from './Panel';
 import { getTimeZone } from '../profile/state/selectors';
+import { toUtc, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 interface GraphContainerProps {
   exploreId: ExploreId;
@@ -34,8 +34,8 @@ export class GraphContainer extends PureComponent<GraphContainerProps> {
   onChangeTime = (absRange: AbsoluteTimeRange) => {
     const { exploreId, timeZone, changeTime } = this.props;
     const range = {
-      from: timeZone.isUtc ? moment.utc(absRange.from) : moment(absRange.from),
-      to: timeZone.isUtc ? moment.utc(absRange.to) : moment(absRange.to),
+      from: timeZone.isUtc ? toUtc(absRange.from) : dateTime(absRange.from),
+      to: timeZone.isUtc ? toUtc(absRange.to) : dateTime(absRange.to),
     };
 
     changeTime(exploreId, range);

+ 3 - 3
public/app/features/explore/QueryEditor.tsx

@@ -1,6 +1,5 @@
 // Libraries
 import React, { PureComponent } from 'react';
-import moment from 'moment';
 
 // Services
 import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
@@ -10,6 +9,7 @@ import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
 import { Emitter } from 'app/core/utils/emitter';
 import { DataQuery, TimeRange } from '@grafana/ui';
 import 'app/features/plugins/plugin_loader';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 interface QueryEditorProps {
   datasource: any;
@@ -67,8 +67,8 @@ export default class QueryEditor extends PureComponent<QueryEditorProps, any> {
     const timeSrv = getTimeSrv();
     timeSrv.init({
       time: {
-        from: moment(range.from),
-        to: moment(range.to),
+        from: dateTime(range.from),
+        to: dateTime(range.to),
       },
       refresh: false,
       getTimezone: () => 'utc',

+ 39 - 39
public/app/features/explore/TimePicker.test.tsx

@@ -1,12 +1,12 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import sinon from 'sinon';
-import moment from 'moment';
 
 import * as dateMath from '@grafana/ui/src/utils/datemath';
 import * as rangeUtil from '@grafana/ui/src/utils/rangeutil';
 import TimePicker from './TimePicker';
 import { RawTimeRange, TimeRange, TIME_FORMAT } from '@grafana/ui';
+import { toUtc, isDateTime, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const DEFAULT_RANGE = {
   from: 'now-6h',
@@ -15,8 +15,8 @@ const DEFAULT_RANGE = {
 
 const fromRaw = (rawRange: RawTimeRange): TimeRange => {
   const raw = {
-    from: moment.isMoment(rawRange.from) ? moment(rawRange.from) : rawRange.from,
-    to: moment.isMoment(rawRange.to) ? moment(rawRange.to) : rawRange.to,
+    from: isDateTime(rawRange.from) ? dateTime(rawRange.from) : rawRange.from,
+    to: isDateTime(rawRange.to) ? dateTime(rawRange.to) : rawRange.to,
   };
 
   return {
@@ -73,19 +73,19 @@ describe('<TimePicker />', () => {
 
   it('apply with absolute range and non-utc', () => {
     const range = {
-      from: moment.utc(1),
-      to: moment.utc(1000),
+      from: toUtc(1),
+      to: toUtc(1000),
       raw: {
-        from: moment.utc(1),
-        to: moment.utc(1000),
+        from: toUtc(1),
+        to: toUtc(1000),
       },
     };
     const localRange = {
-      from: moment(1),
-      to: moment(1000),
+      from: dateTime(1),
+      to: dateTime(1000),
       raw: {
-        from: moment(1),
-        to: moment(1000),
+        from: dateTime(1),
+        to: dateTime(1000),
       },
     };
     const expectedRangeString = rangeUtil.describeTimeRange(localRange);
@@ -110,11 +110,11 @@ describe('<TimePicker />', () => {
 
   it('apply with absolute range and utc', () => {
     const range = {
-      from: moment.utc(1),
-      to: moment.utc(1000),
+      from: toUtc(1),
+      to: toUtc(1000),
       raw: {
-        from: moment.utc(1),
-        to: moment.utc(1000),
+        from: toUtc(1),
+        to: toUtc(1000),
       },
     };
     const onChangeTime = sinon.spy();
@@ -137,11 +137,11 @@ describe('<TimePicker />', () => {
 
   it('moves ranges backward by half the range on left arrow click when utc', () => {
     const rawRange = {
-      from: moment.utc(2000),
-      to: moment.utc(4000),
+      from: toUtc(2000),
+      to: toUtc(4000),
       raw: {
-        from: moment.utc(2000),
-        to: moment.utc(4000),
+        from: toUtc(2000),
+        to: toUtc(4000),
       },
     };
     const range = fromRaw(rawRange);
@@ -159,19 +159,19 @@ describe('<TimePicker />', () => {
 
   it('moves ranges backward by half the range on left arrow click when not utc', () => {
     const range = {
-      from: moment.utc(2000),
-      to: moment.utc(4000),
+      from: toUtc(2000),
+      to: toUtc(4000),
       raw: {
-        from: moment.utc(2000),
-        to: moment.utc(4000),
+        from: toUtc(2000),
+        to: toUtc(4000),
       },
     };
     const localRange = {
-      from: moment(2000),
-      to: moment(4000),
+      from: dateTime(2000),
+      to: dateTime(4000),
       raw: {
-        from: moment(2000),
-        to: moment(4000),
+        from: dateTime(2000),
+        to: dateTime(4000),
       },
     };
 
@@ -188,11 +188,11 @@ describe('<TimePicker />', () => {
 
   it('moves ranges forward by half the range on right arrow click when utc', () => {
     const range = {
-      from: moment.utc(1000),
-      to: moment.utc(3000),
+      from: toUtc(1000),
+      to: toUtc(3000),
       raw: {
-        from: moment.utc(1000),
-        to: moment.utc(3000),
+        from: toUtc(1000),
+        to: toUtc(3000),
       },
     };
 
@@ -209,19 +209,19 @@ describe('<TimePicker />', () => {
 
   it('moves ranges forward by half the range on right arrow click when not utc', () => {
     const range = {
-      from: moment.utc(1000),
-      to: moment.utc(3000),
+      from: toUtc(1000),
+      to: toUtc(3000),
       raw: {
-        from: moment.utc(1000),
-        to: moment.utc(3000),
+        from: toUtc(1000),
+        to: toUtc(3000),
       },
     };
     const localRange = {
-      from: moment(1000),
-      to: moment(3000),
+      from: dateTime(1000),
+      to: dateTime(3000),
       raw: {
-        from: moment(1000),
-        to: moment(3000),
+        from: dateTime(1000),
+        to: dateTime(3000),
       },
     };
 

+ 9 - 9
public/app/features/explore/TimePicker.tsx

@@ -1,7 +1,7 @@
 import React, { PureComponent } from 'react';
-import moment from 'moment';
 import * as rangeUtil from '@grafana/ui/src/utils/rangeutil';
 import { Input, RawTimeRange, TimeRange, TIME_FORMAT } from '@grafana/ui';
+import { toUtc, isDateTime, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 interface TimePickerProps {
   isOpen?: boolean;
@@ -28,14 +28,14 @@ const getRaw = (isUtc: boolean, range: any) => {
     to: range.raw.to,
   };
 
-  if (moment.isMoment(rawRange.from)) {
+  if (isDateTime(rawRange.from)) {
     if (!isUtc) {
       rawRange.from = rawRange.from.local();
     }
     rawRange.from = rawRange.from.format(TIME_FORMAT);
   }
 
-  if (moment.isMoment(rawRange.to)) {
+  if (isDateTime(rawRange.to)) {
     if (!isUtc) {
       rawRange.to = rawRange.to.local();
     }
@@ -99,8 +99,8 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
   move(direction: number, scanning?: boolean): RawTimeRange {
     const { onChangeTime, range: origRange } = this.props;
     const range = {
-      from: moment.utc(origRange.from),
-      to: moment.utc(origRange.to),
+      from: toUtc(origRange.from),
+      to: toUtc(origRange.to),
     };
     const timespan = (range.to.valueOf() - range.from.valueOf()) / 2;
     let to, from;
@@ -116,8 +116,8 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
     }
 
     const nextTimeRange = {
-      from: this.props.isUtc ? moment.utc(from) : moment(from),
-      to: this.props.isUtc ? moment.utc(to) : moment(to),
+      from: this.props.isUtc ? toUtc(from) : dateTime(from),
+      to: this.props.isUtc ? toUtc(to) : dateTime(to),
     };
     if (onChangeTime) {
       onChangeTime(nextTimeRange);
@@ -149,11 +149,11 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
         };
 
         if (rawRange.from.indexOf('now') === -1) {
-          rawRange.from = isUtc ? moment.utc(rawRange.from, TIME_FORMAT) : moment(rawRange.from, TIME_FORMAT);
+          rawRange.from = isUtc ? toUtc(rawRange.from, TIME_FORMAT) : dateTime(rawRange.from, TIME_FORMAT);
         }
 
         if (rawRange.to.indexOf('now') === -1) {
-          rawRange.to = isUtc ? moment.utc(rawRange.to, TIME_FORMAT) : moment(rawRange.to, TIME_FORMAT);
+          rawRange.to = isUtc ? toUtc(rawRange.to, TIME_FORMAT) : dateTime(rawRange.to, TIME_FORMAT);
         }
 
         const rangeString = rangeUtil.describeTimeRange(rawRange);

+ 2 - 2
public/app/features/explore/state/actions.test.ts

@@ -1,4 +1,3 @@
-import moment from 'moment';
 import { refreshExplore, testDatasource, loadDatasource } from './actions';
 import { ExploreId, ExploreUrlState, ExploreUpdateState } from 'app/types';
 import { thunkTester } from 'test/core/thunk/thunkTester';
@@ -20,6 +19,7 @@ import { ActionOf } from 'app/core/redux/actionCreatorFactory';
 import { makeInitialUpdateState } from './reducers';
 import { DataQuery } from '@grafana/ui/src/types/datasource';
 import { DefaultTimeZone, RawTimeRange } from '@grafana/ui';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 jest.mock('app/features/plugins/datasource_srv', () => ({
   getDatasourceSrv: () => ({
@@ -31,7 +31,7 @@ jest.mock('app/features/plugins/datasource_srv', () => ({
   }),
 }));
 
-const t = moment.utc();
+const t = toUtc();
 const testRange = {
   from: t,
   to: t,

+ 3 - 3
public/app/features/explore/state/actions.ts

@@ -1,6 +1,5 @@
 // Libraries
 import _ from 'lodash';
-import moment from 'moment';
 
 // Services & Utils
 import store from 'app/core/store';
@@ -87,6 +86,7 @@ import {
 import { ActionOf, ActionCreator } from 'app/core/redux/actionCreatorFactory';
 import { LogsDedupStrategy } from 'app/core/logs_model';
 import { getTimeZone } from 'app/features/profile/state/selectors';
+import { isDateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 /**
  * Updates UI state and save it to the URL
@@ -732,12 +732,12 @@ export function splitOpen(): ThunkResult<void> {
 
 const toRawTimeRange = (range: TimeRange): RawTimeRange => {
   let from = range.raw.from;
-  if (moment.isMoment(from)) {
+  if (isDateTime(from)) {
     from = from.valueOf().toString(10);
   }
 
   let to = range.raw.to;
-  if (moment.isMoment(to)) {
+  if (isDateTime(to)) {
     to = to.valueOf().toString(10);
   }
 

+ 1 - 0
public/app/features/plugins/plugin_loader.ts

@@ -1,3 +1,4 @@
+/* tslint:disable:import-blacklist */
 import System from 'systemjs/dist/system.js';
 import _ from 'lodash';
 import * as sdk from 'app/plugins/sdk';

+ 3 - 3
public/app/features/templating/specs/variable_srv.test.ts

@@ -1,8 +1,8 @@
 import '../all';
 import { VariableSrv } from '../variable_srv';
 import { DashboardModel } from '../../dashboard/state/DashboardModel';
-import moment from 'moment';
 import $q from 'q';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('VariableSrv', function(this: any) {
   const ctx = {
@@ -106,7 +106,7 @@ describe('VariableSrv', function(this: any) {
       };
 
       const range = {
-        from: moment(new Date())
+        from: dateTime(new Date())
           .subtract(7, 'days')
           .toDate(),
         to: new Date(),
@@ -514,7 +514,7 @@ describe('VariableSrv', function(this: any) {
 
     beforeEach(() => {
       const range = {
-        from: moment(new Date())
+        from: dateTime(new Date())
           .subtract(7, 'days')
           .toDate(),
         to: new Date(),

+ 2 - 2
public/app/plugins/datasource/elasticsearch/datasource.ts

@@ -1,9 +1,9 @@
 import angular from 'angular';
 import _ from 'lodash';
-import moment from 'moment';
 import { ElasticResponse } from './elastic_response';
 import { IndexPattern } from './index_pattern';
 import { ElasticQueryBuilder } from './query_builder';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 export class ElasticDatasource {
   basicAuth: string;
@@ -176,7 +176,7 @@ export class ElasticDatasource {
 
         const event = {
           annotation: annotation,
-          time: moment.utc(time).valueOf(),
+          time: toUtc(time).valueOf(),
           text: getFieldFromSource(source, textField),
           tags: getFieldFromSource(source, tagsField),
         };

+ 4 - 4
public/app/plugins/datasource/elasticsearch/index_pattern.ts

@@ -1,4 +1,4 @@
-import moment from 'moment';
+import { toUtc, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const intervalMap = {
   Hourly: { startOf: 'hour', amount: 'hours' },
@@ -13,7 +13,7 @@ export class IndexPattern {
 
   getIndexForToday() {
     if (this.interval) {
-      return moment.utc().format(this.pattern);
+      return toUtc().format(this.pattern);
     } else {
       return this.pattern;
     }
@@ -25,10 +25,10 @@ export class IndexPattern {
     }
 
     const intervalInfo = intervalMap[this.interval];
-    const start = moment(from)
+    const start = dateTime(from)
       .utc()
       .startOf(intervalInfo.startOf);
-    const endEpoch = moment(to)
+    const endEpoch = dateTime(to)
       .utc()
       .startOf(intervalInfo.startOf)
       .valueOf();

+ 8 - 8
public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts

@@ -1,8 +1,8 @@
 import angular from 'angular';
 import * as dateMath from '@grafana/ui/src/utils/datemath';
 import _ from 'lodash';
-import moment from 'moment';
 import { ElasticDatasource } from '../datasource';
+import { toUtc, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('ElasticDatasource', function(this: any) {
   const backendSrv = {
@@ -66,7 +66,7 @@ describe('ElasticDatasource', function(this: any) {
 
       ctx.ds.testDatasource();
 
-      const today = moment.utc().format('YYYY.MM.DD');
+      const today = toUtc().format('YYYY.MM.DD');
       expect(requestOptions.url).toBe('http://es.com/asd-' + today + '/_mapping');
     });
   });
@@ -105,8 +105,8 @@ describe('ElasticDatasource', function(this: any) {
 
       query = {
         range: {
-          from: moment.utc([2015, 4, 30, 10]),
-          to: moment.utc([2015, 5, 1, 10]),
+          from: toUtc([2015, 4, 30, 10]),
+          to: toUtc([2015, 5, 1, 10]),
         },
         targets: [
           {
@@ -159,8 +159,8 @@ describe('ElasticDatasource', function(this: any) {
 
       ctx.ds.query({
         range: {
-          from: moment([2015, 4, 30, 10]),
-          to: moment([2015, 5, 1, 10]),
+          from: dateTime([2015, 4, 30, 10]),
+          to: dateTime([2015, 5, 1, 10]),
         },
         targets: [
           {
@@ -441,8 +441,8 @@ describe('ElasticDatasource', function(this: any) {
 
       ctx.ds.query({
         range: {
-          from: moment([2015, 4, 30, 10]),
-          to: moment([2015, 5, 1, 10]),
+          from: dateTime([2015, 4, 30, 10]),
+          to: dateTime([2015, 5, 1, 10]),
         },
         targets: [
           {

+ 2 - 2
public/app/plugins/datasource/elasticsearch/specs/index_pattern.test.ts

@@ -1,13 +1,13 @@
 ///<amd-dependency path="test/specs/helpers" name="helpers" />
 
-import moment from 'moment';
 import { IndexPattern } from '../index_pattern';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('IndexPattern', () => {
   describe('when getting index for today', () => {
     test('should return correct index name', () => {
       const pattern = new IndexPattern('[asd-]YYYY.MM.DD', 'Daily');
-      const expected = 'asd-' + moment.utc().format('YYYY.MM.DD');
+      const expected = 'asd-' + toUtc().format('YYYY.MM.DD');
 
       expect(pattern.getIndexForToday()).toBe(expected);
     });

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts

@@ -1,7 +1,7 @@
 import AzureMonitorDatasource from '../datasource';
 import Q from 'q';
-import moment from 'moment';
 import { TemplateSrv } from 'app/features/templating/template_srv';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('AppInsightsDatasource', () => {
   const ctx: any = {
@@ -110,8 +110,8 @@ describe('AppInsightsDatasource', () => {
   describe('When performing query', () => {
     const options = {
       range: {
-        from: moment.utc('2017-08-22T20:00:00Z'),
-        to: moment.utc('2017-08-22T23:59:00Z'),
+        from: toUtc('2017-08-22T20:00:00Z'),
+        to: toUtc('2017-08-22T23:59:00Z'),
       },
       targets: [
         {

+ 2 - 2
public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.test.ts

@@ -1,11 +1,11 @@
 import AppInsightsQuerystringBuilder from './app_insights_querystring_builder';
-import moment from 'moment';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('AppInsightsQuerystringBuilder', () => {
   let builder: AppInsightsQuerystringBuilder;
 
   beforeEach(() => {
-    builder = new AppInsightsQuerystringBuilder(moment.utc('2017-08-22 06:00'), moment.utc('2017-08-22 07:00'), '1h');
+    builder = new AppInsightsQuerystringBuilder(toUtc('2017-08-22 06:00'), toUtc('2017-08-22 07:00'), '1h');
   });
 
   describe('with only from/to date range', () => {

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/response_parser.ts

@@ -1,5 +1,5 @@
-import moment from 'moment';
 import _ from 'lodash';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export default class ResponseParser {
   constructor(private results) {}
@@ -173,8 +173,8 @@ export default class ResponseParser {
     return _.intersection(keys, ['sum', 'avg', 'min', 'max', 'count', 'unique'])[0];
   }
 
-  static dateTimeToEpoch(dateTime) {
-    return moment(dateTime).valueOf();
+  static dateTimeToEpoch(dateTimeValue) {
+    return dateTime(dateTimeValue).valueOf();
   }
 
   static parseMetricNames(result) {

+ 5 - 5
public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts

@@ -1,9 +1,9 @@
 import AzureMonitorDatasource from '../datasource';
 import FakeSchemaData from './__mocks__/schema';
 import Q from 'q';
-import moment from 'moment';
 import { TemplateSrv } from 'app/features/templating/template_srv';
 import { KustoSchema } from '../types';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('AzureLogAnalyticsDatasource', () => {
   const ctx: any = {
@@ -114,8 +114,8 @@ describe('AzureLogAnalyticsDatasource', () => {
   describe('When performing query', () => {
     const options = {
       range: {
-        from: moment.utc('2017-08-22T20:00:00Z'),
-        to: moment.utc('2017-08-22T23:59:00Z'),
+        from: toUtc('2017-08-22T20:00:00Z'),
+        to: toUtc('2017-08-22T23:59:00Z'),
       },
       rangeRaw: {
         from: 'now-4h',
@@ -376,8 +376,8 @@ describe('AzureLogAnalyticsDatasource', () => {
           workspace: 'abc1b44e-3e57-4410-b027-6cc0ae6dee67',
         },
         range: {
-          from: moment.utc('2017-08-22T20:00:00Z'),
-          to: moment.utc('2017-08-22T23:59:00Z'),
+          from: toUtc('2017-08-22T20:00:00Z'),
+          to: toUtc('2017-08-22T23:59:00Z'),
         },
         rangeRaw: {
           from: 'now-4h',

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/response_parser.ts

@@ -1,5 +1,5 @@
 import _ from 'lodash';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 import {
   AzureLogsVariable,
   AzureLogsTableData,
@@ -216,7 +216,7 @@ export default class ResponseParser {
     return dataTarget;
   }
 
-  static dateTimeToEpoch(dateTime) {
-    return moment(dateTime).valueOf();
+  static dateTimeToEpoch(dateTimeValue) {
+    return dateTime(dateTimeValue).valueOf();
   }
 }

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts

@@ -1,7 +1,7 @@
 import AzureMonitorDatasource from '../datasource';
 import Q from 'q';
-import moment from 'moment';
 import { TemplateSrv } from 'app/features/templating/template_srv';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('AzureMonitorDatasource', () => {
   const ctx: any = {
@@ -79,8 +79,8 @@ describe('AzureMonitorDatasource', () => {
   describe('When performing query', () => {
     const options = {
       range: {
-        from: moment.utc('2017-08-22T20:00:00Z'),
-        to: moment.utc('2017-08-22T23:59:00Z'),
+        from: toUtc('2017-08-22T20:00:00Z'),
+        to: toUtc('2017-08-22T23:59:00Z'),
       },
       targets: [
         {

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.test.ts

@@ -19,7 +19,7 @@ jest.mock('app/core/utils/kbn', () => {
 });
 
 import AzureMonitorFilterBuilder from './azure_monitor_filter_builder';
-import moment from 'moment';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('AzureMonitorFilterBuilder', () => {
   let builder: AzureMonitorFilterBuilder;
@@ -30,8 +30,8 @@ describe('AzureMonitorFilterBuilder', () => {
   beforeEach(() => {
     builder = new AzureMonitorFilterBuilder(
       'Percentage CPU',
-      moment.utc('2017-08-22 06:00'),
-      moment.utc('2017-08-22 07:00'),
+      toUtc('2017-08-22 06:00'),
+      toUtc('2017-08-22 07:00'),
       'PT1H',
       '3m'
     );

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/response_parser.ts

@@ -1,6 +1,6 @@
-import moment from 'moment';
 import _ from 'lodash';
 import TimeGrainConverter from '../time_grain_converter';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export default class ResponseParser {
   constructor(private results) {}
@@ -95,8 +95,8 @@ export default class ResponseParser {
     return dataPoints;
   }
 
-  static dateTimeToEpoch(dateTime) {
-    return moment(dateTime).valueOf();
+  static dateTimeToEpoch(dateTimeValue) {
+    return dateTime(dateTimeValue).valueOf();
   }
 
   static getKeyForAggregationField(dataObj): string {

+ 4 - 4
public/app/plugins/datasource/grafana-azure-monitor-datasource/log_analytics/querystring_builder.test.ts

@@ -1,5 +1,5 @@
 import LogAnalyticsQuerystringBuilder from './querystring_builder';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('LogAnalyticsDatasource', () => {
   let builder: LogAnalyticsQuerystringBuilder;
@@ -10,8 +10,8 @@ describe('LogAnalyticsDatasource', () => {
       {
         interval: '5m',
         range: {
-          from: moment().subtract(24, 'hours'),
-          to: moment(),
+          from: dateTime().subtract(24, 'hours'),
+          to: dateTime(),
         },
         rangeRaw: {
           from: 'now-24h',
@@ -106,7 +106,7 @@ describe('LogAnalyticsDatasource', () => {
   describe('when using $__from and $__to is in the query and range is a specific interval', () => {
     beforeEach(() => {
       builder.rawQueryString = 'query=Tablename | where myTime >= $__from and myTime <= $__to';
-      builder.options.range.to = moment().subtract(1, 'hour');
+      builder.options.range.to = dateTime().subtract(1, 'hour');
       builder.options.rangeRaw.to = 'now-1h';
     });
 

+ 3 - 3
public/app/plugins/datasource/grafana-azure-monitor-datasource/log_analytics/querystring_builder.ts

@@ -1,4 +1,4 @@
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export default class LogAnalyticsQuerystringBuilder {
   constructor(public rawQueryString, public options, public defaultTimeField) {}
@@ -37,7 +37,7 @@ export default class LogAnalyticsQuerystringBuilder {
 
   getFrom(options) {
     const from = options.range.from;
-    return `datetime(${moment(from)
+    return `datetime(${dateTime(from)
       .startOf('minute')
       .toISOString()})`;
   }
@@ -47,7 +47,7 @@ export default class LogAnalyticsQuerystringBuilder {
       return 'now()';
     } else {
       const until = options.range.to;
-      return `datetime(${moment(until)
+      return `datetime(${dateTime(until)
         .startOf('minute')
         .toISOString()})`;
     }

+ 3 - 3
public/app/plugins/datasource/grafana/specs/datasource.test.ts

@@ -1,6 +1,6 @@
 import { GrafanaDatasource } from '../datasource';
 import q from 'q';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('grafana data source', () => {
   describe('when executing an annotations query', () => {
@@ -70,8 +70,8 @@ function setupAnnotationQueryOptions(annotation, dashboard?) {
     annotation: annotation,
     dashboard: dashboard,
     range: {
-      from: moment(1432288354),
-      to: moment(1432288401),
+      from: dateTime(1432288354),
+      to: dateTime(1432288401),
     },
     rangeRaw: { from: 'now-24h', to: 'now' },
   };

+ 3 - 3
public/app/plugins/datasource/graphite/specs/datasource.test.ts

@@ -1,8 +1,8 @@
 import { GraphiteDatasource } from '../datasource';
-import moment from 'moment';
 import _ from 'lodash';
 import $q from 'q';
 import { TemplateSrvStub } from 'test/specs/helpers';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('graphiteDatasource', () => {
   const ctx: any = {
@@ -86,8 +86,8 @@ describe('graphiteDatasource', () => {
         tags: 'tag1',
       },
       range: {
-        from: moment(1432288354),
-        to: moment(1432288401),
+        from: dateTime(1432288354),
+        to: dateTime(1432288401),
       },
       rangeRaw: { from: 'now-24h', to: 'now' },
     };

+ 2 - 2
public/app/plugins/datasource/loki/language_provider.ts

@@ -1,6 +1,5 @@
 // Libraries
 import _ from 'lodash';
-import moment from 'moment';
 
 // Services & Utils
 import { parseSelector, labelRegexp, selectorRegexp } from 'app/plugins/datasource/prometheus/language_utils';
@@ -16,6 +15,7 @@ import {
   HistoryItem,
 } from 'app/types/explore';
 import { LokiQuery } from './types';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const DEFAULT_KEYS = ['job', 'namespace'];
 const EMPTY_SELECTOR = '{}';
@@ -34,7 +34,7 @@ export function addHistoryMetadata(item: CompletionItem, history: LokiHistoryIte
   const recent = historyForItem[0];
   let hint = `Queried ${count} times in the last 24h.`;
   if (recent) {
-    const lastQueried = moment(recent.ts).fromNow();
+    const lastQueried = dateTime(recent.ts).fromNow();
     hint = `${hint} Last queried ${lastQueried}.`;
   }
   return {

+ 5 - 5
public/app/plugins/datasource/mssql/specs/datasource.test.ts

@@ -1,8 +1,8 @@
-import moment from 'moment';
 import { MssqlDatasource } from '../datasource';
 import { TemplateSrvStub, TimeSrvStub } from 'test/specs/helpers';
 import { CustomVariable } from 'app/features/templating/custom_variable';
 import q from 'q';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('MSSQLDatasource', () => {
   const ctx: any = {
@@ -29,8 +29,8 @@ describe('MSSQLDatasource', () => {
         rawQuery: 'select time, text, tags from table;',
       },
       range: {
-        from: moment(1432288354),
-        to: moment(1432288401),
+        from: dateTime(1432288354),
+        to: dateTime(1432288401),
       },
     };
 
@@ -209,8 +209,8 @@ describe('MSSQLDatasource', () => {
       },
     };
     const time = {
-      from: moment(1521545610656),
-      to: moment(1521546251185),
+      from: dateTime(1521545610656),
+      to: dateTime(1521546251185),
     };
 
     beforeEach(() => {

+ 5 - 5
public/app/plugins/datasource/mysql/specs/datasource.test.ts

@@ -1,6 +1,6 @@
-import moment from 'moment';
 import { MysqlDatasource } from '../datasource';
 import { CustomVariable } from 'app/features/templating/custom_variable';
+import { toUtc, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('MySQLDatasource', () => {
   const instanceSettings = { name: 'mysql' };
@@ -10,8 +10,8 @@ describe('MySQLDatasource', () => {
   };
 
   const raw = {
-    from: moment.utc('2018-04-25 10:00'),
-    to: moment.utc('2018-04-25 11:00'),
+    from: toUtc('2018-04-25 10:00'),
+    to: toUtc('2018-04-25 11:00'),
   };
   const ctx = {
     backendSrv,
@@ -39,8 +39,8 @@ describe('MySQLDatasource', () => {
         rawQuery: 'select time_sec, text, tags from table;',
       },
       range: {
-        from: moment(1432288354),
-        to: moment(1432288401),
+        from: dateTime(1432288354),
+        to: dateTime(1432288401),
       },
     };
 

+ 5 - 5
public/app/plugins/datasource/postgres/specs/datasource.test.ts

@@ -1,6 +1,6 @@
-import moment from 'moment';
 import { PostgresDatasource } from '../datasource';
 import { CustomVariable } from 'app/features/templating/custom_variable';
+import { toUtc, dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('PostgreSQLDatasource', () => {
   const instanceSettings = { name: 'postgresql' };
@@ -10,8 +10,8 @@ describe('PostgreSQLDatasource', () => {
     replace: jest.fn(text => text),
   };
   const raw = {
-    from: moment.utc('2018-04-25 10:00'),
-    to: moment.utc('2018-04-25 11:00'),
+    from: toUtc('2018-04-25 10:00'),
+    to: toUtc('2018-04-25 11:00'),
   };
   const ctx = {
     backendSrv,
@@ -39,8 +39,8 @@ describe('PostgreSQLDatasource', () => {
         rawQuery: 'select time, title, text, tags from table;',
       },
       range: {
-        from: moment(1432288354),
-        to: moment(1432288401),
+        from: dateTime(1432288354),
+        to: dateTime(1432288401),
       },
     };
 

+ 2 - 2
public/app/plugins/datasource/prometheus/language_provider.ts

@@ -1,5 +1,4 @@
 import _ from 'lodash';
-import moment from 'moment';
 
 import {
   CompletionItem,
@@ -11,6 +10,7 @@ import {
 
 import { parseSelector, processLabels, processHistogramLabels } from './language_utils';
 import PromqlSyntax, { FUNCTIONS, RATE_RANGES } from './promql';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const DEFAULT_KEYS = ['job', 'instance'];
 const EMPTY_SELECTOR = '{}';
@@ -31,7 +31,7 @@ export function addHistoryMetadata(item: CompletionItem, history: any[]): Comple
   const recent = historyForItem[0];
   let hint = `Queried ${count} times in the last 24h.`;
   if (recent) {
-    const lastQueried = moment(recent.ts).fromNow();
+    const lastQueried = dateTime(recent.ts).fromNow();
     hint = `${hint} Last queried ${lastQueried}.`;
   }
   return {

+ 7 - 7
public/app/plugins/datasource/prometheus/specs/datasource.test.ts

@@ -1,5 +1,4 @@
 import _ from 'lodash';
-import moment from 'moment';
 import q from 'q';
 import {
   alignRange,
@@ -8,6 +7,7 @@ import {
   prometheusRegularEscape,
   prometheusSpecialRegexEscape,
 } from '../datasource';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 jest.mock('../metric_find_query');
 
@@ -33,8 +33,8 @@ describe('PrometheusDatasource', () => {
   ctx.timeSrvMock = {
     timeRange: () => {
       return {
-        from: moment(1531468681),
-        to: moment(1531489712),
+        from: dateTime(1531468681),
+        to: dateTime(1531489712),
       };
     },
   };
@@ -135,7 +135,7 @@ describe('PrometheusDatasource', () => {
   describe('When converting prometheus histogram to heatmap format', () => {
     beforeEach(() => {
       ctx.query = {
-        range: { from: moment(1443454528000), to: moment(1443454528000) },
+        range: { from: dateTime(1443454528000), to: dateTime(1443454528000) },
         targets: [{ expr: 'test{job="testjob"}', format: 'heatmap', legendFormat: '{{le}}' }],
         interval: '1s',
       };
@@ -310,8 +310,8 @@ describe('PrometheusDatasource', () => {
       ctx.templateSrvMock.replace = jest.fn();
       ctx.timeSrvMock.timeRange = () => {
         return {
-          from: moment(1531468681),
-          to: moment(1531489712),
+          from: dateTime(1531468681),
+          to: dateTime(1531489712),
         };
       };
       ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
@@ -344,7 +344,7 @@ const SECOND = 1000;
 const MINUTE = 60 * SECOND;
 const HOUR = 60 * MINUTE;
 
-const time = ({ hours = 0, seconds = 0, minutes = 0 }) => moment(hours * HOUR + minutes * MINUTE + seconds * SECOND);
+const time = ({ hours = 0, seconds = 0, minutes = 0 }) => dateTime(hours * HOUR + minutes * MINUTE + seconds * SECOND);
 
 const ctx = {} as any;
 const instanceSettings = {

+ 3 - 3
public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts

@@ -1,7 +1,7 @@
-import moment from 'moment';
 import { PrometheusDatasource } from '../datasource';
 import PrometheusMetricFindQuery from '../metric_find_query';
 import q from 'q';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('PrometheusMetricFindQuery', () => {
   const instanceSettings = {
@@ -12,8 +12,8 @@ describe('PrometheusMetricFindQuery', () => {
     jsonData: { httpMethod: 'GET' },
   };
   const raw = {
-    from: moment.utc('2018-04-25 10:00'),
-    to: moment.utc('2018-04-25 11:00'),
+    from: toUtc('2018-04-25 10:00'),
+    to: toUtc('2018-04-25 11:00'),
   };
   const ctx: any = {
     backendSrvMock: {

+ 3 - 3
public/app/plugins/datasource/stackdriver/specs/datasource.test.ts

@@ -1,8 +1,8 @@
 import StackdriverDataSource from '../datasource';
 import { metricDescriptors } from './testData';
-import moment from 'moment';
 import { TemplateSrv } from 'app/features/templating/template_srv';
 import { CustomVariable } from 'app/features/templating/all';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('StackdriverDataSource', () => {
   const instanceSettings = {
@@ -73,8 +73,8 @@ describe('StackdriverDataSource', () => {
   describe('When performing query', () => {
     const options = {
       range: {
-        from: moment.utc('2017-08-22T20:00:00Z'),
-        to: moment.utc('2017-08-22T23:59:00Z'),
+        from: toUtc('2017-08-22T20:00:00Z'),
+        to: toUtc('2017-08-22T23:59:00Z'),
       },
       rangeRaw: {
         from: 'now-4h',

+ 3 - 3
public/app/plugins/datasource/testdata/query_ctrl.ts

@@ -1,9 +1,9 @@
 import _ from 'lodash';
 
 import { QueryCtrl } from 'app/plugins/sdk';
-import moment from 'moment';
 import { defaultQuery } from './StreamHandler';
 import { getBackendSrv } from 'app/core/services/backend_srv';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export class TestDataQueryCtrl extends QueryCtrl {
   static templateUrl = 'partials/query.editor.html';
@@ -20,14 +20,14 @@ export class TestDataQueryCtrl extends QueryCtrl {
 
     this.target.scenarioId = this.target.scenarioId || 'random_walk';
     this.scenarioList = [];
-    this.newPointTime = moment();
+    this.newPointTime = dateTime();
     this.selectedPoint = { text: 'Select point', value: null };
   }
 
   getPoints() {
     return _.map(this.target.points, (point, index) => {
       return {
-        text: moment(point[1]).format('MMMM Do YYYY, H:mm:ss') + ' : ' + point[0],
+        text: dateTime(point[1]).format('MMMM Do YYYY, H:mm:ss') + ' : ' + point[0],
         value: index,
       };
     });

+ 2 - 2
public/app/plugins/panel/alertlist/module.ts

@@ -1,9 +1,9 @@
 import _ from 'lodash';
-import moment from 'moment';
 import alertDef from '../../../features/alerting/state/alertDef';
 import { PanelCtrl } from 'app/plugins/sdk';
 
 import * as dateMath from '@grafana/ui/src/utils/datemath';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 class AlertListPanel extends PanelCtrl {
   static templateUrl = 'module.html';
@@ -157,7 +157,7 @@ class AlertListPanel extends PanelCtrl {
       this.currentAlerts = this.sortResult(
         _.map(res, al => {
           al.stateModel = alertDef.getStateDisplayModel(al.state);
-          al.newStateDateAgo = moment(al.newStateDate)
+          al.newStateDateAgo = dateTime(al.newStateDate)
             .locale('en')
             .fromNow(true);
           return al;

+ 3 - 3
public/app/plugins/panel/graph/graph.ts

@@ -10,7 +10,6 @@ import './jquery.flot.events';
 
 import $ from 'jquery';
 import _ from 'lodash';
-import moment from 'moment';
 import { tickStep } from 'app/core/utils/ticks';
 import { appEvents, coreModule, updateLegendValues } from 'app/core/core';
 import GraphTooltip from './graph_tooltip';
@@ -27,6 +26,7 @@ import { Legend, GraphLegendProps } from './Legend/Legend';
 import { GraphCtrl } from './module';
 import { getValueFormat } from '@grafana/ui';
 import { provideTheme } from 'app/core/utils/ConfigProvider';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 const LegendWithThemeProvider = provideTheme(Legend);
 
@@ -164,8 +164,8 @@ class GraphElement {
     } else {
       this.scope.$apply(() => {
         this.timeSrv.setTime({
-          from: moment.utc(ranges.xaxis.from),
-          to: moment.utc(ranges.xaxis.to),
+          from: toUtc(ranges.xaxis.from),
+          to: toUtc(ranges.xaxis.to),
         });
       });
     }

+ 9 - 9
public/app/plugins/panel/graph/specs/graph.test.ts

@@ -24,17 +24,17 @@ import { PanelCtrl } from 'app/features/panel/panel_ctrl';
 import config from 'app/core/config';
 
 import TimeSeries from 'app/core/time_series2';
-import moment from 'moment';
 import $ from 'jquery';
 import { graphDirective } from '../graph';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 const ctx = {} as any;
 let ctrl;
 const scope = {
   ctrl: {},
   range: {
-    from: moment([2015, 1, 1]),
-    to: moment([2015, 11, 20]),
+    from: dateTime([2015, 1, 1]),
+    to: dateTime([2015, 11, 20]),
   },
   $on: () => {},
 };
@@ -85,8 +85,8 @@ describe('grafanaGraph', () => {
         getTimezone: () => 'browser',
       },
       range: {
-        from: moment([2015, 1, 1, 10]),
-        to: moment([2015, 1, 1, 22]),
+        from: dateTime([2015, 1, 1, 10]),
+        to: dateTime([2015, 1, 1, 22]),
       },
     } as any;
 
@@ -443,8 +443,8 @@ describe('grafanaGraph', () => {
     describe('and the range is less than 24 hours', () => {
       beforeEach(() => {
         setupCtx(() => {
-          ctrl.range.from = moment([2015, 1, 1, 10]);
-          ctrl.range.to = moment([2015, 1, 1, 22]);
+          ctrl.range.from = dateTime([2015, 1, 1, 10]);
+          ctrl.range.to = dateTime([2015, 1, 1, 22]);
         });
       });
 
@@ -457,8 +457,8 @@ describe('grafanaGraph', () => {
     describe('and the range is less than one year', () => {
       beforeEach(() => {
         setupCtx(() => {
-          ctrl.range.from = moment([2015, 1, 1]);
-          ctrl.range.to = moment([2015, 11, 20]);
+          ctrl.range.from = dateTime([2015, 1, 1]);
+          ctrl.range.to = dateTime([2015, 11, 20]);
         });
       });
 

+ 4 - 4
public/app/plugins/panel/graph/specs/graph_ctrl.test.ts

@@ -1,5 +1,5 @@
-import moment from 'moment';
 import { GraphCtrl } from '../module';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 jest.mock('../graph', () => ({}));
 
@@ -50,7 +50,7 @@ describe('GraphCtrl', () => {
         },
       ];
 
-      ctx.ctrl.range = { from: moment().valueOf(), to: moment().valueOf() };
+      ctx.ctrl.range = { from: dateTime().valueOf(), to: dateTime().valueOf() };
       ctx.ctrl.onDataReceived(data);
     });
 
@@ -62,10 +62,10 @@ describe('GraphCtrl', () => {
   describe('when time series are inside range', () => {
     beforeEach(() => {
       const range = {
-        from: moment()
+        from: dateTime()
           .subtract(1, 'days')
           .valueOf(),
-        to: moment().valueOf(),
+        to: dateTime().valueOf(),
       };
 
       const data = [

+ 97 - 95
public/app/plugins/panel/graph/specs/time_region_manager.test.ts

@@ -1,5 +1,5 @@
 import { TimeRegionManager, colorModes } from '../time_region_manager';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('TimeRegionManager', () => {
   function plotOptionsScenario(desc, func) {
@@ -32,9 +32,11 @@ describe('TimeRegionManager', () => {
           `Time range: from=${ctx.panelCtrl.range.from.format()}, to=${ctx.panelCtrl.range.to.format()}`,
           ctx.panelCtrl.range.from._isUTC
         );
-        ctx.options.grid.markings.forEach((m, i) => {
+        ctx.options.grid.markings.forEach((m: any, i: number) => {
           console.log(
-            `Marking (${i}): from=${moment(m.xaxis.from).format()}, to=${moment(m.xaxis.to).format()}, color=${m.color}`
+            `Marking (${i}): from=${dateTime(m.xaxis.from).format()}, to=${dateTime(m.xaxis.to).format()}, color=${
+              m.color
+            }`
           );
         });
       };
@@ -48,16 +50,16 @@ describe('TimeRegionManager', () => {
       const regions = [
         { fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, line: true, lineColor: '#ffffff', colorMode: 'custom' },
       ];
-      const from = moment('2018-01-01T00:00:00+01:00');
-      const to = moment('2018-01-01T23:59:00+01:00');
+      const from = dateTime('2018-01-01T00:00:00+01:00');
+      const to = dateTime('2018-01-01T23:59:00+01:00');
       expect(() => ctx.setup(regions, from, to)).not.toThrow();
     });
     plotOptionsScenario('should not throw an error when lineColor is undefined', ctx => {
       const regions = [
         { fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, fillColor: '#ffffff', line: true, colorMode: 'custom' },
       ];
-      const from = moment('2018-01-01T00:00:00+01:00');
-      const to = moment('2018-01-01T23:59:00+01:00');
+      const from = dateTime('2018-01-01T00:00:00+01:00');
+      const to = dateTime('2018-01-01T23:59:00+01:00');
       expect(() => ctx.setup(regions, from, to)).not.toThrow();
     });
   });
@@ -65,8 +67,8 @@ describe('TimeRegionManager', () => {
   describe('When creating plot markings using local time', () => {
     plotOptionsScenario('for day of week region', ctx => {
       const regions = [{ fromDayOfWeek: 1, toDayOfWeek: 1, fill: true, line: true, colorMode: 'red' }];
-      const from = moment('2018-01-01T00:00:00+01:00');
-      const to = moment('2018-01-01T23:59:00+01:00');
+      const from = dateTime('2018-01-01T00:00:00+01:00');
+      const to = dateTime('2018-01-01T23:59:00+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -75,30 +77,30 @@ describe('TimeRegionManager', () => {
 
       it('should add fill', () => {
         const markings = ctx.options.grid.markings;
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-01T01:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-02T00:59:59+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-01T01:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-02T00:59:59+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
       });
 
       it('should add line before', () => {
         const markings = ctx.options.grid.markings;
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-01T01:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-01T01:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-01T01:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-01T01:00:00+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.line);
       });
 
       it('should add line after', () => {
         const markings = ctx.options.grid.markings;
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-02T00:59:59+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-02T00:59:59+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-02T00:59:59+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-02T00:59:59+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.line);
       });
     });
 
     plotOptionsScenario('for time from region', ctx => {
       const regions = [{ from: '05:00', fill: true, colorMode: 'red' }];
-      const from = moment('2018-01-01T00:00+01:00');
-      const to = moment('2018-01-03T23:59+01:00');
+      const from = dateTime('2018-01-01T00:00+01:00');
+      const to = dateTime('2018-01-03T23:59+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -108,24 +110,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill at 05:00 each day', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-01T06:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-01T06:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-01T06:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-01T06:00:00+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-02T06:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-02T06:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-02T06:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-02T06:00:00+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-03T06:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-03T06:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-03T06:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-03T06:00:00+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for time to region', ctx => {
       const regions = [{ to: '05:00', fill: true, colorMode: 'red' }];
-      const from = moment('2018-02-01T00:00+01:00');
-      const to = moment('2018-02-03T23:59+01:00');
+      const from = dateTime('2018-02-01T00:00+01:00');
+      const to = dateTime('2018-02-03T23:59+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -135,24 +137,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill at 05:00 each day', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-02-01T06:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-02-01T06:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-02-01T06:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-02-01T06:00:00+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-02-02T06:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-02-02T06:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-02-02T06:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-02-02T06:00:00+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-02-03T06:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-02-03T06:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-02-03T06:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-02-03T06:00:00+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for time from/to region', ctx => {
       const regions = [{ from: '00:00', to: '05:00', fill: true, colorMode: 'red' }];
-      const from = moment('2018-12-01T00:00+01:00');
-      const to = moment('2018-12-03T23:59+01:00');
+      const from = dateTime('2018-12-01T00:00+01:00');
+      const to = dateTime('2018-12-03T23:59+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -162,24 +164,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill between 00:00 and 05:00 each day', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-12-01T01:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-12-01T06:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-01T01:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-01T06:00:00+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-12-02T01:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-12-02T06:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-12-02T01:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-12-02T06:00:00+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-12-03T01:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-12-03T06:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-12-03T01:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-12-03T06:00:00+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for time from/to region crossing midnight', ctx => {
       const regions = [{ from: '22:00', to: '00:30', fill: true, colorMode: 'red' }];
-      const from = moment('2018-12-01T12:00+01:00');
-      const to = moment('2018-12-04T08:00+01:00');
+      const from = dateTime('2018-12-01T12:00+01:00');
+      const to = dateTime('2018-12-04T08:00+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -189,24 +191,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill between 22:00 and 00:30 each day', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-12-01T23:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-12-02T01:30:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-01T23:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-02T01:30:00+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-12-02T23:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-12-03T01:30:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-12-02T23:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-12-03T01:30:00+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-12-03T23:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-12-04T01:30:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-12-03T23:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-12-04T01:30:00+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for day of week from/to region', ctx => {
       const regions = [{ fromDayOfWeek: 7, toDayOfWeek: 7, fill: true, colorMode: 'red' }];
-      const from = moment('2018-01-01T18:45:05+01:00');
-      const to = moment('2018-01-22T08:27:00+01:00');
+      const from = dateTime('2018-01-01T18:45:05+01:00');
+      const to = dateTime('2018-01-22T08:27:00+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -216,24 +218,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill at each sunday', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-07T01:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-08T00:59:59+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-07T01:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-08T00:59:59+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-14T01:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-15T00:59:59+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-14T01:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-15T00:59:59+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-21T01:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-22T00:59:59+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-21T01:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-22T00:59:59+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for day of week from region', ctx => {
       const regions = [{ fromDayOfWeek: 7, fill: true, colorMode: 'red' }];
-      const from = moment('2018-01-01T18:45:05+01:00');
-      const to = moment('2018-01-22T08:27:00+01:00');
+      const from = dateTime('2018-01-01T18:45:05+01:00');
+      const to = dateTime('2018-01-22T08:27:00+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -243,24 +245,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill at each sunday', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-07T01:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-08T00:59:59+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-07T01:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-08T00:59:59+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-14T01:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-15T00:59:59+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-14T01:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-15T00:59:59+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-21T01:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-22T00:59:59+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-21T01:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-22T00:59:59+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for day of week to region', ctx => {
       const regions = [{ toDayOfWeek: 7, fill: true, colorMode: 'red' }];
-      const from = moment('2018-01-01T18:45:05+01:00');
-      const to = moment('2018-01-22T08:27:00+01:00');
+      const from = dateTime('2018-01-01T18:45:05+01:00');
+      const to = dateTime('2018-01-22T08:27:00+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -270,24 +272,24 @@ describe('TimeRegionManager', () => {
       it('should add one fill at each sunday', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-01-07T01:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-01-08T00:59:59+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-01-07T01:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-01-08T00:59:59+01:00').format());
         expect(markings[0].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-01-14T01:00:00+01:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-01-15T00:59:59+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-01-14T01:00:00+01:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-01-15T00:59:59+01:00').format());
         expect(markings[1].color).toBe(colorModes.red.color.fill);
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-01-21T01:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-01-22T00:59:59+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-01-21T01:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-01-22T00:59:59+01:00').format());
         expect(markings[2].color).toBe(colorModes.red.color.fill);
       });
     });
 
     plotOptionsScenario('for day of week from/to time region', ctx => {
       const regions = [{ fromDayOfWeek: 7, from: '23:00', toDayOfWeek: 1, to: '01:40', fill: true, colorMode: 'red' }];
-      const from = moment('2018-12-07T12:51:19+01:00');
-      const to = moment('2018-12-10T13:51:29+01:00');
+      const from = dateTime('2018-12-07T12:51:19+01:00');
+      const to = dateTime('2018-12-10T13:51:29+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 1 marking', () => {
@@ -297,15 +299,15 @@ describe('TimeRegionManager', () => {
       it('should add one fill between sunday 23:00 and monday 01:40', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-12-10T00:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-12-10T02:40:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-10T00:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-10T02:40:00+01:00').format());
       });
     });
 
     plotOptionsScenario('for day of week from/to time region', ctx => {
       const regions = [{ fromDayOfWeek: 6, from: '03:00', toDayOfWeek: 7, to: '02:00', fill: true, colorMode: 'red' }];
-      const from = moment('2018-12-07T12:51:19+01:00');
-      const to = moment('2018-12-10T13:51:29+01:00');
+      const from = dateTime('2018-12-07T12:51:19+01:00');
+      const to = dateTime('2018-12-10T13:51:29+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 1 marking', () => {
@@ -315,15 +317,15 @@ describe('TimeRegionManager', () => {
       it('should add one fill between saturday 03:00 and sunday 02:00', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-12-08T04:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-12-09T03:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-12-08T04:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-12-09T03:00:00+01:00').format());
       });
     });
 
     plotOptionsScenario('for day of week from/to time region with daylight saving time', ctx => {
       const regions = [{ fromDayOfWeek: 7, from: '20:00', toDayOfWeek: 7, to: '23:00', fill: true, colorMode: 'red' }];
-      const from = moment('2018-03-17T06:00:00+01:00');
-      const to = moment('2018-04-03T06:00:00+02:00');
+      const from = dateTime('2018-03-17T06:00:00+01:00');
+      const to = dateTime('2018-04-03T06:00:00+02:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -333,21 +335,21 @@ describe('TimeRegionManager', () => {
       it('should add one fill at each sunday between 20:00 and 23:00', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-03-18T21:00:00+01:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-03-19T00:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-03-18T21:00:00+01:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-03-19T00:00:00+01:00').format());
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-03-25T22:00:00+02:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-03-26T01:00:00+02:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-03-25T22:00:00+02:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-03-26T01:00:00+02:00').format());
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-04-01T22:00:00+02:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-04-02T01:00:00+02:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-04-01T22:00:00+02:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-04-02T01:00:00+02:00').format());
       });
     });
 
     plotOptionsScenario('for each day of week with winter time', ctx => {
       const regions = [{ fromDayOfWeek: 7, toDayOfWeek: 7, fill: true, colorMode: 'red' }];
-      const from = moment('2018-10-20T14:50:11+02:00');
-      const to = moment('2018-11-07T12:56:23+01:00');
+      const from = dateTime('2018-10-20T14:50:11+02:00');
+      const to = dateTime('2018-11-07T12:56:23+01:00');
       ctx.setup(regions, from, to);
 
       it('should add 3 markings', () => {
@@ -357,14 +359,14 @@ describe('TimeRegionManager', () => {
       it('should add one fill at each sunday', () => {
         const markings = ctx.options.grid.markings;
 
-        expect(moment(markings[0].xaxis.from).format()).toBe(moment('2018-10-21T02:00:00+02:00').format());
-        expect(moment(markings[0].xaxis.to).format()).toBe(moment('2018-10-22T01:59:59+02:00').format());
+        expect(dateTime(markings[0].xaxis.from).format()).toBe(dateTime('2018-10-21T02:00:00+02:00').format());
+        expect(dateTime(markings[0].xaxis.to).format()).toBe(dateTime('2018-10-22T01:59:59+02:00').format());
 
-        expect(moment(markings[1].xaxis.from).format()).toBe(moment('2018-10-28T02:00:00+02:00').format());
-        expect(moment(markings[1].xaxis.to).format()).toBe(moment('2018-10-29T00:59:59+01:00').format());
+        expect(dateTime(markings[1].xaxis.from).format()).toBe(dateTime('2018-10-28T02:00:00+02:00').format());
+        expect(dateTime(markings[1].xaxis.to).format()).toBe(dateTime('2018-10-29T00:59:59+01:00').format());
 
-        expect(moment(markings[2].xaxis.from).format()).toBe(moment('2018-11-04T01:00:00+01:00').format());
-        expect(moment(markings[2].xaxis.to).format()).toBe(moment('2018-11-05T00:59:59+01:00').format());
+        expect(dateTime(markings[2].xaxis.from).format()).toBe(dateTime('2018-11-04T01:00:00+01:00').format());
+        expect(dateTime(markings[2].xaxis.to).format()).toBe(dateTime('2018-11-05T00:59:59+01:00').format());
       });
     });
   });

+ 7 - 4
public/app/plugins/panel/graph/time_region_manager.ts

@@ -1,7 +1,7 @@
 import 'vendor/flot/jquery.flot';
 import _ from 'lodash';
-import moment from 'moment';
 import { GrafanaThemeType, getColorFromHexRgbOrName } from '@grafana/ui';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 type TimeRegionColorDefinition = {
   fill: string;
@@ -83,7 +83,10 @@ export class TimeRegionManager {
       return;
     }
 
-    const tRange = { from: moment(this.panelCtrl.range.from).utc(), to: moment(this.panelCtrl.range.to).utc() };
+    const tRange = {
+      from: dateTime(this.panelCtrl.range.from).utc(),
+      to: dateTime(this.panelCtrl.range.to).utc(),
+    };
 
     let i, hRange, timeRegion, regions, fromStart, fromEnd, timeRegionColor: TimeRegionColorDefinition;
 
@@ -143,7 +146,7 @@ export class TimeRegionManager {
 
       regions = [];
 
-      fromStart = moment(tRange.from);
+      fromStart = dateTime(tRange.from);
       fromStart.set('hour', 0);
       fromStart.set('minute', 0);
       fromStart.set('second', 0);
@@ -160,7 +163,7 @@ export class TimeRegionManager {
           break;
         }
 
-        fromEnd = moment(fromStart);
+        fromEnd = dateTime(fromStart);
 
         if (hRange.from.h <= hRange.to.h) {
           fromEnd.add(hRange.to.h - hRange.from.h, 'hours');

+ 3 - 3
public/app/plugins/panel/heatmap/rendering.ts

@@ -1,6 +1,5 @@
 import _ from 'lodash';
 import $ from 'jquery';
-import moment from 'moment';
 import * as d3 from 'd3';
 import { appEvents, contextSrv } from 'app/core/core';
 import * as ticksUtils from 'app/core/utils/ticks';
@@ -8,6 +7,7 @@ import { HeatmapTooltip } from './heatmap_tooltip';
 import { mergeZeroBuckets } from './heatmap_data_converter';
 import { getColorScale, getOpacityScale } from './color_scale';
 import { GrafanaThemeType, getColorFromHexRgbOrName, getValueFormat } from '@grafana/ui';
+import { toUtc } from '@grafana/ui/src/utils/moment_wrapper';
 
 const MIN_CARD_SIZE = 1,
   CARD_PADDING = 1,
@@ -713,8 +713,8 @@ export class HeatmapRenderer {
       const timeTo = this.xScale.invert(Math.max(this.selection.x1, this.selection.x2) - this.yAxisWidth);
 
       this.ctrl.timeSrv.setTime({
-        from: moment.utc(timeFrom),
-        to: moment.utc(timeTo),
+        from: toUtc(timeFrom),
+        to: toUtc(timeTo),
       });
     }
 

+ 4 - 4
public/app/plugins/panel/heatmap/specs/heatmap_ctrl.test.ts

@@ -1,5 +1,5 @@
-import moment from 'moment';
 import { HeatmapCtrl } from '../heatmap_ctrl';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('HeatmapCtrl', () => {
   const ctx = {} as any;
@@ -32,7 +32,7 @@ describe('HeatmapCtrl', () => {
         },
       ];
 
-      ctx.ctrl.range = { from: moment().valueOf(), to: moment().valueOf() };
+      ctx.ctrl.range = { from: dateTime().valueOf(), to: dateTime().valueOf() };
       ctx.ctrl.onDataReceived(data);
     });
 
@@ -44,10 +44,10 @@ describe('HeatmapCtrl', () => {
   describe('when time series are inside range', () => {
     beforeEach(() => {
       const range = {
-        from: moment()
+        from: dateTime()
           .subtract(1, 'days')
           .valueOf(),
-        to: moment().valueOf(),
+        to: dateTime().valueOf(),
       };
 
       const data = [

+ 3 - 3
public/app/plugins/panel/singlestat/specs/singlestat.test.ts

@@ -1,5 +1,5 @@
 import { SingleStatCtrl } from '../module';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 describe('SingleStatCtrl', () => {
   const ctx = {} as any;
@@ -89,7 +89,7 @@ describe('SingleStatCtrl', () => {
     });
 
     it('should set formatted value', () => {
-      expect(moment(ctx.data.valueFormatted).valueOf()).toBe(1505634997000);
+      expect(dateTime(ctx.data.valueFormatted).valueOf()).toBe(1505634997000);
     });
   });
 
@@ -120,7 +120,7 @@ describe('SingleStatCtrl', () => {
     });
 
     it('should set formatted value', () => {
-      expect(ctx.data.valueFormatted).toBe(moment(1505634997920).format('MM/DD/YYYY h:mm:ss a'));
+      expect(ctx.data.valueFormatted).toBe(dateTime(1505634997920).format('MM/DD/YYYY h:mm:ss a'));
     });
   });
 

+ 2 - 2
public/app/plugins/panel/table/renderer.ts

@@ -1,7 +1,7 @@
 import _ from 'lodash';
-import moment from 'moment';
 import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType, stringToJsRegex } from '@grafana/ui';
 import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export class TableRenderer {
   formatters: any[];
@@ -105,7 +105,7 @@ export class TableRenderer {
           v = parseInt(v, 10);
         }
 
-        let date = moment(v);
+        let date = dateTime(v);
 
         if (this.isUtc) {
           date = date.utc();

+ 2 - 2
public/test/helpers/getQueryOptions.ts

@@ -1,11 +1,11 @@
 import { DataQueryRequest, DataQuery } from '@grafana/ui';
-import moment from 'moment';
+import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
 
 export function getQueryOptions<TQuery extends DataQuery>(
   options: Partial<DataQueryRequest<TQuery>>
 ): DataQueryRequest<TQuery> {
   const raw = { from: 'now', to: 'now-1h' };
-  const range = { from: moment(), to: moment(), raw: raw };
+  const range = { from: dateTime(), to: dateTime(), raw: raw };
 
   const defaults: DataQueryRequest<TQuery> = {
     requestId: 'TEST',

+ 10 - 5
tslint.json

@@ -2,14 +2,18 @@
   "rules": {
     "array-type": [true, "array-simple"],
     "arrow-return-shorthand": true,
-    "ban": [true,
-      {"name": "Array", "message": "tsstyle#array-constructor"}
+    "ban": [
+      true,
+      { "name": "Array", "message": "tsstyle#array-constructor" }
+      // { "name": "moment", "message": "Use 'dateTime' instead." }
     ],
-    "ban-types": [true,
+    "ban-types": [
+      true,
       ["Object", "Use {} instead."],
       ["String", "Use 'string' instead."],
       ["Number", "Use 'number' instead."],
-      ["Boolean", "Use 'boolean' instead."]
+      ["Boolean", "Use 'boolean' instead."],
+      ["Moment", "Use 'DateTime' instead."]
     ],
     "interface-name": [true, "never-prefix"],
     "no-string-throw": true,
@@ -71,6 +75,7 @@
       "allow-pascal-case"
     ],
     "use-isnan": true,
-    "whitespace": [true, "check-branch", "check-decl", "check-type", "check-preblock"]
+    "whitespace": [true, "check-branch", "check-decl", "check-type", "check-preblock"],
+    "import-blacklist": [true, "moment"]
   }
 }