Convert dates to use luxon.js

This commit is contained in:
Kia Lam
2021-07-22 12:12:35 -04:00
parent f6104dd438
commit 304ec80d80
15 changed files with 148 additions and 163 deletions

View File

@@ -21,6 +21,7 @@
"has-ansi": "4.0.0", "has-ansi": "4.0.0",
"html-entities": "2.3.2", "html-entities": "2.3.2",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"luxon": "^2.0.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.13.1", "react": "^16.13.1",
"react-ace": "^9.3.0", "react-ace": "^9.3.0",
@@ -14601,10 +14602,9 @@
} }
}, },
"node_modules/luxon": { "node_modules/luxon": {
"version": "1.26.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", "resolved": "https://registry.npmjs.org/luxon/-/luxon-2.0.1.tgz",
"integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==", "integrity": "sha512-8Eawf81c9ZlQj62W3eq4mp+C7SAIAnmaS7ZuEAiX503YMcn+0C1JnMQRtfaQj6B5qTZLgHv0F4H5WabBCvi1fw==",
"optional": true,
"engines": { "engines": {
"node": "*" "node": "*"
} }
@@ -21000,6 +21000,15 @@
"luxon": "^1.21.3" "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": { "node_modules/rst-selector-parser": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
@@ -38379,10 +38388,9 @@
} }
}, },
"luxon": { "luxon": {
"version": "1.26.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", "resolved": "https://registry.npmjs.org/luxon/-/luxon-2.0.1.tgz",
"integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==", "integrity": "sha512-8Eawf81c9ZlQj62W3eq4mp+C7SAIAnmaS7ZuEAiX503YMcn+0C1JnMQRtfaQj6B5qTZLgHv0F4H5WabBCvi1fw=="
"optional": true
}, },
"magic-string": { "magic-string": {
"version": "0.25.7", "version": "0.25.7",
@@ -43724,6 +43732,14 @@
"requires": { "requires": {
"luxon": "^1.21.3", "luxon": "^1.21.3",
"tslib": "^1.10.0" "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": { "rst-selector-parser": {

View File

@@ -21,6 +21,7 @@
"has-ansi": "4.0.0", "has-ansi": "4.0.0",
"html-entities": "2.3.2", "html-entities": "2.3.2",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"luxon": "^2.0.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.13.1", "react": "^16.13.1",
"react-ace": "^9.3.0", "react-ace": "^9.3.0",

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { node, string } from 'prop-types'; import { node, string } from 'prop-types';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import styled from 'styled-components'; import styled from 'styled-components';
import { formatDateString } from 'util/dates';
import _Detail from './Detail'; import _Detail from './Detail';
const Detail = styled(_Detail)` const Detail = styled(_Detail)`
@@ -10,14 +9,8 @@ const Detail = styled(_Detail)`
`; `;
function NumberSinceDetail({ label, number, date, dataCy = null }) { function NumberSinceDetail({ label, number, date, dataCy = null }) {
const dateStr = formatDateString(date);
return ( return (
<Detail <Detail label={label} dataCy={dataCy} value={t`${number} since ${date}`} />
label={label}
dataCy={dataCy}
value={t`${number} since ${dateStr}`}
/>
); );
} }
NumberSinceDetail.propTypes = { NumberSinceDetail.propTypes = {

View File

@@ -85,7 +85,7 @@ describe('<ScheduleAdd />', () => {
interval: 1, interval: 1,
name: 'Run once schedule', name: 'Run once schedule',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:00:00', startTime: '10:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -108,7 +108,7 @@ describe('<ScheduleAdd />', () => {
name: 'Run every 10 minutes 10 times', name: 'Run every 10 minutes 10 times',
occurrences: 10, occurrences: 10,
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:30:00', startTime: '10:30 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -127,12 +127,12 @@ describe('<ScheduleAdd />', () => {
description: 'test description', description: 'test description',
end: 'onDate', end: 'onDate',
endDate: '2020-03-26', endDate: '2020-03-26',
endTime: '10:45:00', endTime: '10:45 AM',
frequency: 'hour', frequency: 'hour',
interval: 1, interval: 1,
name: 'Run every hour until date', name: 'Run every hour until date',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:45:00', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -154,7 +154,7 @@ describe('<ScheduleAdd />', () => {
interval: 1, interval: 1,
name: 'Run daily', name: 'Run daily',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:45:00', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -178,7 +178,7 @@ describe('<ScheduleAdd />', () => {
name: 'Run weekly on mon/wed/fri', name: 'Run weekly on mon/wed/fri',
occurrences: 1, occurrences: 1,
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:45:00', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -201,7 +201,7 @@ describe('<ScheduleAdd />', () => {
occurrences: 1, occurrences: 1,
runOn: 'day', runOn: 'day',
runOnDayNumber: 1, runOnDayNumber: 1,
startTime: '10:45', startTime: '10:45 AM',
startDate: '2020-04-01', startDate: '2020-04-01',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
@@ -221,7 +221,7 @@ describe('<ScheduleAdd />', () => {
description: 'test description', description: 'test description',
end: 'never', end: 'never',
endDate: '2020-03-26', endDate: '2020-03-26',
endTime: '11:00:00', endTime: '11:00 AM',
frequency: 'month', frequency: 'month',
interval: 1, interval: 1,
name: 'Run monthly on the last Tuesday', name: 'Run monthly on the last Tuesday',
@@ -230,7 +230,7 @@ describe('<ScheduleAdd />', () => {
runOnTheDay: 'tuesday', runOnTheDay: 'tuesday',
runOnTheOccurrence: -1, runOnTheOccurrence: -1,
startDate: '2020-03-31', startDate: '2020-03-31',
startTime: '11:00', startTime: '11:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -255,7 +255,7 @@ describe('<ScheduleAdd />', () => {
runOnDayMonth: 3, runOnDayMonth: 3,
runOnDayNumber: 1, runOnDayNumber: 1,
startDate: '2020-03-01', startDate: '2020-03-01',
startTime: '00:00', startTime: '12:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -282,7 +282,7 @@ describe('<ScheduleAdd />', () => {
runOnTheDay: 'friday', runOnTheDay: 'friday',
runOnTheMonth: 4, runOnTheMonth: 4,
startDate: '2020-04-10', startDate: '2020-04-10',
startTime: '11:15', startTime: '11:15 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -309,7 +309,7 @@ describe('<ScheduleAdd />', () => {
runOnTheDay: 'weekday', runOnTheDay: 'weekday',
runOnTheMonth: 10, runOnTheMonth: 10,
startDate: '2020-04-10', startDate: '2020-04-10',
startTime: '11:15', startTime: '11:15 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -378,7 +378,7 @@ describe('<ScheduleAdd />', () => {
name: 'Schedule', name: 'Schedule',
end: 'never', end: 'never',
endDate: '2021-01-29', endDate: '2021-01-29',
endTime: '14:15:00', endTime: '2:15 PM',
frequency: 'none', frequency: 'none',
occurrences: 1, occurrences: 1,
runOn: 'day', runOn: 'day',
@@ -394,7 +394,7 @@ describe('<ScheduleAdd />', () => {
{ name: 'cred 2', id: 20 }, { name: 'cred 2', id: 20 },
], ],
startDate: '2021-01-28', startDate: '2021-01-28',
startTime: '14:15:00', startTime: '2:15 PM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -467,7 +467,7 @@ describe('<ScheduleAdd />', () => {
interval: 1, interval: 1,
name: 'Run once schedule', name: 'Run once schedule',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:00:00', startTime: '10:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });

View File

@@ -250,15 +250,21 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
<DetailList gutter="sm"> <DetailList gutter="sm">
<Detail label={t`Name`} value={name} /> <Detail label={t`Name`} value={name} />
<Detail label={t`Description`} value={description} /> <Detail label={t`Description`} value={description} />
<Detail label={t`First Run`} value={formatDateString(dtstart)} /> <Detail
<Detail label={t`Next Run`} value={formatDateString(next_run)} /> label={t`First Run`}
<Detail label={t`Last Run`} value={formatDateString(dtend)} /> 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`Local Time Zone`} value={timezone} />
<Detail label={t`Repeat Frequency`} value={repeatFrequency} /> <Detail label={t`Repeat Frequency`} value={repeatFrequency} />
{hasDaysToKeepField ? ( {hasDaysToKeepField ? (
<Detail label={t`Days of Data to Keep`} value={daysToKeep} /> <Detail label={t`Days of Data to Keep`} value={daysToKeep} />
) : null} ) : null}
<ScheduleOccurrences preview={preview} /> <ScheduleOccurrences preview={preview} tz={timezone} />
<UserDateDetail <UserDateDetail
label={t`Created`} label={t`Created`}
date={created} date={created}

View File

@@ -13,10 +13,6 @@ import ScheduleEdit from './ScheduleEdit';
jest.mock('../../../api'); jest.mock('../../../api');
let wrapper; 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 = { const mockSchedule = {
rrule: rrule:
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY', 'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
@@ -204,7 +200,7 @@ describe('<ScheduleEdit />', () => {
interval: 1, interval: 1,
name: 'Run once schedule', name: 'Run once schedule',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:00:00', startTime: '10:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -228,7 +224,7 @@ describe('<ScheduleEdit />', () => {
name: 'Run every 10 minutes 10 times', name: 'Run every 10 minutes 10 times',
occurrences: 10, occurrences: 10,
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:30:00', startTime: '10:30 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -248,12 +244,12 @@ describe('<ScheduleEdit />', () => {
description: 'test description', description: 'test description',
end: 'onDate', end: 'onDate',
endDate: '2020-03-26', endDate: '2020-03-26',
endTime: '10:45:00', endTime: '10:45 AM',
frequency: 'hour', frequency: 'hour',
interval: 1, interval: 1,
name: 'Run every hour until date', name: 'Run every hour until date',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:45:00', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -276,7 +272,7 @@ describe('<ScheduleEdit />', () => {
interval: 1, interval: 1,
name: 'Run daily', name: 'Run daily',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:45:00', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -299,7 +295,7 @@ describe('<ScheduleEdit />', () => {
name: 'Run weekly on mon/wed/fri', name: 'Run weekly on mon/wed/fri',
occurrences: 1, occurrences: 1,
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:45:00', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -324,7 +320,7 @@ describe('<ScheduleEdit />', () => {
runOn: 'day', runOn: 'day',
runOnDayNumber: 1, runOnDayNumber: 1,
startDate: '2020-04-01', startDate: '2020-04-01',
startTime: '10:45', startTime: '10:45 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -352,7 +348,7 @@ describe('<ScheduleEdit />', () => {
runOnTheDay: 'tuesday', runOnTheDay: 'tuesday',
runOnTheOccurrence: -1, runOnTheOccurrence: -1,
startDate: '2020-03-31', startDate: '2020-03-31',
startTime: '11:00', startTime: '11:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });
@@ -380,7 +376,7 @@ describe('<ScheduleEdit />', () => {
runOn: 'day', runOn: 'day',
runOnDayMonth: 3, runOnDayMonth: 3,
runOnDayNumber: 1, runOnDayNumber: 1,
startTime: '00:00', startTime: '12:00 AM',
startDate: '2020-03-01', startDate: '2020-03-01',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
@@ -408,7 +404,7 @@ describe('<ScheduleEdit />', () => {
runOnTheOccurrence: 2, runOnTheOccurrence: 2,
runOnTheDay: 'friday', runOnTheDay: 'friday',
runOnTheMonth: 4, runOnTheMonth: 4,
startTime: '11:15', startTime: '11:15 AM',
startDate: '2020-04-10', startDate: '2020-04-10',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
@@ -437,7 +433,7 @@ describe('<ScheduleEdit />', () => {
runOnTheOccurrence: 1, runOnTheOccurrence: 1,
runOnTheDay: 'weekday', runOnTheDay: 'weekday',
runOnTheMonth: 10, runOnTheMonth: 10,
startTime: '11:15', startTime: '11:15 AM',
startDate: '2020-04-10', startDate: '2020-04-10',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
@@ -528,7 +524,7 @@ describe('<ScheduleEdit />', () => {
name: mockSchedule.name, name: mockSchedule.name,
end: 'never', end: 'never',
endDate: '2021-01-29', endDate: '2021-01-29',
endTime: '14:15:00', endTime: '2:15 PM',
frequency: 'none', frequency: 'none',
occurrences: 1, occurrences: 1,
runOn: 'day', runOn: 'day',
@@ -539,7 +535,7 @@ describe('<ScheduleEdit />', () => {
runOnTheOccurrence: 1, runOnTheOccurrence: 1,
skip_tags: '', skip_tags: '',
startDate: '2021-01-28', startDate: '2021-01-28',
startTime: '14:15:00', startTime: '2:15 PM',
timezone: 'America/New_York', timezone: 'America/New_York',
credentials: [ credentials: [
{ id: 3, name: 'Credential 3', kind: 'ssh', url: '' }, { id: 3, name: 'Credential 3', kind: 'ssh', url: '' },
@@ -630,7 +626,7 @@ describe('<ScheduleEdit />', () => {
name: 'foo', name: 'foo',
inventory: 702, inventory: 702,
rrule: 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, interval: 1,
name: 'Run once schedule', name: 'Run once schedule',
startDate: '2020-03-25', startDate: '2020-03-25',
startTime: '10:00:00', startTime: '10:00 AM',
timezone: 'America/New_York', timezone: 'America/New_York',
}); });
}); });

View File

@@ -103,7 +103,7 @@ function ScheduleListItem({
<DetailList stacked> <DetailList stacked>
<Detail <Detail
label={t`Next Run`} label={t`Next Run`}
value={formatDateString(schedule.next_run)} value={formatDateString(schedule.next_run, schedule.timezone)}
/> />
</DetailList> </DetailList>
)} )}

View File

@@ -1,11 +1,11 @@
import 'styled-components/macro'; import 'styled-components/macro';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { shape } from 'prop-types'; import { shape, string } from 'prop-types';
import styled from 'styled-components'; import styled from 'styled-components';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Split, SplitItem, TextListItemVariants } from '@patternfly/react-core'; 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 { DetailName, DetailValue } from '../../DetailList';
import MultiButtonToggle from '../../MultiButtonToggle'; 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'); const [mode, setMode] = useState('local');
if (preview.local.length < 2) { if (preview.local.length < 2) {
@@ -64,8 +64,8 @@ function ScheduleOccurrences({ preview = { local: [], utc: [] } }) {
{preview[mode].map((dateStr) => ( {preview[mode].map((dateStr) => (
<div key={dateStr}> <div key={dateStr}>
{mode === 'local' {mode === 'local'
? formatDateString(dateStr) ? formatDateString(dateStr, tz)
: formatDateStringUTC(dateStr)} : formatDateString(dateStr, 'UTC')}
</div> </div>
))} ))}
</DetailValue> </DetailValue>
@@ -75,10 +75,12 @@ function ScheduleOccurrences({ preview = { local: [], utc: [] } }) {
ScheduleOccurrences.propTypes = { ScheduleOccurrences.propTypes = {
preview: shape(), preview: shape(),
tz: string,
}; };
ScheduleOccurrences.defaultProps = { ScheduleOccurrences.defaultProps = {
preview: { local: [], utc: [] }, preview: { local: [], utc: [] },
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
}; };
export default ScheduleOccurrences; export default ScheduleOccurrences;

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useCallback, useState } from 'react'; import React, { useEffect, useCallback, useState } from 'react';
import { shape, func } from 'prop-types'; import { shape, func } from 'prop-types';
import { DateTime } from 'luxon';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Formik, useField } from 'formik'; import { Formik, useField } from 'formik';
import { RRule } from 'rrule'; import { RRule } from 'rrule';
@@ -190,13 +191,11 @@ function ScheduleForm({
const [isSaveDisabled, setIsSaveDisabled] = useState(false); const [isSaveDisabled, setIsSaveDisabled] = useState(false);
let rruleError; let rruleError;
const now = new Date(); const now = DateTime.now();
const closestQuarterHour = new Date( const closestQuarterHour = DateTime.fromMillis(
Math.ceil(now.getTime() / 900000) * 900000 Math.ceil(now.ts / 900000) * 900000
); );
const tomorrow = new Date(closestQuarterHour); const tomorrow = closestQuarterHour.plus({ days: 1 });
tomorrow.setDate(tomorrow.getDate() + 1);
const isTemplate = const isTemplate =
resource.type === 'workflow_job_template' || resource.type === 'workflow_job_template' ||
resource.type === 'job_template'; resource.type === 'job_template';
@@ -376,9 +375,9 @@ function ScheduleForm({
) { ) {
showPromptButton = true; showPromptButton = true;
} }
const [currentDate, time] = dateToInputDateTime(closestQuarterHour); const [currentDate, time] = dateToInputDateTime(closestQuarterHour.toISO());
const [tomorrowDate] = dateToInputDateTime(tomorrow); const [tomorrowDate] = dateToInputDateTime(tomorrow.toISO());
const initialValues = { const initialValues = {
daysOfWeek: [], daysOfWeek: [],
description: schedule.description || '', description: schedule.description || '',
@@ -448,7 +447,10 @@ function ScheduleForm({
} = RRule.fromString(schedule.rrule.replace(' ', '\n')); } = RRule.fromString(schedule.rrule.replace(' ', '\n'));
if (dtstart) { if (dtstart) {
const [startDate, startTime] = dateToInputDateTime(schedule.dtstart); const [startDate, startTime] = dateToInputDateTime(
schedule.dtstart,
schedule.timezone
);
overriddenValues.startDate = startDate; overriddenValues.startDate = startDate;
overriddenValues.startTime = startTime; overriddenValues.startTime = startTime;
@@ -457,7 +459,10 @@ function ScheduleForm({
if (schedule.until) { if (schedule.until) {
overriddenValues.end = 'onDate'; overriddenValues.end = 'onDate';
const [endDate, endTime] = dateToInputDateTime(schedule.until); const [endDate, endTime] = dateToInputDateTime(
schedule.until,
schedule.timezone
);
overriddenValues.endDate = endDate; overriddenValues.endDate = endDate;
overriddenValues.endTime = endTime; overriddenValues.endTime = endTime;
@@ -550,7 +555,10 @@ function ScheduleForm({
startDate, startDate,
} = values; } = 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.`; errors.endDate = t`Please select an end date/time that comes after the start date/time.`;
} }

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { DateTime } from 'luxon';
import { dateToInputDateTime } from 'util/dates'; import { dateToInputDateTime } from 'util/dates';
import { SchedulesAPI, JobTemplatesAPI, InventoriesAPI } from 'api'; import { SchedulesAPI, JobTemplatesAPI, InventoriesAPI } from 'api';
@@ -105,7 +106,7 @@ const nonRRuleValuesMatch = () => {
wrapper.find('DatePicker[aria-label="Start date"]').prop('value') wrapper.find('DatePicker[aria-label="Start date"]').prop('value')
).toBe('2020-04-02'); ).toBe('2020-04-02');
expect(wrapper.find('TimePicker[aria-label="Start time"]').prop('time')).toBe( 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( expect(wrapper.find('select#schedule-timezone').prop('value')).toBe(
'America/New_York' 'America/New_York'
@@ -474,11 +475,11 @@ describe('<ScheduleForm />', () => {
}); });
test('initially renders expected fields and values', () => { test('initially renders expected fields and values', () => {
const now = new Date(); const now = DateTime.now();
const closestQuarterHour = new Date( const closestQuarterHour = DateTime.fromMillis(
Math.ceil(now.getTime() / 900000) * 900000 Math.ceil(now.ts / 900000) * 900000
); );
const [date, time] = dateToInputDateTime(closestQuarterHour); const [date, time] = dateToInputDateTime(closestQuarterHour.toISO());
expect(wrapper.find('ScheduleForm').length).toBe(1); expect(wrapper.find('ScheduleForm').length).toBe(1);
defaultFieldsVisible(); defaultFieldsVisible();
expect(wrapper.find('FormGroup[label="Run every"]').length).toBe(0); expect(wrapper.find('FormGroup[label="Run every"]').length).toBe(0);

View File

@@ -1,14 +1,12 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { RRule } from 'rrule'; import { RRule } from 'rrule';
import { DateTime } from 'luxon';
import { getRRuleDayConstants } from 'util/dates'; import { getRRuleDayConstants } from 'util/dates';
const parseTime = (time) => { const parseTime = (time) => [
const [hour, minute, ampm] = time.split(/[: ]/); DateTime.fromFormat(time, 'h:mm a').hour,
const timeHour = DateTime.fromFormat(time, 'h:mm a').minute,
ampm === 'PM' && hour !== '12' ? `${parseInt(hour, 10) + 12}` : `${hour}`; ];
return [timeHour, minute];
};
export default function buildRuleObj(values) { export default function buildRuleObj(values) {
// Dates are formatted like "YYYY-MM-DD" // Dates are formatted like "YYYY-MM-DD"

View File

@@ -12,11 +12,7 @@ import RoutedTabs from 'components/RoutedTabs';
import { CardBody, CardActionsRow } from 'components/Card'; import { CardBody, CardActionsRow } from 'components/Card';
import { DetailList, Detail, NumberSinceDetail } from 'components/DetailList'; import { DetailList, Detail, NumberSinceDetail } from 'components/DetailList';
import { useConfig } from 'contexts/Config'; import { useConfig } from 'contexts/Config';
import { import { formatDateString, secondsToDays } from 'util/dates';
formatDateString,
formatDateStringUTC,
secondsToDays,
} from 'util/dates';
function SubscriptionDetail() { function SubscriptionDetail() {
const { me = {}, license_info, version } = useConfig(); const { me = {}, license_info, version } = useConfig();
@@ -98,8 +94,9 @@ function SubscriptionDetail() {
label={t`Expires on UTC`} label={t`Expires on UTC`}
value={ value={
license_info.license_date && license_info.license_date &&
formatDateStringUTC( formatDateString(
new Date(license_info.license_date * 1000).toISOString() new Date(license_info.license_date * 1000).toISOString(),
'UTC'
) )
} }
/> />

View File

@@ -20,7 +20,7 @@ import {
import { ExclamationTriangleIcon } from '@patternfly/react-icons'; import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import { ConfigAPI } from 'api'; import { ConfigAPI } from 'api';
import { formatDateStringUTC } from 'util/dates'; import { formatDateString } from 'util/dates';
import useRequest from 'hooks/useRequest'; import useRequest from 'hooks/useRequest';
import useSelected from 'hooks/useSelected'; import useSelected from 'hooks/useSelected';
import ErrorDetail from 'components/ErrorDetail'; import ErrorDetail from 'components/ErrorDetail';
@@ -168,8 +168,9 @@ function SubscriptionModal({
{subscription.instance_count} {subscription.instance_count}
</Td> </Td>
<Td dataLabel={t`Expires`} modifier="nowrap"> <Td dataLabel={t`Expires`} modifier="nowrap">
{formatDateStringUTC( {formatDateString(
new Date(subscription.license_date * 1000).toISOString() new Date(subscription.license_date * 1000).toISOString(),
'UTC'
)} )}
</Td> </Td>
</Tr> </Tr>

View File

@@ -1,62 +1,43 @@
/* eslint-disable import/prefer-default-export */ /* eslint-disable import/prefer-default-export */
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { RRule } from 'rrule'; import { RRule } from 'rrule';
import { getLanguage } from './language'; import { DateTime, Duration } from 'luxon';
const prependZeros = (value) => value.toString().padStart(2, 0); export function formatDateString(dateObj, tz = null) {
if (dateObj === null) {
export function formatDateString(dateString, lang = getLanguage(navigator)) {
if (dateString === null) {
return null; return null;
} }
return new Date(dateString).toLocaleString(lang);
}
export function formatDateStringUTC(dateString, lang = getLanguage(navigator)) { return tz !== null
if (dateString === null) { ? DateTime.fromISO(dateObj, { zone: tz }).toLocaleString(
return null; DateTime.DATETIME_SHORT_WITH_SECONDS
} )
return new Date(dateString).toLocaleString(lang, { timeZone: 'UTC' }); : DateTime.fromISO(dateObj).toLocaleString(
DateTime.DATETIME_SHORT_WITH_SECONDS
);
} }
export function secondsToHHMMSS(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) { export function secondsToDays(seconds) {
let duration = Math.floor(parseInt(seconds, 10) / 86400); return Duration.fromObject({ seconds }).toFormat('d');
if (duration < 0) {
duration = 0;
}
return duration.toString();
} }
export function timeOfDay() { export function timeOfDay() {
const date = new Date(); const dateTime = DateTime.local();
const hour = date.getHours(); return dateTime.toFormat('hh:mm a');
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;
} }
export function dateToInputDateTime(dateObj) { export function dateToInputDateTime(dt, tz = null) {
let date = dateObj; let dateTime;
if (typeof dateObj === 'string') { if (tz) {
date = new Date(dateObj); dateTime = DateTime.fromISO(dt, { zone: tz });
} else {
dateTime = DateTime.fromISO(dt);
} }
const year = date.getFullYear(); return [dateTime.toFormat('yyyy-LL-dd'), dateTime.toFormat('h:mm a')];
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}`];
} }
export function getRRuleDayConstants(dayString) { export function getRRuleDayConstants(dayString) {

View File

@@ -2,7 +2,6 @@ import { RRule } from 'rrule';
import { import {
dateToInputDateTime, dateToInputDateTime,
formatDateString, formatDateString,
formatDateStringUTC,
getRRuleDayConstants, getRRuleDayConstants,
secondsToDays, secondsToDays,
secondsToHHMMSS, secondsToHHMMSS,
@@ -21,35 +20,20 @@ const i18n = {
describe('formatDateString', () => { describe('formatDateString', () => {
test('it returns the expected value', () => { test('it returns the expected value', () => {
const lang = 'en-US'; expect(formatDateString(null)).toEqual(null);
expect(formatDateString(null, lang)).toEqual(null); expect(formatDateString('')).toEqual('Invalid DateTime');
expect(formatDateString('', lang)).toEqual('Invalid Date'); expect(formatDateString({})).toEqual('Invalid DateTime');
expect(formatDateString({}, lang)).toEqual('Invalid Date'); expect(formatDateString(undefined)).toEqual('Invalid DateTime');
expect(formatDateString(undefined, lang)).toEqual('Invalid Date'); expect(formatDateString('foobar')).toEqual('Invalid DateTime');
expect(formatDateString('foobar', lang)).toEqual('Invalid Date'); expect(formatDateString('2018-011-31T01:14:52.969227Z', undefined)).toEqual(
expect(formatDateString('2018-011-31T01:14:52.969227Z', lang)).toEqual( 'Invalid DateTime'
'Invalid Date'
); );
expect(formatDateString('2018-01-31T01:14:52.969227Z', lang)).toEqual( expect(formatDateString('2018-01-31T01:14:52.969227Z')).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(
'1/31/2018, 1:14:52 AM' '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', () => { describe('dateToInputDateTime', () => {
test('it returns the expected value', () => { test('it returns the expected value', () => {
expect( expect(dateToInputDateTime('2018-01-31T01:14:52.969227Z')).toEqual([
dateToInputDateTime(new Date('2018-01-31T01:14:52.969227Z')) '2018-01-31',
).toEqual(['2018-01-31', '1:14 AM']); '1:14 AM',
]);
}); });
}); });