From 9e22385e8daf0631c8d759972cce909caf2fd488 Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Tue, 4 Apr 2023 22:22:28 -0400 Subject: [PATCH] Add back timezone when not using utc picker --- .../src/widgets/datetime/DateTimeControl.tsx | 26 ++++-- .../__tests__/DateTimeControl.spec.ts | 90 +++++++++---------- .../datetime/__tests__/utc.util.spec.ts | 8 +- .../core/src/widgets/datetime/constants.ts | 1 + packages/core/test/globalSetup.js | 2 +- 5 files changed, 69 insertions(+), 58 deletions(-) diff --git a/packages/core/src/widgets/datetime/DateTimeControl.tsx b/packages/core/src/widgets/datetime/DateTimeControl.tsx index 2d221159..60a54689 100644 --- a/packages/core/src/widgets/datetime/DateTimeControl.tsx +++ b/packages/core/src/widgets/datetime/DateTimeControl.tsx @@ -12,7 +12,12 @@ import Field from '@staticcms/core/components/common/field/Field'; import TextField from '@staticcms/core/components/common/text-field/TextField'; import { isNotEmpty } from '@staticcms/core/lib/util/string.util'; import NowButton from './components/NowButton'; -import { DEFAULT_DATETIME_FORMAT, DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT } from './constants'; +import { + DEFAULT_DATETIME_FORMAT, + DEFAULT_DATE_FORMAT, + DEFAULT_TIMEZONE_FORMAT, + DEFAULT_TIME_FORMAT, +} from './constants'; import { localToUTC } from './utc.util'; import type { TextFieldProps as MuiTextFieldProps } from '@mui/material/TextField'; @@ -58,6 +63,11 @@ const DateTimeControl: FC> = ({ setOpen(false); }, []); + const timezoneExtra = useMemo( + () => (field.picker_utc ? '' : DEFAULT_TIMEZONE_FORMAT), + [field.picker_utc], + ); + const { format, dateFormat, timeFormat } = useMemo(() => { // dateFormat and timeFormat are strictly for modifying input field with the date/time pickers const dateFormat: string | boolean = field.date_format ?? true; @@ -68,9 +78,9 @@ const DateTimeControl: FC> = ({ if (timeFormat === false) { finalFormat = field.format ?? DEFAULT_DATE_FORMAT; } else if (dateFormat === false) { - finalFormat = field.format ?? DEFAULT_TIME_FORMAT; + finalFormat = field.format ?? `${DEFAULT_TIME_FORMAT}${timezoneExtra}`; } else { - finalFormat = field.format ?? DEFAULT_DATETIME_FORMAT; + finalFormat = field.format ?? `${DEFAULT_DATETIME_FORMAT}${timezoneExtra}`; } return { @@ -78,7 +88,7 @@ const DateTimeControl: FC> = ({ dateFormat, timeFormat, }; - }, [field.date_format, field.format, field.time_format]); + }, [field.date_format, field.format, field.time_format, timezoneExtra]); const inputFormat = useMemo(() => { if (typeof dateFormat === 'string' || typeof timeFormat === 'string') { @@ -92,7 +102,7 @@ const DateTimeControl: FC> = ({ if (typeof timeFormat === 'string' && isNotEmpty(timeFormat)) { formatParts.push(timeFormat); } else if (timeFormat !== false) { - formatParts.push(DEFAULT_TIME_FORMAT); + formatParts.push(`${DEFAULT_TIME_FORMAT}${timezoneExtra}`); } if (formatParts.length > 0) { @@ -105,11 +115,11 @@ const DateTimeControl: FC> = ({ } if (dateFormat === false) { - return format ?? DEFAULT_TIME_FORMAT; + return format ?? `${DEFAULT_TIME_FORMAT}${timezoneExtra}`; } - return format ?? DEFAULT_DATETIME_FORMAT; - }, [dateFormat, format, timeFormat]); + return format ?? `${DEFAULT_DATETIME_FORMAT}${timezoneExtra}`; + }, [dateFormat, format, timeFormat, timezoneExtra]); const defaultValue = useMemo(() => { const today = field.picker_utc ? localToUTC(new Date()) : new Date(); diff --git a/packages/core/src/widgets/datetime/__tests__/DateTimeControl.spec.ts b/packages/core/src/widgets/datetime/__tests__/DateTimeControl.spec.ts index 1f52f881..18264440 100644 --- a/packages/core/src/widgets/datetime/__tests__/DateTimeControl.spec.ts +++ b/packages/core/src/widgets/datetime/__tests__/DateTimeControl.spec.ts @@ -202,7 +202,7 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('2023-02-12T10:15:35.000'); + expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00'); }); it('should use default if provided', () => { @@ -210,39 +210,39 @@ describe(DateTimeControl.name, () => { label: 'I am a label', field: { ...mockDateTimeField, - default: '2023-01-10T06:23:15.000', + default: '2023-01-10T06:23:15.000-10:00', }, }); const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('2023-01-10T06:23:15.000'); + expect(input).toHaveValue('2023-01-10T06:23:15.000-10:00'); }); it('should only use prop value as initial value', async () => { - const { rerender, getByTestId } = renderControl({ value: '2023-02-12T10:15:35.000' }); + const { rerender, getByTestId } = renderControl({ value: '2023-02-12T10:15:35.000-10:00' }); const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('2023-02-12T10:15:35.000'); + expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00'); - rerender({ value: '2023-02-18T14:37:02.000' }); - expect(input).toHaveValue('2023-02-12T10:15:35.000'); + rerender({ value: '2023-02-18T14:37:02.000-10:00' }); + expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00'); }); it('should use prop value exclusively if field is i18n duplicate', async () => { const { rerender, getByTestId } = renderControl({ field: { ...mockDateTimeField, i18n: 'duplicate' }, duplicate: true, - value: '2023-02-12T10:15:35.000', + value: '2023-02-12T10:15:35.000-10:00', }); const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('2023-02-12T10:15:35.000'); + expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00'); - rerender({ value: '2023-02-18T14:37:02.000' }); - expect(input).toHaveValue('2023-02-18T14:37:02.000'); + rerender({ value: '2023-02-18T14:37:02.000-10:00' }); + expect(input).toHaveValue('2023-02-18T14:37:02.000-10:00'); }); it('should disable input and now button if disabled', () => { @@ -297,7 +297,7 @@ describe(DateTimeControl.name, () => { await userEventActions.click(days[0]); }); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T10:15:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T10:15:35.000-10:00'); const hours = document.querySelectorAll('.MuiClockNumber-root'); expect(hours.length).toBe(12); @@ -312,7 +312,7 @@ describe(DateTimeControl.name, () => { fireEvent.touchEnd(square!, hourClockEvent); }); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:15:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:15:35.000-10:00'); const minutes = document.querySelectorAll('.MuiClockNumber-root'); expect(minutes.length).toBe(12); @@ -324,7 +324,7 @@ describe(DateTimeControl.name, () => { fireEvent.touchEnd(square!, minuteClockEvent); }); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:05:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:05:35.000-10:00'); }); it('should set value to current date and time when now button is clicked', async () => { @@ -337,20 +337,20 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('2023-02-12T10:15:35.000'); + expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00'); await selectDateTime(getByTestId, 1, 2, 20, 'am'); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000'); - expect(input).toHaveValue('2023-02-01T02:20:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00'); + expect(input).toHaveValue('2023-02-01T02:20:35.000-10:00'); await act(async () => { const nowButton = getByTestId('datetime-now'); await userEventActions.click(nowButton); }); - expect(onChange).toHaveBeenLastCalledWith('2023-02-12T10:15:36.000'); // Testing framework moves the time forward by a second by this point - expect(input).toHaveValue('2023-02-12T10:15:36.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-12T10:15:36.000-10:00'); // Testing framework moves the time forward by a second by this point + expect(input).toHaveValue('2023-02-12T10:15:36.000-10:00'); }); describe('format', () => { @@ -368,12 +368,12 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('02/12/2023 10:15:35.000'); + expect(input).toHaveValue('02/12/2023 10:15:35.000-10:00'); await selectDateTime(getByTestId, 1, 2, 20, 'am'); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000'); - expect(input).toHaveValue('02/01/2023 02:20:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00'); + expect(input).toHaveValue('02/01/2023 02:20:35.000-10:00'); }); it('uses custom time display format', async () => { @@ -394,7 +394,7 @@ describe(DateTimeControl.name, () => { await selectDateTime(getByTestId, 1, 2, 20, 'am'); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00'); expect(input).toHaveValue('2023-02-01 02:20 am'); }); @@ -417,7 +417,7 @@ describe(DateTimeControl.name, () => { await selectDateTime(getByTestId, 1, 2, 20, 'am'); - expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000'); + expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00'); expect(input).toHaveValue('02/01/2023 02:20 am'); }); @@ -479,7 +479,7 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('date-time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('2023-02-12T15:15:35.000'); + expect(input).toHaveValue('2023-02-12T20:15:35.000'); }); it('should use default if provided (assuming default is already in UTC)', () => { @@ -741,7 +741,7 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('10:15:35.000'); + expect(input).toHaveValue('10:15:35.000-10:00'); }); it('should use default if provided', () => { @@ -749,43 +749,43 @@ describe(DateTimeControl.name, () => { label: 'I am a label', field: { ...mockTimeField, - default: '06:23:15.000', + default: '06:23:15.000-10:00', }, }); const inputWrapper = getByTestId('time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('06:23:15.000'); + expect(input).toHaveValue('06:23:15.000-10:00'); }); it('should only use prop value as initial value', async () => { const { rerender, getByTestId } = renderControl({ field: mockTimeField, - value: '10:15:35.000', + value: '10:15:35.000-10:00', }); const inputWrapper = getByTestId('time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('10:15:35.000'); + expect(input).toHaveValue('10:15:35.000-10:00'); - rerender({ value: '14:37:02.000' }); - expect(input).toHaveValue('10:15:35.000'); + rerender({ value: '14:37:02.000-10:00' }); + expect(input).toHaveValue('10:15:35.000-10:00'); }); it('should use prop value exclusively if field is i18n duplicate', async () => { const { rerender, getByTestId } = renderControl({ field: { ...mockTimeField, i18n: 'duplicate' }, duplicate: true, - value: '10:15:35.000', + value: '10:15:35.000-10:00', }); const inputWrapper = getByTestId('time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('10:15:35.000'); + expect(input).toHaveValue('10:15:35.000-10:00'); - rerender({ value: '14:37:02.000' }); - expect(input).toHaveValue('14:37:02.000'); + rerender({ value: '14:37:02.000-10:00' }); + expect(input).toHaveValue('14:37:02.000-10:00'); }); it('should disable input and now button if disabled', () => { @@ -850,7 +850,7 @@ describe(DateTimeControl.name, () => { fireEvent.touchEnd(square!, hourClockEvent); }); - expect(onChange).toHaveBeenLastCalledWith('01:15:35.000'); + expect(onChange).toHaveBeenLastCalledWith('01:15:35.000-10:00'); const minutes = document.querySelectorAll('.MuiClockNumber-root'); expect(minutes.length).toBe(12); @@ -862,7 +862,7 @@ describe(DateTimeControl.name, () => { fireEvent.touchEnd(square!, minuteClockEvent); }); - expect(onChange).toHaveBeenLastCalledWith('01:05:35.000'); + expect(onChange).toHaveBeenLastCalledWith('01:05:35.000-10:00'); }); it('should set value to current time when now button is clicked', async () => { @@ -876,20 +876,20 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('10:15:35.000'); + expect(input).toHaveValue('10:15:35.000-10:00'); await selectTime(getByTestId, 2, 20, 'am'); - expect(onChange).toHaveBeenLastCalledWith('02:20:35.000'); - expect(input).toHaveValue('02:20:35.000'); + expect(onChange).toHaveBeenLastCalledWith('02:20:35.000-10:00'); + expect(input).toHaveValue('02:20:35.000-10:00'); await act(async () => { const nowButton = getByTestId('datetime-now'); await userEventActions.click(nowButton); }); - expect(onChange).toHaveBeenLastCalledWith('10:15:36.000'); // Testing framework moves the time forward by a second by this point - expect(input).toHaveValue('10:15:36.000'); + expect(onChange).toHaveBeenLastCalledWith('10:15:36.000-10:00'); // Testing framework moves the time forward by a second by this point + expect(input).toHaveValue('10:15:36.000-10:00'); }); describe('format', () => { @@ -911,7 +911,7 @@ describe(DateTimeControl.name, () => { await selectTime(getByTestId, 2, 20, 'am'); - expect(onChange).toHaveBeenLastCalledWith('02:20:35.000'); + expect(onChange).toHaveBeenLastCalledWith('02:20:35.000-10:00'); expect(input).toHaveValue('02:20 am'); }); @@ -972,7 +972,7 @@ describe(DateTimeControl.name, () => { const inputWrapper = getByTestId('time-input'); const input = inputWrapper.getElementsByTagName('input')[0]; - expect(input).toHaveValue('15:15:35.000'); + expect(input).toHaveValue('20:15:35.000'); }); it('should use default if provided (assuming default is already in UTC)', () => { diff --git a/packages/core/src/widgets/datetime/__tests__/utc.util.spec.ts b/packages/core/src/widgets/datetime/__tests__/utc.util.spec.ts index c719f812..e326315f 100644 --- a/packages/core/src/widgets/datetime/__tests__/utc.util.spec.ts +++ b/packages/core/src/widgets/datetime/__tests__/utc.util.spec.ts @@ -1,15 +1,15 @@ import { localToUTC, utcToLocal } from '../utc.util'; describe('utc util', () => { - it('converts local (EST) to UTC', () => { + it('converts local (Hawaii time) to UTC', () => { expect(localToUTC(new Date(2023, 1, 12, 10, 5, 35)).toString()).toEqual( - 'Sun Feb 12 2023 15:05:35 GMT-0500 (Eastern Standard Time)', + 'Sun Feb 12 2023 20:05:35 GMT-1000 (Hawaii-Aleutian Standard Time)', ); }); - it('converts UTC to local (EST)', () => { + it('converts UTC to local (Hawaii time)', () => { expect(utcToLocal(new Date(2023, 1, 12, 15, 5, 35)).toString()).toEqual( - 'Sun Feb 12 2023 10:05:35 GMT-0500 (Eastern Standard Time)', + 'Sun Feb 12 2023 05:05:35 GMT-1000 (Hawaii-Aleutian Standard Time)', ); }); }); diff --git a/packages/core/src/widgets/datetime/constants.ts b/packages/core/src/widgets/datetime/constants.ts index 30a50c2e..64704e3d 100644 --- a/packages/core/src/widgets/datetime/constants.ts +++ b/packages/core/src/widgets/datetime/constants.ts @@ -1,3 +1,4 @@ export const DEFAULT_DATE_FORMAT = 'yyyy-MM-dd'; export const DEFAULT_TIME_FORMAT = 'HH:mm:ss.SSS'; export const DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"; +export const DEFAULT_TIMEZONE_FORMAT = "XXX"; diff --git a/packages/core/test/globalSetup.js b/packages/core/test/globalSetup.js index e96f7d07..7526fb5e 100644 --- a/packages/core/test/globalSetup.js +++ b/packages/core/test/globalSetup.js @@ -1,3 +1,3 @@ module.exports = async () => { - process.env.TZ = 'America/New_York'; + process.env.TZ = 'US/Hawaii'; };