refactor(moment): Replace Moment.js with DayJs (#31310)
This commit is contained in:
parent
b382ef1058
commit
a193d790b2
|
|
@ -34175,7 +34175,8 @@
|
|||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.escape": {
|
||||
"version": "4.0.1",
|
||||
|
|
@ -39964,6 +39965,8 @@
|
|||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
|
|
@ -39974,6 +39977,7 @@
|
|||
"resolved": "https://registry.npmjs.org/moment-locales-webpack-plugin/-/moment-locales-webpack-plugin-1.2.0.tgz",
|
||||
"integrity": "sha512-QAi5v0OlPUP7GXviKMtxnpBAo8WmTHrUNN7iciAhNOEAd9evCOvuN0g1N7ThIg3q11GLCkjY1zQ2saRcf/43nQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.difference": "^4.5.0"
|
||||
},
|
||||
|
|
@ -58121,9 +58125,9 @@
|
|||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"d3-array": "^1.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1"
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@superset-ui/chart-controls": "*",
|
||||
|
|
@ -58150,8 +58154,8 @@
|
|||
"@superset-ui/chart-controls": "*",
|
||||
"@superset-ui/core": "*",
|
||||
"ace-builds": "^1.4.14",
|
||||
"dayjs": "^1.11.13",
|
||||
"lodash": "^4.17.11",
|
||||
"moment": "^2.26.0",
|
||||
"react": "^16.13.1",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "^16.13.1"
|
||||
|
|
@ -68727,9 +68731,9 @@
|
|||
"version": "file:plugins/plugin-chart-echarts",
|
||||
"requires": {
|
||||
"d3-array": "^1.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1"
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"@superset-ui/plugin-chart-handlebars": {
|
||||
|
|
@ -86638,7 +86642,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.30.1"
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="
|
||||
},
|
||||
"moment-locales-webpack-plugin": {
|
||||
"version": "1.2.0",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
"d3-color": "^1.4.1",
|
||||
"d3-scale": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"mousetrap": "^1.6.5",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-bootstrap-slider": "3.0.0",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
"dompurify": "^3.2.3",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"nvd3-fork": "^2.0.5",
|
||||
"prop-types": "^15.8.1",
|
||||
"urijs": "^1.19.11"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
*/
|
||||
import { kebabCase, throttle } from 'lodash';
|
||||
import d3 from 'd3';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import nv from 'nvd3-fork';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
|
|
@ -83,6 +84,8 @@ const NO_DATA_RENDER_DATA = [
|
|||
},
|
||||
];
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
const smartDateVerboseFormatter = getTimeFormatter(SMART_DATE_VERBOSE_ID);
|
||||
|
||||
// Override the noData render function to make a prettier UX
|
||||
|
|
@ -1055,7 +1058,7 @@ function nvd3Vis(element, props) {
|
|||
});
|
||||
const records = (annotationData[e.name].records || [])
|
||||
.map(r => {
|
||||
const timeValue = new Date(moment.utc(r[e.timeColumn]));
|
||||
const timeValue = new Date(dayjs.utc(r[e.timeColumn]));
|
||||
|
||||
return {
|
||||
...r,
|
||||
|
|
@ -1131,9 +1134,9 @@ function nvd3Vis(element, props) {
|
|||
|
||||
const records = (annotationData[e.name].records || [])
|
||||
.map(r => {
|
||||
const timeValue = new Date(moment.utc(r[e.timeColumn]));
|
||||
const timeValue = new Date(dayjs.utc(r[e.timeColumn]));
|
||||
const intervalEndValue = new Date(
|
||||
moment.utc(r[e.intervalEndColumn]),
|
||||
dayjs.utc(r[e.intervalEndColumn]),
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
"d3-array": "^1.2.0",
|
||||
"echarts": "^5.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1"
|
||||
"dayjs": "^1.11.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@superset-ui/chart-controls": "*",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import {
|
||||
ChartProps,
|
||||
getMetricLabel,
|
||||
|
|
@ -27,9 +28,11 @@ import {
|
|||
} from '@superset-ui/core';
|
||||
import { getComparisonFontSize, getHeaderFontSize } from './utils';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export const parseMetricValue = (metricValue: number | string | null) => {
|
||||
if (typeof metricValue === 'string') {
|
||||
const dateObject = moment.utc(metricValue, moment.ISO_8601, true);
|
||||
const dateObject = dayjs.utc(metricValue, undefined, true);
|
||||
if (dateObject.isValid()) {
|
||||
return dateObject.valueOf();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import {
|
||||
getTimeFormatter,
|
||||
getTimeFormatterForGranularity,
|
||||
|
|
@ -25,9 +26,11 @@ import {
|
|||
TimeGranularity,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export const parseMetricValue = (metricValue: number | string | null) => {
|
||||
if (typeof metricValue === 'string') {
|
||||
const dateObject = moment.utc(metricValue, moment.ISO_8601, true);
|
||||
const dateObject = dayjs.utc(metricValue, undefined, true);
|
||||
if (dateObject.isValid()) {
|
||||
return dateObject.valueOf();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
"@superset-ui/core": "*",
|
||||
"ace-builds": "^1.4.14",
|
||||
"lodash": "^4.17.11",
|
||||
"moment": "^2.26.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"react": "^16.13.1",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "^16.13.1"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import { SafeMarkdown, styled, t } from '@superset-ui/core';
|
||||
import Handlebars from 'handlebars';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import Helpers from 'just-handlebars-helpers';
|
||||
|
|
@ -78,7 +78,7 @@ export const HandlebarsViewer = ({
|
|||
// usage: {{dateFormat my_date format="MMMM YYYY"}}
|
||||
Handlebars.registerHelper('dateFormat', function (context, block) {
|
||||
const f = block.hash.format || 'YYYY-MM-DD';
|
||||
return moment(context).format(f);
|
||||
return dayjs(context).format(f);
|
||||
});
|
||||
|
||||
// usage: {{ }}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { useMemo, ReactNode } from 'react';
|
||||
import moment from 'moment';
|
||||
import Card from 'src/components/Card';
|
||||
import ProgressBar from 'src/components/ProgressBar';
|
||||
import { t, useTheme, QueryResponse } from '@superset-ui/core';
|
||||
|
|
@ -32,7 +31,7 @@ import {
|
|||
} from 'src/SqlLab/actions/sqlLab';
|
||||
import TableView from 'src/components/TableView';
|
||||
import Button from 'src/components/Button';
|
||||
import { fDuration } from 'src/utils/dates';
|
||||
import { fDuration, extendedDayjs } from 'src/utils/dates';
|
||||
import Icons from 'src/components/Icons';
|
||||
import Label from 'src/components/Label';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
|
|
@ -255,7 +254,9 @@ const QueryTable = ({
|
|||
</Button>
|
||||
);
|
||||
q.started = (
|
||||
<Label monospace>{moment(q.startDttm).format('L HH:mm:ss')}</Label>
|
||||
<Label monospace>
|
||||
{extendedDayjs(q.startDttm).format('L HH:mm:ss')}
|
||||
</Label>
|
||||
);
|
||||
q.querylink = (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import {
|
|||
VizType,
|
||||
} from '@superset-ui/core';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import rison from 'rison';
|
||||
import { createDatasource } from 'src/SqlLab/actions/sqlLab';
|
||||
import { addDangerToast } from 'src/components/MessageToasts/actions';
|
||||
|
|
@ -164,7 +164,7 @@ export const SaveDatasetModal = ({
|
|||
);
|
||||
|
||||
const getDefaultDatasetName = () =>
|
||||
`${datasource?.name || UNTITLED} ${moment().format('L HH:mm:ss')}`;
|
||||
`${datasource?.name || UNTITLED} ${dayjs().format('L HH:mm:ss')}`;
|
||||
const [datasetName, setDatasetName] = useState(getDefaultDatasetName());
|
||||
const [newOrOverwrite, setNewOrOverwrite] = useState(
|
||||
DatasetRadioState.SaveNew,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { TooltipContent } from './TooltipContent';
|
||||
|
||||
test('Rendering TooltipContent correctly - no timestep', () => {
|
||||
|
|
@ -31,7 +31,7 @@ test('Rendering TooltipContent correctly - no timestep', () => {
|
|||
test('Rendering TooltipContent correctly - with timestep', () => {
|
||||
render(<TooltipContent cachedTimestamp="01-01-2000" />);
|
||||
expect(screen.getByTestId('tooltip-content')?.textContent).toBe(
|
||||
`Loaded data cached ${moment
|
||||
`Loaded data cached ${extendedDayjs
|
||||
.utc('01-01-2000')
|
||||
.fromNow()}. Click to force-refresh`,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { FC } from 'react';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { t } from '@superset-ui/core';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -28,7 +28,7 @@ export const TooltipContent: FC<Props> = ({ cachedTimestamp }) => {
|
|||
const cachedText = cachedTimestamp ? (
|
||||
<span>
|
||||
{t('Loaded data cached')}
|
||||
<b> {moment.utc(cachedTimestamp).fromNow()}</b>
|
||||
<b> {extendedDayjs.utc(cachedTimestamp).fromNow()}</b>
|
||||
</span>
|
||||
) : (
|
||||
t('Loaded from cache')
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
/* eslint no-undef: 'error' */
|
||||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||||
import moment from 'moment';
|
||||
import {
|
||||
FeatureFlag,
|
||||
isDefined,
|
||||
|
|
@ -43,6 +42,7 @@ import { allowCrossDomain as domainShardingEnabled } from 'src/utils/hostNamesCo
|
|||
import { updateDataMask } from 'src/dataMask/actions';
|
||||
import { waitForAsyncData } from 'src/middleware/asyncEvent';
|
||||
import { safeStringify } from 'src/utils/safeStringify';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
|
||||
export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED';
|
||||
export function chartUpdateStarted(queryController, latestQueryFormData, key) {
|
||||
|
|
@ -454,7 +454,9 @@ export function exploreJSON(
|
|||
formData.extra_filters && formData.extra_filters.length > 0,
|
||||
viz_type: formData.viz_type,
|
||||
data_age: resultItem.is_cached
|
||||
? moment(new Date()).diff(moment.utc(resultItem.cached_dttm))
|
||||
? extendedDayjs(new Date()).diff(
|
||||
extendedDayjs.utc(resultItem.cached_dttm),
|
||||
)
|
||||
: null,
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -23,17 +23,18 @@ import {
|
|||
MouseEventHandler,
|
||||
} from 'react';
|
||||
|
||||
import moment, { Moment, MomentInput } from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import Icons from 'src/components/Icons';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const REFRESH_INTERVAL = 60000; // every minute
|
||||
|
||||
interface LastUpdatedProps {
|
||||
updatedAt: MomentInput;
|
||||
updatedAt: string | number | Date | undefined;
|
||||
update?: MouseEventHandler<HTMLSpanElement>;
|
||||
}
|
||||
moment.updateLocale('en', {
|
||||
extendedDayjs.updateLocale('en', {
|
||||
calendar: {
|
||||
lastDay: '[Yesterday at] LTS',
|
||||
sameDay: '[Today at] LTS',
|
||||
|
|
@ -62,14 +63,16 @@ export const LastUpdated: FunctionComponent<LastUpdatedProps> = ({
|
|||
updatedAt,
|
||||
update,
|
||||
}) => {
|
||||
const [timeSince, setTimeSince] = useState<Moment>(moment(updatedAt));
|
||||
const [timeSince, setTimeSince] = useState<dayjs.Dayjs>(
|
||||
extendedDayjs(updatedAt),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeSince(() => moment(updatedAt));
|
||||
setTimeSince(() => extendedDayjs(updatedAt));
|
||||
|
||||
// update UI every minute in case day changes
|
||||
const interval = setInterval(() => {
|
||||
setTimeSince(() => moment(updatedAt));
|
||||
setTimeSince(() => extendedDayjs(updatedAt));
|
||||
}, REFRESH_INTERVAL);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
RefObject,
|
||||
} from 'react';
|
||||
|
||||
// TODO: @msyavuz - Replace with dayjs after migrating datepicker to antd5
|
||||
import moment, { Moment } from 'moment';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { RangePicker } from 'src/components/DatePicker';
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import Timer, { TimerProps } from './index';
|
||||
|
||||
export default {
|
||||
|
|
@ -32,7 +32,7 @@ InteractiveTimer.args = {
|
|||
|
||||
InteractiveTimer.argTypes = {
|
||||
startTime: {
|
||||
defaultValue: moment().utc().valueOf(),
|
||||
defaultValue: extendedDayjs().utc().valueOf(),
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,12 +18,7 @@
|
|||
*/
|
||||
|
||||
import { FC } from 'react';
|
||||
import {
|
||||
render,
|
||||
waitFor,
|
||||
screen,
|
||||
waitForElementToBeRemoved,
|
||||
} from 'spec/helpers/testing-library';
|
||||
import { render, waitFor, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import type { TimezoneSelectorProps } from './index';
|
||||
|
||||
|
|
@ -52,7 +47,6 @@ test('render timezones in correct order for daylight saving time', async () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
const searchInput = screen.getByRole('combobox');
|
||||
userEvent.click(searchInput);
|
||||
|
||||
|
|
@ -62,7 +56,7 @@ test('render timezones in correct order for daylight saving time', async () => {
|
|||
|
||||
// first option is always current timezone
|
||||
expect(options[0]).toHaveTextContent('GMT -04:00 (Eastern Daylight Time)');
|
||||
expect(options[1]).toHaveTextContent('GMT -11:00 (Pacific/Pago_Pago)');
|
||||
expect(options[1]).toHaveTextContent('GMT -11:00 (Pacific/Midway)');
|
||||
expect(options[2]).toHaveTextContent('GMT -10:00 (Hawaii Standard Time)');
|
||||
expect(options[3]).toHaveTextContent('GMT -09:30 (Pacific/Marquesas)');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,14 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { FC } from 'react';
|
||||
import moment from 'moment-timezone';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
waitForElementToBeRemoved,
|
||||
} from 'spec/helpers/testing-library';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import type { TimezoneSelectorProps } from './index';
|
||||
|
||||
const loadComponent = (mockCurrentTime?: string) => {
|
||||
|
|
@ -46,20 +41,18 @@ const openSelectMenu = () => {
|
|||
userEvent.click(searchInput);
|
||||
};
|
||||
|
||||
jest.spyOn(moment.tz, 'guess').mockReturnValue('America/New_York');
|
||||
jest.spyOn(extendedDayjs.tz, 'guess').mockReturnValue('America/New_York');
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
test('use the timezone from `moment` if no timezone provided', async () => {
|
||||
test('use the timezone from `dayjs` if no timezone provided', async () => {
|
||||
const TimezoneSelector = await loadComponent('2022-01-01');
|
||||
const onTimezoneChange = jest.fn();
|
||||
render(<TimezoneSelector onTimezoneChange={onTimezoneChange} />);
|
||||
expect(screen.getByLabelText('Loading')).toBeVisible();
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
expect(onTimezoneChange).toHaveBeenCalledTimes(1);
|
||||
expect(onTimezoneChange).toHaveBeenCalledWith('America/Nassau');
|
||||
expect(onTimezoneChange).toHaveBeenCalledWith('America/Detroit');
|
||||
});
|
||||
|
||||
test('update to closest deduped timezone when timezone is provided', async () => {
|
||||
|
|
@ -68,12 +61,11 @@ test('update to closest deduped timezone when timezone is provided', async () =>
|
|||
render(
|
||||
<TimezoneSelector
|
||||
onTimezoneChange={onTimezoneChange}
|
||||
timezone="America/Los_Angeles"
|
||||
timezone="America/Tijuana"
|
||||
/>,
|
||||
);
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
expect(onTimezoneChange).toHaveBeenCalledTimes(1);
|
||||
expect(onTimezoneChange).toHaveBeenLastCalledWith('America/Vancouver');
|
||||
expect(onTimezoneChange).toHaveBeenLastCalledWith('America/Los_Angeles');
|
||||
});
|
||||
|
||||
test('use the default timezone when an invalid timezone is provided', async () => {
|
||||
|
|
@ -82,7 +74,6 @@ test('use the default timezone when an invalid timezone is provided', async () =
|
|||
render(
|
||||
<TimezoneSelector onTimezoneChange={onTimezoneChange} timezone="UTC" />,
|
||||
);
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
expect(onTimezoneChange).toHaveBeenCalledTimes(1);
|
||||
expect(onTimezoneChange).toHaveBeenLastCalledWith('Africa/Abidjan');
|
||||
});
|
||||
|
|
@ -96,12 +87,11 @@ test('render timezones in correct order for standard time', async () => {
|
|||
timezone="America/Nassau"
|
||||
/>,
|
||||
);
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
openSelectMenu();
|
||||
const options = await getSelectOptions();
|
||||
expect(options[0]).toHaveTextContent('GMT -05:00 (Eastern Standard Time)');
|
||||
expect(options[1]).toHaveTextContent('GMT -11:00 (Pacific/Pago_Pago)');
|
||||
expect(options[2]).toHaveTextContent('GMT -10:00 (Hawaii Standard Time)');
|
||||
expect(options[1]).toHaveTextContent('GMT -11:00 (Pacific/Midway)');
|
||||
expect(options[2]).toHaveTextContent('GMT -10:00 (America/Adak)');
|
||||
});
|
||||
|
||||
test('can select a timezone values and returns canonical timezone name', async () => {
|
||||
|
|
@ -113,17 +103,16 @@ test('can select a timezone values and returns canonical timezone name', async (
|
|||
timezone="Africa/Abidjan"
|
||||
/>,
|
||||
);
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
openSelectMenu();
|
||||
|
||||
const searchInput = screen.getByRole('combobox');
|
||||
// search for mountain time
|
||||
await userEvent.type(searchInput, 'mou');
|
||||
userEvent.type(searchInput, 'mou');
|
||||
const findTitle = 'GMT -07:00 (Mountain Standard Time)';
|
||||
const selectOption = await screen.findByTitle(findTitle);
|
||||
userEvent.click(selectOption);
|
||||
expect(onTimezoneChange).toHaveBeenCalledTimes(1);
|
||||
expect(onTimezoneChange).toHaveBeenLastCalledWith('America/Cambridge_Bay');
|
||||
expect(onTimezoneChange).toHaveBeenLastCalledWith('America/Boise');
|
||||
});
|
||||
|
||||
test('can update props and rerender with different values', async () => {
|
||||
|
|
@ -135,14 +124,13 @@ test('can update props and rerender with different values', async () => {
|
|||
timezone="Asia/Dubai"
|
||||
/>,
|
||||
);
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
expect(screen.getByTitle('GMT +04:00 (Asia/Dubai)')).toBeInTheDocument();
|
||||
expect(screen.getByTitle('GMT +04:00 (Asia/Baku)')).toBeInTheDocument();
|
||||
rerender(
|
||||
<TimezoneSelector
|
||||
onTimezoneChange={onTimezoneChange}
|
||||
timezone="Australia/Perth"
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTitle('GMT +08:00 (Australia/Perth)')).toBeInTheDocument();
|
||||
expect(onTimezoneChange).toHaveBeenCalledTimes(0);
|
||||
expect(screen.getByTitle('GMT +08:00 (Asia/Brunei)')).toBeInTheDocument();
|
||||
expect(onTimezoneChange).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { Select } from 'src/components';
|
||||
import Loading from 'src/components/Loading';
|
||||
import { isDST, extendedDayjs } from 'src/utils/dates';
|
||||
|
||||
const DEFAULT_TIMEZONE = {
|
||||
name: 'GMT Standard Time',
|
||||
|
|
@ -56,24 +56,11 @@ export default function TimezoneSelector({
|
|||
timezone,
|
||||
minWidth = MIN_SELECT_WIDTH, // smallest size for current values
|
||||
}: TimezoneSelectorProps) {
|
||||
const [momentLib, setMomentLib] = useState<
|
||||
typeof import('moment-timezone') | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
import('moment-timezone').then(momentLib =>
|
||||
setMomentLib(() => momentLib.default),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const { TIMEZONE_OPTIONS, TIMEZONE_OPTIONS_SORT_COMPARATOR, validTimezone } =
|
||||
useMemo(() => {
|
||||
if (!momentLib) {
|
||||
return {};
|
||||
}
|
||||
const currentDate = momentLib();
|
||||
const JANUARY = momentLib([2021, 1]);
|
||||
const JULY = momentLib([2021, 7]);
|
||||
const currentDate = extendedDayjs();
|
||||
const JANUARY = extendedDayjs.tz('2021-01-01');
|
||||
const JULY = extendedDayjs.tz('2021-07-01');
|
||||
|
||||
const getOffsetKey = (name: string) =>
|
||||
JANUARY.tz(name).utcOffset().toString() +
|
||||
|
|
@ -82,43 +69,41 @@ export default function TimezoneSelector({
|
|||
const getTimezoneName = (name: string) => {
|
||||
const offsets = getOffsetKey(name);
|
||||
return (
|
||||
(currentDate.tz(name).isDST()
|
||||
(isDST(currentDate.tz(name), name)
|
||||
? offsetsToName[offsets]?.[1]
|
||||
: offsetsToName[offsets]?.[0]) || name
|
||||
);
|
||||
};
|
||||
|
||||
const ALL_ZONES = momentLib.tz
|
||||
.countries()
|
||||
.map(country => momentLib.tz.zonesForCountry(country, true))
|
||||
.flat();
|
||||
const dedupedTimezones = new Map();
|
||||
|
||||
// TODO: remove this ts-ignore when typescript is upgraded to 5.1
|
||||
// @ts-ignore
|
||||
const ALL_ZONES: string[] = Intl.supportedValuesOf('timeZone');
|
||||
|
||||
const TIMEZONES: import('moment-timezone').MomentZoneOffset[] = [];
|
||||
ALL_ZONES.forEach(zone => {
|
||||
if (
|
||||
!TIMEZONES.find(
|
||||
option => getOffsetKey(option.name) === getOffsetKey(zone.name),
|
||||
)
|
||||
) {
|
||||
TIMEZONES.push(zone); // dedupe zones by offsets
|
||||
const offsetKey = getOffsetKey(zone);
|
||||
if (!dedupedTimezones.has(offsetKey)) {
|
||||
dedupedTimezones.set(offsetKey, zone);
|
||||
}
|
||||
});
|
||||
const TIMEZONES: string[] = Array.from(dedupedTimezones.values());
|
||||
|
||||
const TIMEZONE_OPTIONS = TIMEZONES.map(zone => ({
|
||||
label: `GMT ${momentLib
|
||||
.tz(currentDate, zone.name)
|
||||
.format('Z')} (${getTimezoneName(zone.name)})`,
|
||||
value: zone.name,
|
||||
offsets: getOffsetKey(zone.name),
|
||||
timezoneName: zone.name,
|
||||
label: `GMT ${extendedDayjs
|
||||
.tz(currentDate, zone)
|
||||
.format('Z')} (${getTimezoneName(zone)})`,
|
||||
value: zone,
|
||||
offsets: getOffsetKey(zone),
|
||||
timezoneName: zone,
|
||||
}));
|
||||
|
||||
const TIMEZONE_OPTIONS_SORT_COMPARATOR = (
|
||||
a: (typeof TIMEZONE_OPTIONS)[number],
|
||||
b: (typeof TIMEZONE_OPTIONS)[number],
|
||||
) =>
|
||||
momentLib.tz(currentDate, a.timezoneName).utcOffset() -
|
||||
momentLib.tz(currentDate, b.timezoneName).utcOffset();
|
||||
extendedDayjs.tz(currentDate, a.timezoneName).utcOffset() -
|
||||
extendedDayjs.tz(currentDate, b.timezoneName).utcOffset();
|
||||
|
||||
// pre-sort timezone options by time offset
|
||||
TIMEZONE_OPTIONS.sort(TIMEZONE_OPTIONS_SORT_COMPARATOR);
|
||||
|
|
@ -129,7 +114,7 @@ export default function TimezoneSelector({
|
|||
)?.value || DEFAULT_TIMEZONE.value;
|
||||
|
||||
const validTimezone = matchTimezoneToOptions(
|
||||
timezone || momentLib.tz.guess(),
|
||||
timezone || extendedDayjs.tz.guess(),
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -137,7 +122,7 @@ export default function TimezoneSelector({
|
|||
TIMEZONE_OPTIONS_SORT_COMPARATOR,
|
||||
validTimezone,
|
||||
};
|
||||
}, [momentLib, timezone]);
|
||||
}, [timezone]);
|
||||
|
||||
// force trigger a timezone update if provided `timezone` is not invalid
|
||||
useEffect(() => {
|
||||
|
|
@ -146,10 +131,6 @@ export default function TimezoneSelector({
|
|||
}
|
||||
}, [validTimezone, onTimezoneChange, timezone]);
|
||||
|
||||
if (!TIMEZONE_OPTIONS || !TIMEZONE_OPTIONS_SORT_COMPARATOR) {
|
||||
return <Loading position="inline-centered" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
ariaLabel={t('Timezone selector')}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
/* eslint-env browser */
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
styled,
|
||||
|
|
@ -258,7 +258,9 @@ const Header = () => {
|
|||
if (predefinedValue) {
|
||||
intervalMessage = t(predefinedValue[1]);
|
||||
} else {
|
||||
intervalMessage = moment.duration(interval, 'millisecond').humanize();
|
||||
intervalMessage = extendedDayjs
|
||||
.duration(interval, 'millisecond')
|
||||
.humanize();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { Fragment, useMemo, useCallback, RefObject, createRef } from 'react';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ReactDiffViewer from 'react-diff-viewer-continued';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
|
|
@ -179,7 +179,9 @@ const OverrideConfirmModal = ({ overwriteConfirmMetadata }: Props) => {
|
|||
newValue={newValue}
|
||||
leftTitle={t(
|
||||
'Last Updated %s by %s',
|
||||
moment.utc(overwriteConfirmMetadata.updatedAt).calendar(),
|
||||
extendedDayjs
|
||||
.utc(overwriteConfirmMetadata.updatedAt)
|
||||
.calendar(),
|
||||
overwriteConfirmMetadata.updatedBy,
|
||||
)}
|
||||
rightTitle="new value"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import { MouseEvent, Key, useState, useRef, RefObject } from 'react';
|
||||
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import {
|
||||
Behavior,
|
||||
css,
|
||||
|
|
@ -257,9 +257,11 @@ const SliceHeaderControls = (props: SliceHeaderControlsProps) => {
|
|||
const isTable = slice.viz_type === VizType.Table;
|
||||
const isPivotTable = slice.viz_type === VizType.PivotTable;
|
||||
const cachedWhen = (cachedDttm || []).map(itemCachedDttm =>
|
||||
moment.utc(itemCachedDttm).fromNow(),
|
||||
extendedDayjs.utc(itemCachedDttm).fromNow(),
|
||||
);
|
||||
const updatedWhen = updatedDttm ? moment.utc(updatedDttm).fromNow() : '';
|
||||
const updatedWhen = updatedDttm
|
||||
? extendedDayjs.utc(updatedDttm).fromNow()
|
||||
: '';
|
||||
const getCachedTitle = (itemCached: boolean) => {
|
||||
if (itemCached) {
|
||||
return t('Cached %s', cachedWhen);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
// TODO: @msyavuz - Replace this with dayjs after datepicker migration
|
||||
import moment, { Moment } from 'moment';
|
||||
import { CustomRangeType } from 'src/explore/components/controls/DateFilterControl/types';
|
||||
import { MOMENT_FORMAT } from './constants';
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import { isEmpty, isEqual } from 'lodash';
|
||||
// TODO: @msyavuz - Replace with dayjs when migrating datpicker to antd5
|
||||
import moment, { Moment } from 'moment';
|
||||
import {
|
||||
parseDttmToDate,
|
||||
|
|
|
|||
|
|
@ -18,13 +18,7 @@
|
|||
*/
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
within,
|
||||
waitForElementToBeRemoved,
|
||||
} from 'spec/helpers/testing-library';
|
||||
import { render, screen, within } from 'spec/helpers/testing-library';
|
||||
import { VizType } from '@superset-ui/core';
|
||||
import { buildErrorTooltipMessage } from './buildErrorTooltipMessage';
|
||||
import AlertReportModal, { AlertReportModalProps } from './AlertReportModal';
|
||||
|
|
@ -193,10 +187,8 @@ const comboboxSelect = async (
|
|||
) => {
|
||||
expect(element).toBeInTheDocument();
|
||||
userEvent.type(element, `${value}{enter}`);
|
||||
await waitFor(() => {
|
||||
const element = newElementQuery();
|
||||
expect(element).toBeInTheDocument();
|
||||
});
|
||||
const newElement = newElementQuery();
|
||||
expect(newElement).toBeInTheDocument();
|
||||
};
|
||||
|
||||
// --------------- TEST SECTION ------------------
|
||||
|
|
@ -526,7 +518,6 @@ test('renders default Schedule fields', async () => {
|
|||
useRedux: true,
|
||||
});
|
||||
userEvent.click(screen.getByTestId('schedule-panel'));
|
||||
await waitForElementToBeRemoved(() => screen.queryByLabelText('Loading'));
|
||||
const scheduleType = screen.getByRole('combobox', {
|
||||
name: /schedule type/i,
|
||||
});
|
||||
|
|
@ -561,12 +552,13 @@ test('shows CRON Expression when CRON is selected', async () => {
|
|||
useRedux: true,
|
||||
});
|
||||
userEvent.click(screen.getByTestId('schedule-panel'));
|
||||
await comboboxSelect(
|
||||
userEvent.click(screen.getByRole('combobox', { name: /schedule type/i }));
|
||||
userEvent.type(
|
||||
screen.getByRole('combobox', { name: /schedule type/i }),
|
||||
'cron schedule',
|
||||
() => screen.getByPlaceholderText(/cron expression/i),
|
||||
'cron schedule{enter}',
|
||||
);
|
||||
expect(screen.getByPlaceholderText(/cron expression/i)).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText(/cron expression/i)).toBeInTheDocument();
|
||||
});
|
||||
test('defaults to day when CRON is not selected', async () => {
|
||||
render(<AlertReportModal {...generateMockedProps(true, false, false)} />, {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import TableView, { EmptyWrapperType } from 'src/components/TableView';
|
||||
import { TagsList } from 'src/components/Tags';
|
||||
|
|
@ -54,7 +54,7 @@ interface TaggedObject {
|
|||
type: string;
|
||||
name: string;
|
||||
url: string;
|
||||
changed_on: moment.MomentInput;
|
||||
changed_on: string | number | Date;
|
||||
created_by: number | undefined;
|
||||
creator: string;
|
||||
owners: Owner[];
|
||||
|
|
@ -89,7 +89,7 @@ export default function AllEntitiesTable({
|
|||
const renderTable = (type: objectType) => {
|
||||
const data = objects[type].map((o: TaggedObject) => ({
|
||||
[type]: <a href={o.url}>{o.name}</a>,
|
||||
modified: moment.utc(o.changed_on).fromNow(),
|
||||
modified: extendedDayjs.utc(o.changed_on).fromNow(),
|
||||
tags: o.tags,
|
||||
owners: o.owners,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { FunctionComponent, useState, useEffect, ChangeEvent } from 'react';
|
|||
import { styled, t } from '@superset-ui/core';
|
||||
import { useSingleViewResource } from 'src/views/CRUD/hooks';
|
||||
import { RangePicker } from 'src/components/DatePicker';
|
||||
// TODO: @msyavuz - Remove this after datepicker
|
||||
import moment from 'moment';
|
||||
import Icons from 'src/components/Icons';
|
||||
import Modal from 'src/components/Modal';
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import Icons from 'src/components/Icons';
|
|||
import { useToasts } from 'src/components/MessageToasts/withToasts';
|
||||
import { useListViewResource } from 'src/views/CRUD/hooks';
|
||||
import { FilterOperator } from 'src/components/ListView';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import TruncatedList from 'src/components/TruncatedList';
|
||||
|
||||
interface DatasetUsageProps {
|
||||
|
|
@ -92,7 +92,9 @@ const columns: ColumnsType<Chart> = [
|
|||
sorter: true,
|
||||
defaultSortOrder: 'descend',
|
||||
render: (value, record) =>
|
||||
record.last_saved_at ? moment.utc(record.last_saved_at).fromNow() : null,
|
||||
record.last_saved_at
|
||||
? extendedDayjs.utc(record.last_saved_at).fromNow()
|
||||
: null,
|
||||
},
|
||||
{
|
||||
key: 'last_saved_by.first_name',
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { setItem, LocalStorageKeys } from 'src/utils/localStorageHelpers';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
|
@ -112,13 +112,16 @@ const getEntityUrl = (entity: ActivityObject) => {
|
|||
|
||||
const getEntityLastActionOn = (entity: ActivityObject) => {
|
||||
if ('time' in entity) {
|
||||
return t('Viewed %s', moment(entity.time).fromNow());
|
||||
return t('Viewed %s', extendedDayjs(entity.time).fromNow());
|
||||
}
|
||||
|
||||
let time: number | string | undefined | null;
|
||||
if ('changed_on' in entity) time = entity.changed_on;
|
||||
if ('changed_on_utc' in entity) time = entity.changed_on_utc;
|
||||
return t('Modified %s', time == null ? UNKNOWN_TIME : moment(time).fromNow());
|
||||
return t(
|
||||
'Modified %s',
|
||||
time == null ? UNKNOWN_TIME : extendedDayjs(time).fromNow(),
|
||||
);
|
||||
};
|
||||
|
||||
export default function ActivityTable({
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
styled,
|
||||
getExtensionsRegistry,
|
||||
} from '@superset-ui/core';
|
||||
import moment from 'moment';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
import ActionsBar, { ActionProps } from 'src/components/ListView/ActionsBar';
|
||||
import FacePile from 'src/components/FacePile';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
|
|
@ -263,7 +263,10 @@ function AlertList({
|
|||
},
|
||||
}: any) =>
|
||||
lastEvalDttm
|
||||
? moment.utc(lastEvalDttm).local().format(DATETIME_WITH_TIME_ZONE)
|
||||
? extendedDayjs
|
||||
.utc(lastEvalDttm)
|
||||
.local()
|
||||
.format(DATETIME_WITH_TIME_ZONE)
|
||||
: '',
|
||||
accessor: 'last_eval_dttm',
|
||||
Header: t('Last run'),
|
||||
|
|
|
|||
|
|
@ -36,13 +36,14 @@ import withToasts, { useToasts } from 'src/components/MessageToasts/withToasts';
|
|||
import { fetchObjectsByTagIds, fetchSingleTag } from 'src/features/tags/tags';
|
||||
import Loading from 'src/components/Loading';
|
||||
import getOwnerName from 'src/utils/getOwnerName';
|
||||
import { ConfigType } from 'dayjs';
|
||||
|
||||
interface TaggedObject {
|
||||
id: number;
|
||||
type: string;
|
||||
name: string;
|
||||
url: string;
|
||||
changed_on: moment.MomentInput;
|
||||
changed_on: ConfigType;
|
||||
created_by: number | undefined;
|
||||
creator: string;
|
||||
owners: Owner[];
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
SupersetClient,
|
||||
getClientErrorObject,
|
||||
} from '@superset-ui/core';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import rison from 'rison';
|
||||
|
||||
import ActionsBar, { ActionProps } from 'src/components/ListView/ActionsBar';
|
||||
|
|
@ -170,7 +170,9 @@ function AnnotationList({
|
|||
row: {
|
||||
original: { start_dttm: startDttm },
|
||||
},
|
||||
}: any) => moment(new Date(startDttm)).format('ll'),
|
||||
}: {
|
||||
row: { original: AnnotationObject };
|
||||
}) => dayjs(new Date(startDttm)).format('ll'),
|
||||
Header: t('Start'),
|
||||
accessor: 'start_dttm',
|
||||
},
|
||||
|
|
@ -179,12 +181,18 @@ function AnnotationList({
|
|||
row: {
|
||||
original: { end_dttm: endDttm },
|
||||
},
|
||||
}: any) => moment(new Date(endDttm)).format('ll'),
|
||||
}: {
|
||||
row: { original: AnnotationObject };
|
||||
}) => dayjs(new Date(endDttm)).format('ll'),
|
||||
Header: t('End'),
|
||||
accessor: 'end_dttm',
|
||||
},
|
||||
{
|
||||
Cell: ({ row: { original } }: any) => {
|
||||
Cell: ({
|
||||
row: { original },
|
||||
}: {
|
||||
row: { original: AnnotationObject };
|
||||
}) => {
|
||||
const handleEdit = () => handleAnnotationEdit(original);
|
||||
const handleDelete = () => setAnnotationCurrentlyDeleting(original);
|
||||
const actions = [
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { css, styled, t } from '@superset-ui/core';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import ListView from 'src/components/ListView';
|
||||
|
|
@ -32,6 +32,7 @@ import {
|
|||
useSingleViewResource,
|
||||
} from 'src/views/CRUD/hooks';
|
||||
import { AlertObject, LogObject } from 'src/features/alerts/types';
|
||||
import { AnnotationObject } from 'src/features/annotations/types';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
|
|
@ -119,7 +120,7 @@ function ExecutionLog({
|
|||
original: { scheduled_dttm: scheduledDttm },
|
||||
},
|
||||
}: any) =>
|
||||
moment(new Date(scheduledDttm)).format('YYYY-MM-DD hh:mm:ss a'),
|
||||
dayjs(new Date(scheduledDttm)).format('YYYY-MM-DD hh:mm:ss a'),
|
||||
accessor: 'scheduled_dttm',
|
||||
Header: t('Scheduled at (UTC)'),
|
||||
},
|
||||
|
|
@ -128,7 +129,9 @@ function ExecutionLog({
|
|||
row: {
|
||||
original: { start_dttm: startDttm },
|
||||
},
|
||||
}: any) => moment(new Date(startDttm)).format('YYYY-MM-DD hh:mm:ss a'),
|
||||
}: {
|
||||
row: { original: AnnotationObject };
|
||||
}) => dayjs(new Date(startDttm)).format('YYYY-MM-DD hh:mm:ss a'),
|
||||
Header: t('Start at (UTC)'),
|
||||
accessor: 'start_dttm',
|
||||
},
|
||||
|
|
@ -137,7 +140,9 @@ function ExecutionLog({
|
|||
row: {
|
||||
original: { start_dttm: startDttm, end_dttm: endDttm },
|
||||
},
|
||||
}: any) =>
|
||||
}: {
|
||||
row: { original: AnnotationObject };
|
||||
}) =>
|
||||
fDuration(new Date(startDttm).getTime(), new Date(endDttm).getTime()),
|
||||
Header: t('Duration'),
|
||||
disableSortBy: true,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import {
|
|||
t,
|
||||
useTheme,
|
||||
} from '@superset-ui/core';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
createFetchRelated,
|
||||
createFetchDistinct,
|
||||
|
|
@ -54,6 +53,7 @@ import Icons from 'src/components/Icons';
|
|||
import QueryPreviewModal from 'src/features/queries/QueryPreviewModal';
|
||||
import { addSuccessToast } from 'src/components/MessageToasts/actions';
|
||||
import getOwnerName from 'src/utils/getOwnerName';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
const SQL_PREVIEW_MAX_LINES = 4;
|
||||
|
|
@ -214,7 +214,7 @@ function QueryList({ addDangerToast }: QueryListProps) {
|
|||
original: { start_time },
|
||||
},
|
||||
}: any) => {
|
||||
const startMoment = moment.utc(start_time).local();
|
||||
const startMoment = extendedDayjs.utc(start_time).local();
|
||||
const formattedStartTimeData = startMoment
|
||||
.format(DATETIME_WITH_TIME_ZONE)
|
||||
.split(' ');
|
||||
|
|
@ -238,7 +238,9 @@ function QueryList({ addDangerToast }: QueryListProps) {
|
|||
}: any) => {
|
||||
const timerType = status === QueryState.Failed ? 'danger' : status;
|
||||
const timerTime = end_time
|
||||
? moment(moment.utc(end_time - start_time)).format(TIME_WITH_MS)
|
||||
? extendedDayjs(extendedDayjs.utc(end_time - start_time)).format(
|
||||
TIME_WITH_MS,
|
||||
)
|
||||
: '00:00:00.000';
|
||||
return (
|
||||
<TimerLabel type={timerType} role="timer">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import { setConfig as setHotLoaderConfig } from 'react-hot-loader';
|
||||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
|
||||
import moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import {
|
||||
configure,
|
||||
|
|
@ -44,7 +44,7 @@ const bootstrapData = getBootstrapData();
|
|||
// Configure translation
|
||||
if (typeof window !== 'undefined') {
|
||||
configure({ languagePack: bootstrapData.common.language_pack });
|
||||
moment.locale(bootstrapData.common.locale);
|
||||
dayjs.locale(bootstrapData.common.locale);
|
||||
} else {
|
||||
configure();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import moment from 'moment';
|
||||
|
||||
export const fDuration = function (t1, t2, format = 'HH:mm:ss.SS') {
|
||||
const diffSec = t2 - t1;
|
||||
const duration = moment(new Date(diffSec));
|
||||
return duration.utc().format(format);
|
||||
};
|
||||
|
||||
export const now = function () {
|
||||
// seconds from EPOCH as a float
|
||||
return moment().utc().valueOf();
|
||||
};
|
||||
|
||||
export const epochTimeXHoursAgo = function (h) {
|
||||
return moment().subtract(h, 'hours').utc().valueOf();
|
||||
};
|
||||
|
||||
export const epochTimeXDaysAgo = function (d) {
|
||||
return moment().subtract(d, 'days').utc().valueOf();
|
||||
};
|
||||
|
||||
export const epochTimeXYearsAgo = function (y) {
|
||||
return moment().subtract(y, 'years').utc().valueOf();
|
||||
};
|
||||
|
|
@ -22,20 +22,41 @@ import {
|
|||
epochTimeXHoursAgo,
|
||||
epochTimeXDaysAgo,
|
||||
epochTimeXYearsAgo,
|
||||
extendedDayjs,
|
||||
} from 'src/utils/dates';
|
||||
|
||||
describe('extendedDayjs', () => {
|
||||
it('returns dayjs object with extended methods', () => {
|
||||
const dayjs = extendedDayjs();
|
||||
expect(dayjs).toHaveProperty('utc');
|
||||
expect(dayjs).toHaveProperty('calendar');
|
||||
expect(dayjs).toHaveProperty('tz');
|
||||
expect(dayjs).toHaveProperty('fromNow');
|
||||
expect(
|
||||
extendedDayjs(
|
||||
'05/02/69 1:02:03 PM -05:00',
|
||||
'MM/DD/YY H:mm:ss A Z',
|
||||
).toISOString(),
|
||||
).toEqual('1969-05-02T18:02:03.000Z');
|
||||
expect(extendedDayjs).toHaveProperty('duration');
|
||||
expect(extendedDayjs).toHaveProperty('updateLocale');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fDuration', () => {
|
||||
it('is a function', () => {
|
||||
expect(typeof fDuration).toBe('function');
|
||||
});
|
||||
|
||||
it('returns a string', () => {
|
||||
expect(typeof fDuration(new Date(), new Date())).toBe('string');
|
||||
expect(typeof fDuration(new Date().getTime(), new Date().getTime())).toBe(
|
||||
'string',
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the expected output', () => {
|
||||
const output = fDuration('1496293608897', '1496293623406');
|
||||
expect(output).toBe('00:00:14.50');
|
||||
const output = fDuration(1496293608897, 1496293623406);
|
||||
expect(output).toBe('00:00:14.509');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import calendar from 'dayjs/plugin/calendar';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(calendar);
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(updateLocale);
|
||||
|
||||
export const extendedDayjs = dayjs;
|
||||
|
||||
export const fDuration = function (
|
||||
t1: number,
|
||||
t2: number,
|
||||
format = 'HH:mm:ss.SSS',
|
||||
): string {
|
||||
const diffSec = t2 - t1;
|
||||
const duration = dayjs(new Date(diffSec));
|
||||
return duration.utc().format(format);
|
||||
};
|
||||
|
||||
export const now = function (): number {
|
||||
// seconds from EPOCH as a float
|
||||
return dayjs().utc().valueOf();
|
||||
};
|
||||
|
||||
export const epochTimeXHoursAgo = function (h: number): number {
|
||||
return dayjs().subtract(h, 'hours').utc().valueOf();
|
||||
};
|
||||
|
||||
export const epochTimeXDaysAgo = function (d: number): number {
|
||||
return dayjs().subtract(d, 'days').utc().valueOf();
|
||||
};
|
||||
|
||||
export const epochTimeXYearsAgo = function (y: number): number {
|
||||
return dayjs().subtract(y, 'years').utc().valueOf();
|
||||
};
|
||||
|
||||
export const isDST = function (date: Dayjs, timezoneName: string): boolean {
|
||||
const standardOffset = dayjs.tz('2021-01-01', timezoneName).utcOffset();
|
||||
const currentOffset = date.tz(timezoneName).utcOffset();
|
||||
return currentOffset !== standardOffset;
|
||||
};
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { ReactNode } from 'react';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
formatNumber,
|
||||
formatTime,
|
||||
|
|
@ -35,6 +34,7 @@ import {
|
|||
XYChart,
|
||||
buildChartTheme,
|
||||
} from '@visx/xychart';
|
||||
import { extendedDayjs } from 'src/utils/dates';
|
||||
|
||||
interface Props {
|
||||
ariaLabel: string;
|
||||
|
|
@ -223,7 +223,7 @@ const SparklineCell = ({
|
|||
{idx !== undefined &&
|
||||
formatTime(
|
||||
dateFormat,
|
||||
moment.utc(entries[idx].time).toDate(),
|
||||
extendedDayjs.utc(entries[idx].time).toDate(),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ const getAvailableTranslationCodes = () => {
|
|||
.map(dirName => dirName.replace('_', '-'))
|
||||
.map(dirName => LOCALE_CODE_MAPPING[dirName] || dirName);
|
||||
}
|
||||
// Indicates to the MomentLocalesPlugin that we only want to keep 'en'.
|
||||
return [];
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue