mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 12:20:45 -03:30
Convert dates to use luxon.js
This commit is contained in:
parent
f6104dd438
commit
304ec80d80
32
awx/ui/package-lock.json
generated
32
awx/ui/package-lock.json
generated
@ -21,6 +21,7 @@
|
||||
"has-ansi": "4.0.0",
|
||||
"html-entities": "2.3.2",
|
||||
"js-yaml": "^3.13.1",
|
||||
"luxon": "^2.0.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.13.1",
|
||||
"react-ace": "^9.3.0",
|
||||
@ -14601,10 +14602,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "1.26.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz",
|
||||
"integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==",
|
||||
"optional": true,
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.0.1.tgz",
|
||||
"integrity": "sha512-8Eawf81c9ZlQj62W3eq4mp+C7SAIAnmaS7ZuEAiX503YMcn+0C1JnMQRtfaQj6B5qTZLgHv0F4H5WabBCvi1fw==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@ -21000,6 +21000,15 @@
|
||||
"luxon": "^1.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/rrule/node_modules/luxon": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz",
|
||||
"integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/rst-selector-parser": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
||||
@ -38379,10 +38388,9 @@
|
||||
}
|
||||
},
|
||||
"luxon": {
|
||||
"version": "1.26.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz",
|
||||
"integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==",
|
||||
"optional": true
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.0.1.tgz",
|
||||
"integrity": "sha512-8Eawf81c9ZlQj62W3eq4mp+C7SAIAnmaS7ZuEAiX503YMcn+0C1JnMQRtfaQj6B5qTZLgHv0F4H5WabBCvi1fw=="
|
||||
},
|
||||
"magic-string": {
|
||||
"version": "0.25.7",
|
||||
@ -43724,6 +43732,14 @@
|
||||
"requires": {
|
||||
"luxon": "^1.21.3",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"luxon": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz",
|
||||
"integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"rst-selector-parser": {
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"has-ansi": "4.0.0",
|
||||
"html-entities": "2.3.2",
|
||||
"js-yaml": "^3.13.1",
|
||||
"luxon": "^2.0.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.13.1",
|
||||
"react-ace": "^9.3.0",
|
||||
|
||||
@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { node, string } from 'prop-types';
|
||||
import { t } from '@lingui/macro';
|
||||
import styled from 'styled-components';
|
||||
import { formatDateString } from 'util/dates';
|
||||
import _Detail from './Detail';
|
||||
|
||||
const Detail = styled(_Detail)`
|
||||
@ -10,14 +9,8 @@ const Detail = styled(_Detail)`
|
||||
`;
|
||||
|
||||
function NumberSinceDetail({ label, number, date, dataCy = null }) {
|
||||
const dateStr = formatDateString(date);
|
||||
|
||||
return (
|
||||
<Detail
|
||||
label={label}
|
||||
dataCy={dataCy}
|
||||
value={t`${number} since ${dateStr}`}
|
||||
/>
|
||||
<Detail label={label} dataCy={dataCy} value={t`${number} since ${date}`} />
|
||||
);
|
||||
}
|
||||
NumberSinceDetail.propTypes = {
|
||||
|
||||
@ -85,7 +85,7 @@ describe('<ScheduleAdd />', () => {
|
||||
interval: 1,
|
||||
name: 'Run once schedule',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:00:00',
|
||||
startTime: '10:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -108,7 +108,7 @@ describe('<ScheduleAdd />', () => {
|
||||
name: 'Run every 10 minutes 10 times',
|
||||
occurrences: 10,
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:30:00',
|
||||
startTime: '10:30 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -127,12 +127,12 @@ describe('<ScheduleAdd />', () => {
|
||||
description: 'test description',
|
||||
end: 'onDate',
|
||||
endDate: '2020-03-26',
|
||||
endTime: '10:45:00',
|
||||
endTime: '10:45 AM',
|
||||
frequency: 'hour',
|
||||
interval: 1,
|
||||
name: 'Run every hour until date',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:45:00',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -154,7 +154,7 @@ describe('<ScheduleAdd />', () => {
|
||||
interval: 1,
|
||||
name: 'Run daily',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:45:00',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -178,7 +178,7 @@ describe('<ScheduleAdd />', () => {
|
||||
name: 'Run weekly on mon/wed/fri',
|
||||
occurrences: 1,
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:45:00',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -201,7 +201,7 @@ describe('<ScheduleAdd />', () => {
|
||||
occurrences: 1,
|
||||
runOn: 'day',
|
||||
runOnDayNumber: 1,
|
||||
startTime: '10:45',
|
||||
startTime: '10:45 AM',
|
||||
startDate: '2020-04-01',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
@ -221,7 +221,7 @@ describe('<ScheduleAdd />', () => {
|
||||
description: 'test description',
|
||||
end: 'never',
|
||||
endDate: '2020-03-26',
|
||||
endTime: '11:00:00',
|
||||
endTime: '11:00 AM',
|
||||
frequency: 'month',
|
||||
interval: 1,
|
||||
name: 'Run monthly on the last Tuesday',
|
||||
@ -230,7 +230,7 @@ describe('<ScheduleAdd />', () => {
|
||||
runOnTheDay: 'tuesday',
|
||||
runOnTheOccurrence: -1,
|
||||
startDate: '2020-03-31',
|
||||
startTime: '11:00',
|
||||
startTime: '11:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -255,7 +255,7 @@ describe('<ScheduleAdd />', () => {
|
||||
runOnDayMonth: 3,
|
||||
runOnDayNumber: 1,
|
||||
startDate: '2020-03-01',
|
||||
startTime: '00:00',
|
||||
startTime: '12:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -282,7 +282,7 @@ describe('<ScheduleAdd />', () => {
|
||||
runOnTheDay: 'friday',
|
||||
runOnTheMonth: 4,
|
||||
startDate: '2020-04-10',
|
||||
startTime: '11:15',
|
||||
startTime: '11:15 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -309,7 +309,7 @@ describe('<ScheduleAdd />', () => {
|
||||
runOnTheDay: 'weekday',
|
||||
runOnTheMonth: 10,
|
||||
startDate: '2020-04-10',
|
||||
startTime: '11:15',
|
||||
startTime: '11:15 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -378,7 +378,7 @@ describe('<ScheduleAdd />', () => {
|
||||
name: 'Schedule',
|
||||
end: 'never',
|
||||
endDate: '2021-01-29',
|
||||
endTime: '14:15:00',
|
||||
endTime: '2:15 PM',
|
||||
frequency: 'none',
|
||||
occurrences: 1,
|
||||
runOn: 'day',
|
||||
@ -394,7 +394,7 @@ describe('<ScheduleAdd />', () => {
|
||||
{ name: 'cred 2', id: 20 },
|
||||
],
|
||||
startDate: '2021-01-28',
|
||||
startTime: '14:15:00',
|
||||
startTime: '2:15 PM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -467,7 +467,7 @@ describe('<ScheduleAdd />', () => {
|
||||
interval: 1,
|
||||
name: 'Run once schedule',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:00:00',
|
||||
startTime: '10:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
|
||||
@ -250,15 +250,21 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
|
||||
<DetailList gutter="sm">
|
||||
<Detail label={t`Name`} value={name} />
|
||||
<Detail label={t`Description`} value={description} />
|
||||
<Detail label={t`First Run`} value={formatDateString(dtstart)} />
|
||||
<Detail label={t`Next Run`} value={formatDateString(next_run)} />
|
||||
<Detail label={t`Last Run`} value={formatDateString(dtend)} />
|
||||
<Detail
|
||||
label={t`First Run`}
|
||||
value={formatDateString(dtstart, timezone)}
|
||||
/>
|
||||
<Detail
|
||||
label={t`Next Run`}
|
||||
value={formatDateString(next_run, timezone)}
|
||||
/>
|
||||
<Detail label={t`Last Run`} value={formatDateString(dtend, timezone)} />
|
||||
<Detail label={t`Local Time Zone`} value={timezone} />
|
||||
<Detail label={t`Repeat Frequency`} value={repeatFrequency} />
|
||||
{hasDaysToKeepField ? (
|
||||
<Detail label={t`Days of Data to Keep`} value={daysToKeep} />
|
||||
) : null}
|
||||
<ScheduleOccurrences preview={preview} />
|
||||
<ScheduleOccurrences preview={preview} tz={timezone} />
|
||||
<UserDateDetail
|
||||
label={t`Created`}
|
||||
date={created}
|
||||
|
||||
@ -13,10 +13,6 @@ import ScheduleEdit from './ScheduleEdit';
|
||||
jest.mock('../../../api');
|
||||
|
||||
let wrapper;
|
||||
const now = new Date();
|
||||
const closestQuarterHour = new Date(Math.ceil(now.getTime() / 900000) * 900000);
|
||||
const tomorrow = new Date(closestQuarterHour);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const mockSchedule = {
|
||||
rrule:
|
||||
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
||||
@ -204,7 +200,7 @@ describe('<ScheduleEdit />', () => {
|
||||
interval: 1,
|
||||
name: 'Run once schedule',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:00:00',
|
||||
startTime: '10:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -228,7 +224,7 @@ describe('<ScheduleEdit />', () => {
|
||||
name: 'Run every 10 minutes 10 times',
|
||||
occurrences: 10,
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:30:00',
|
||||
startTime: '10:30 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -248,12 +244,12 @@ describe('<ScheduleEdit />', () => {
|
||||
description: 'test description',
|
||||
end: 'onDate',
|
||||
endDate: '2020-03-26',
|
||||
endTime: '10:45:00',
|
||||
endTime: '10:45 AM',
|
||||
frequency: 'hour',
|
||||
interval: 1,
|
||||
name: 'Run every hour until date',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:45:00',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -276,7 +272,7 @@ describe('<ScheduleEdit />', () => {
|
||||
interval: 1,
|
||||
name: 'Run daily',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:45:00',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -299,7 +295,7 @@ describe('<ScheduleEdit />', () => {
|
||||
name: 'Run weekly on mon/wed/fri',
|
||||
occurrences: 1,
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:45:00',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -324,7 +320,7 @@ describe('<ScheduleEdit />', () => {
|
||||
runOn: 'day',
|
||||
runOnDayNumber: 1,
|
||||
startDate: '2020-04-01',
|
||||
startTime: '10:45',
|
||||
startTime: '10:45 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -352,7 +348,7 @@ describe('<ScheduleEdit />', () => {
|
||||
runOnTheDay: 'tuesday',
|
||||
runOnTheOccurrence: -1,
|
||||
startDate: '2020-03-31',
|
||||
startTime: '11:00',
|
||||
startTime: '11:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
@ -380,7 +376,7 @@ describe('<ScheduleEdit />', () => {
|
||||
runOn: 'day',
|
||||
runOnDayMonth: 3,
|
||||
runOnDayNumber: 1,
|
||||
startTime: '00:00',
|
||||
startTime: '12:00 AM',
|
||||
startDate: '2020-03-01',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
@ -408,7 +404,7 @@ describe('<ScheduleEdit />', () => {
|
||||
runOnTheOccurrence: 2,
|
||||
runOnTheDay: 'friday',
|
||||
runOnTheMonth: 4,
|
||||
startTime: '11:15',
|
||||
startTime: '11:15 AM',
|
||||
startDate: '2020-04-10',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
@ -437,7 +433,7 @@ describe('<ScheduleEdit />', () => {
|
||||
runOnTheOccurrence: 1,
|
||||
runOnTheDay: 'weekday',
|
||||
runOnTheMonth: 10,
|
||||
startTime: '11:15',
|
||||
startTime: '11:15 AM',
|
||||
startDate: '2020-04-10',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
@ -528,7 +524,7 @@ describe('<ScheduleEdit />', () => {
|
||||
name: mockSchedule.name,
|
||||
end: 'never',
|
||||
endDate: '2021-01-29',
|
||||
endTime: '14:15:00',
|
||||
endTime: '2:15 PM',
|
||||
frequency: 'none',
|
||||
occurrences: 1,
|
||||
runOn: 'day',
|
||||
@ -539,7 +535,7 @@ describe('<ScheduleEdit />', () => {
|
||||
runOnTheOccurrence: 1,
|
||||
skip_tags: '',
|
||||
startDate: '2021-01-28',
|
||||
startTime: '14:15:00',
|
||||
startTime: '2:15 PM',
|
||||
timezone: 'America/New_York',
|
||||
credentials: [
|
||||
{ id: 3, name: 'Credential 3', kind: 'ssh', url: '' },
|
||||
@ -630,7 +626,7 @@ describe('<ScheduleEdit />', () => {
|
||||
name: 'foo',
|
||||
inventory: 702,
|
||||
rrule:
|
||||
'DTSTART;TZID=America/New_York:20200402T184500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
||||
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
||||
});
|
||||
});
|
||||
|
||||
@ -732,7 +728,7 @@ describe('<ScheduleEdit />', () => {
|
||||
interval: 1,
|
||||
name: 'Run once schedule',
|
||||
startDate: '2020-03-25',
|
||||
startTime: '10:00:00',
|
||||
startTime: '10:00 AM',
|
||||
timezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
|
||||
@ -103,7 +103,7 @@ function ScheduleListItem({
|
||||
<DetailList stacked>
|
||||
<Detail
|
||||
label={t`Next Run`}
|
||||
value={formatDateString(schedule.next_run)}
|
||||
value={formatDateString(schedule.next_run, schedule.timezone)}
|
||||
/>
|
||||
</DetailList>
|
||||
)}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import 'styled-components/macro';
|
||||
import React, { useState } from 'react';
|
||||
import { shape } from 'prop-types';
|
||||
import { shape, string } from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { Split, SplitItem, TextListItemVariants } from '@patternfly/react-core';
|
||||
import { formatDateString, formatDateStringUTC } from 'util/dates';
|
||||
import { formatDateString } from 'util/dates';
|
||||
import { DetailName, DetailValue } from '../../DetailList';
|
||||
import MultiButtonToggle from '../../MultiButtonToggle';
|
||||
|
||||
@ -22,7 +22,7 @@ const OccurrencesLabel = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
function ScheduleOccurrences({ preview = { local: [], utc: [] } }) {
|
||||
function ScheduleOccurrences({ preview = { local: [], utc: [] }, tz }) {
|
||||
const [mode, setMode] = useState('local');
|
||||
|
||||
if (preview.local.length < 2) {
|
||||
@ -64,8 +64,8 @@ function ScheduleOccurrences({ preview = { local: [], utc: [] } }) {
|
||||
{preview[mode].map((dateStr) => (
|
||||
<div key={dateStr}>
|
||||
{mode === 'local'
|
||||
? formatDateString(dateStr)
|
||||
: formatDateStringUTC(dateStr)}
|
||||
? formatDateString(dateStr, tz)
|
||||
: formatDateString(dateStr, 'UTC')}
|
||||
</div>
|
||||
))}
|
||||
</DetailValue>
|
||||
@ -75,10 +75,12 @@ function ScheduleOccurrences({ preview = { local: [], utc: [] } }) {
|
||||
|
||||
ScheduleOccurrences.propTypes = {
|
||||
preview: shape(),
|
||||
tz: string,
|
||||
};
|
||||
|
||||
ScheduleOccurrences.defaultProps = {
|
||||
preview: { local: [], utc: [] },
|
||||
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
};
|
||||
|
||||
export default ScheduleOccurrences;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { shape, func } from 'prop-types';
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Formik, useField } from 'formik';
|
||||
import { RRule } from 'rrule';
|
||||
@ -190,13 +191,11 @@ function ScheduleForm({
|
||||
const [isSaveDisabled, setIsSaveDisabled] = useState(false);
|
||||
|
||||
let rruleError;
|
||||
const now = new Date();
|
||||
const closestQuarterHour = new Date(
|
||||
Math.ceil(now.getTime() / 900000) * 900000
|
||||
const now = DateTime.now();
|
||||
const closestQuarterHour = DateTime.fromMillis(
|
||||
Math.ceil(now.ts / 900000) * 900000
|
||||
);
|
||||
const tomorrow = new Date(closestQuarterHour);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
const tomorrow = closestQuarterHour.plus({ days: 1 });
|
||||
const isTemplate =
|
||||
resource.type === 'workflow_job_template' ||
|
||||
resource.type === 'job_template';
|
||||
@ -376,9 +375,9 @@ function ScheduleForm({
|
||||
) {
|
||||
showPromptButton = true;
|
||||
}
|
||||
const [currentDate, time] = dateToInputDateTime(closestQuarterHour);
|
||||
const [currentDate, time] = dateToInputDateTime(closestQuarterHour.toISO());
|
||||
|
||||
const [tomorrowDate] = dateToInputDateTime(tomorrow);
|
||||
const [tomorrowDate] = dateToInputDateTime(tomorrow.toISO());
|
||||
const initialValues = {
|
||||
daysOfWeek: [],
|
||||
description: schedule.description || '',
|
||||
@ -448,7 +447,10 @@ function ScheduleForm({
|
||||
} = RRule.fromString(schedule.rrule.replace(' ', '\n'));
|
||||
|
||||
if (dtstart) {
|
||||
const [startDate, startTime] = dateToInputDateTime(schedule.dtstart);
|
||||
const [startDate, startTime] = dateToInputDateTime(
|
||||
schedule.dtstart,
|
||||
schedule.timezone
|
||||
);
|
||||
|
||||
overriddenValues.startDate = startDate;
|
||||
overriddenValues.startTime = startTime;
|
||||
@ -457,7 +459,10 @@ function ScheduleForm({
|
||||
if (schedule.until) {
|
||||
overriddenValues.end = 'onDate';
|
||||
|
||||
const [endDate, endTime] = dateToInputDateTime(schedule.until);
|
||||
const [endDate, endTime] = dateToInputDateTime(
|
||||
schedule.until,
|
||||
schedule.timezone
|
||||
);
|
||||
|
||||
overriddenValues.endDate = endDate;
|
||||
overriddenValues.endTime = endTime;
|
||||
@ -550,7 +555,10 @@ function ScheduleForm({
|
||||
startDate,
|
||||
} = values;
|
||||
|
||||
if (end === 'onDate' && new Date(startDate) >= new Date(endDate)) {
|
||||
if (
|
||||
end === 'onDate' &&
|
||||
DateTime.fromISO(startDate) >= DateTime.fromISO(endDate)
|
||||
) {
|
||||
errors.endDate = t`Please select an end date/time that comes after the start date/time.`;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { dateToInputDateTime } from 'util/dates';
|
||||
import { SchedulesAPI, JobTemplatesAPI, InventoriesAPI } from 'api';
|
||||
@ -105,7 +106,7 @@ const nonRRuleValuesMatch = () => {
|
||||
wrapper.find('DatePicker[aria-label="Start date"]').prop('value')
|
||||
).toBe('2020-04-02');
|
||||
expect(wrapper.find('TimePicker[aria-label="Start time"]').prop('time')).toBe(
|
||||
'6:45 PM'
|
||||
'2:45 PM'
|
||||
);
|
||||
expect(wrapper.find('select#schedule-timezone').prop('value')).toBe(
|
||||
'America/New_York'
|
||||
@ -474,11 +475,11 @@ describe('<ScheduleForm />', () => {
|
||||
});
|
||||
|
||||
test('initially renders expected fields and values', () => {
|
||||
const now = new Date();
|
||||
const closestQuarterHour = new Date(
|
||||
Math.ceil(now.getTime() / 900000) * 900000
|
||||
const now = DateTime.now();
|
||||
const closestQuarterHour = DateTime.fromMillis(
|
||||
Math.ceil(now.ts / 900000) * 900000
|
||||
);
|
||||
const [date, time] = dateToInputDateTime(closestQuarterHour);
|
||||
const [date, time] = dateToInputDateTime(closestQuarterHour.toISO());
|
||||
expect(wrapper.find('ScheduleForm').length).toBe(1);
|
||||
defaultFieldsVisible();
|
||||
expect(wrapper.find('FormGroup[label="Run every"]').length).toBe(0);
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { RRule } from 'rrule';
|
||||
import { DateTime } from 'luxon';
|
||||
import { getRRuleDayConstants } from 'util/dates';
|
||||
|
||||
const parseTime = (time) => {
|
||||
const [hour, minute, ampm] = time.split(/[: ]/);
|
||||
const timeHour =
|
||||
ampm === 'PM' && hour !== '12' ? `${parseInt(hour, 10) + 12}` : `${hour}`;
|
||||
|
||||
return [timeHour, minute];
|
||||
};
|
||||
const parseTime = (time) => [
|
||||
DateTime.fromFormat(time, 'h:mm a').hour,
|
||||
DateTime.fromFormat(time, 'h:mm a').minute,
|
||||
];
|
||||
|
||||
export default function buildRuleObj(values) {
|
||||
// Dates are formatted like "YYYY-MM-DD"
|
||||
|
||||
@ -12,11 +12,7 @@ import RoutedTabs from 'components/RoutedTabs';
|
||||
import { CardBody, CardActionsRow } from 'components/Card';
|
||||
import { DetailList, Detail, NumberSinceDetail } from 'components/DetailList';
|
||||
import { useConfig } from 'contexts/Config';
|
||||
import {
|
||||
formatDateString,
|
||||
formatDateStringUTC,
|
||||
secondsToDays,
|
||||
} from 'util/dates';
|
||||
import { formatDateString, secondsToDays } from 'util/dates';
|
||||
|
||||
function SubscriptionDetail() {
|
||||
const { me = {}, license_info, version } = useConfig();
|
||||
@ -98,8 +94,9 @@ function SubscriptionDetail() {
|
||||
label={t`Expires on UTC`}
|
||||
value={
|
||||
license_info.license_date &&
|
||||
formatDateStringUTC(
|
||||
new Date(license_info.license_date * 1000).toISOString()
|
||||
formatDateString(
|
||||
new Date(license_info.license_date * 1000).toISOString(),
|
||||
'UTC'
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
@ -20,7 +20,7 @@ import {
|
||||
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
|
||||
|
||||
import { ConfigAPI } from 'api';
|
||||
import { formatDateStringUTC } from 'util/dates';
|
||||
import { formatDateString } from 'util/dates';
|
||||
import useRequest from 'hooks/useRequest';
|
||||
import useSelected from 'hooks/useSelected';
|
||||
import ErrorDetail from 'components/ErrorDetail';
|
||||
@ -168,8 +168,9 @@ function SubscriptionModal({
|
||||
{subscription.instance_count}
|
||||
</Td>
|
||||
<Td dataLabel={t`Expires`} modifier="nowrap">
|
||||
{formatDateStringUTC(
|
||||
new Date(subscription.license_date * 1000).toISOString()
|
||||
{formatDateString(
|
||||
new Date(subscription.license_date * 1000).toISOString(),
|
||||
'UTC'
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
|
||||
@ -1,62 +1,43 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { t } from '@lingui/macro';
|
||||
import { RRule } from 'rrule';
|
||||
import { getLanguage } from './language';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
|
||||
const prependZeros = (value) => value.toString().padStart(2, 0);
|
||||
|
||||
export function formatDateString(dateString, lang = getLanguage(navigator)) {
|
||||
if (dateString === null) {
|
||||
export function formatDateString(dateObj, tz = null) {
|
||||
if (dateObj === null) {
|
||||
return null;
|
||||
}
|
||||
return new Date(dateString).toLocaleString(lang);
|
||||
}
|
||||
|
||||
export function formatDateStringUTC(dateString, lang = getLanguage(navigator)) {
|
||||
if (dateString === null) {
|
||||
return null;
|
||||
}
|
||||
return new Date(dateString).toLocaleString(lang, { timeZone: 'UTC' });
|
||||
return tz !== null
|
||||
? DateTime.fromISO(dateObj, { zone: tz }).toLocaleString(
|
||||
DateTime.DATETIME_SHORT_WITH_SECONDS
|
||||
)
|
||||
: DateTime.fromISO(dateObj).toLocaleString(
|
||||
DateTime.DATETIME_SHORT_WITH_SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
export function secondsToHHMMSS(seconds) {
|
||||
return new Date(seconds * 1000).toISOString().substr(11, 8);
|
||||
return Duration.fromObject({ seconds }).toFormat('hh:mm:ss');
|
||||
}
|
||||
|
||||
export function secondsToDays(seconds) {
|
||||
let duration = Math.floor(parseInt(seconds, 10) / 86400);
|
||||
if (duration < 0) {
|
||||
duration = 0;
|
||||
}
|
||||
return duration.toString();
|
||||
return Duration.fromObject({ seconds }).toFormat('d');
|
||||
}
|
||||
|
||||
export function timeOfDay() {
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minute = prependZeros(date.getMinutes());
|
||||
const second = prependZeros(date.getSeconds());
|
||||
const time =
|
||||
hour > 12
|
||||
? `${hour - 12}:${minute}:${second} PM`
|
||||
: `${hour}:${minute}:${second} AM`;
|
||||
return time;
|
||||
const dateTime = DateTime.local();
|
||||
return dateTime.toFormat('hh:mm a');
|
||||
}
|
||||
|
||||
export function dateToInputDateTime(dateObj) {
|
||||
let date = dateObj;
|
||||
if (typeof dateObj === 'string') {
|
||||
date = new Date(dateObj);
|
||||
export function dateToInputDateTime(dt, tz = null) {
|
||||
let dateTime;
|
||||
if (tz) {
|
||||
dateTime = DateTime.fromISO(dt, { zone: tz });
|
||||
} else {
|
||||
dateTime = DateTime.fromISO(dt);
|
||||
}
|
||||
const year = date.getFullYear();
|
||||
const month = prependZeros(date.getMonth() + 1);
|
||||
const day = prependZeros(date.getDate());
|
||||
const hour =
|
||||
date.getHours() > 12 ? parseInt(date.getHours(), 10) - 12 : date.getHours();
|
||||
const minute = prependZeros(date.getMinutes());
|
||||
const amPmText = date.getHours() > 11 ? 'PM' : 'AM';
|
||||
|
||||
return [`${year}-${month}-${day}`, `${hour}:${minute} ${amPmText}`];
|
||||
return [dateTime.toFormat('yyyy-LL-dd'), dateTime.toFormat('h:mm a')];
|
||||
}
|
||||
|
||||
export function getRRuleDayConstants(dayString) {
|
||||
|
||||
@ -2,7 +2,6 @@ import { RRule } from 'rrule';
|
||||
import {
|
||||
dateToInputDateTime,
|
||||
formatDateString,
|
||||
formatDateStringUTC,
|
||||
getRRuleDayConstants,
|
||||
secondsToDays,
|
||||
secondsToHHMMSS,
|
||||
@ -21,35 +20,20 @@ const i18n = {
|
||||
|
||||
describe('formatDateString', () => {
|
||||
test('it returns the expected value', () => {
|
||||
const lang = 'en-US';
|
||||
expect(formatDateString(null, lang)).toEqual(null);
|
||||
expect(formatDateString('', lang)).toEqual('Invalid Date');
|
||||
expect(formatDateString({}, lang)).toEqual('Invalid Date');
|
||||
expect(formatDateString(undefined, lang)).toEqual('Invalid Date');
|
||||
expect(formatDateString('foobar', lang)).toEqual('Invalid Date');
|
||||
expect(formatDateString('2018-011-31T01:14:52.969227Z', lang)).toEqual(
|
||||
'Invalid Date'
|
||||
expect(formatDateString(null)).toEqual(null);
|
||||
expect(formatDateString('')).toEqual('Invalid DateTime');
|
||||
expect(formatDateString({})).toEqual('Invalid DateTime');
|
||||
expect(formatDateString(undefined)).toEqual('Invalid DateTime');
|
||||
expect(formatDateString('foobar')).toEqual('Invalid DateTime');
|
||||
expect(formatDateString('2018-011-31T01:14:52.969227Z', undefined)).toEqual(
|
||||
'Invalid DateTime'
|
||||
);
|
||||
expect(formatDateString('2018-01-31T01:14:52.969227Z', lang)).toEqual(
|
||||
'1/31/2018, 1:14:52 AM'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatDateStringUTC', () => {
|
||||
test('it returns the expected value', () => {
|
||||
const lang = 'en-US';
|
||||
expect(formatDateStringUTC(null, lang)).toEqual(null);
|
||||
expect(formatDateStringUTC('', lang)).toEqual('Invalid Date');
|
||||
expect(formatDateStringUTC({}, lang)).toEqual('Invalid Date');
|
||||
expect(formatDateStringUTC(undefined, lang)).toEqual('Invalid Date');
|
||||
expect(formatDateStringUTC('foobar', lang)).toEqual('Invalid Date');
|
||||
expect(formatDateStringUTC('2018-011-31T01:14:52.969227Z', lang)).toEqual(
|
||||
'Invalid Date'
|
||||
);
|
||||
expect(formatDateStringUTC('2018-01-31T01:14:52.969227Z', lang)).toEqual(
|
||||
expect(formatDateString('2018-01-31T01:14:52.969227Z')).toEqual(
|
||||
'1/31/2018, 1:14:52 AM'
|
||||
);
|
||||
expect(
|
||||
formatDateString('2018-01-31T01:14:52.969227Z', 'America/Los_Angeles')
|
||||
).toEqual('1/30/2018, 5:14:52 PM');
|
||||
});
|
||||
});
|
||||
|
||||
@ -68,9 +52,10 @@ describe('secondsToHHMMSS', () => {
|
||||
|
||||
describe('dateToInputDateTime', () => {
|
||||
test('it returns the expected value', () => {
|
||||
expect(
|
||||
dateToInputDateTime(new Date('2018-01-31T01:14:52.969227Z'))
|
||||
).toEqual(['2018-01-31', '1:14 AM']);
|
||||
expect(dateToInputDateTime('2018-01-31T01:14:52.969227Z')).toEqual([
|
||||
'2018-01-31',
|
||||
'1:14 AM',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user