diff --git a/docs/DateInput.md b/docs/DateInput.md index 262d464462..92186fe85a 100644 --- a/docs/DateInput.md +++ b/docs/DateInput.md @@ -26,7 +26,18 @@ import { DateInput } from 'react-admin'; ``` -The field value must be a string with the pattern `YYYY-MM-DD` (ISO 8601), e.g. `'2022-04-30'`. +The field value must be a string using the pattern `YYYY-MM-DD` (ISO 8601), e.g. `'2022-04-30'`. The returned input value will also be in this format, regardless of the browser locale. + +`` also accepts values that can be converted to a `Date` object, such as: + +- a localized date string (e.g. `'30/04/2022'`), +- an ISO date string (e.g. `'2022-04-30T00:00:00.000Z'`), +- a `Date` object, or +- a Linux timestamp (e.g. `1648694400000`). + +In these cases, `` will automatically convert the value to the `YYYY-MM-DD` format. + +**Note**: This conversion may change the date because of timezones. For example, the date string `'2022-04-30T00:00:00.000Z'` in Europe may be displayed as `'2022-04-29'` in Honolulu. If this is not what you want, pass your own [`parse`](./Inputs.md#parse) function to ``. ## Props diff --git a/packages/ra-ui-materialui/src/input/DateInput.spec.tsx b/packages/ra-ui-materialui/src/input/DateInput.spec.tsx index 2524698847..8aaeb5da2a 100644 --- a/packages/ra-ui-materialui/src/input/DateInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/DateInput.spec.tsx @@ -103,8 +103,8 @@ describe('', () => { it.each([ '2021-09-11T20:46:20.000+02:00', '2021-09-11 20:46:20.000+02:00', - '2021-09-11T20:46:20.000-04:00', - '2021-09-11 20:46:20.000-04:00', + '2021-09-10T20:46:20.000-04:00', + '2021-09-10 20:46:20.000-04:00', '2021-09-11T20:46:20.000Z', '2021-09-11 20:46:20.000Z', ])('should accept a value with timezone %s', async publishedAt => { diff --git a/packages/ra-ui-materialui/src/input/DateInput.stories.tsx b/packages/ra-ui-materialui/src/input/DateInput.stories.tsx index b41457ef54..26f6bcdbff 100644 --- a/packages/ra-ui-materialui/src/input/DateInput.stories.tsx +++ b/packages/ra-ui-materialui/src/input/DateInput.stories.tsx @@ -61,7 +61,7 @@ export const DefaultValue = () => ( '2021-09-11 20:46:20.000Z', new Date('2021-09-11T20:46:20.000+02:00'), // although this one is 2021-09-10, its local timezone makes it 2021-09-11 in the test timezone - new Date('2021-09-10T20:46:20.000-04:00'), + new Date('2021-09-10T23:46:20.000-09:00'), new Date('2021-09-11T20:46:20.000Z'), 1631385980000, ].map((defaultValue, index) => ( diff --git a/packages/ra-ui-materialui/src/input/DateInput.tsx b/packages/ra-ui-materialui/src/input/DateInput.tsx index 4bba3ae61a..7eff1db638 100644 --- a/packages/ra-ui-materialui/src/input/DateInput.tsx +++ b/packages/ra-ui-materialui/src/input/DateInput.tsx @@ -205,22 +205,22 @@ export type DateInputProps = CommonInputProps & Omit; /** - * Convert Date object to String, ignoring the timezone. + * Convert Date object to String, using the local timezone * * @param {Date} value value to convert * @returns {String} A standardized date (yyyy-MM-dd), to be passed to an */ const convertDateToString = (value: Date) => { if (!(value instanceof Date) || isNaN(value.getDate())) return ''; - let UTCDate = new Date(value.getTime() + value.getTimezoneOffset() * 60000); + let localDate = new Date(value.getTime()); const pad = '00'; - const yyyy = UTCDate.getFullYear().toString(); - const MM = (UTCDate.getMonth() + 1).toString(); - const dd = UTCDate.getDate().toString(); + const yyyy = localDate.getFullYear().toString(); + const MM = (localDate.getMonth() + 1).toString(); + const dd = localDate.getDate().toString(); return `${yyyy}-${(pad + MM).slice(-2)}-${(pad + dd).slice(-2)}`; }; -const dateRegex = /^(\d{4}-\d{2}-\d{2}).*$/; +const dateRegex = /^\d{4}-\d{2}-\d{2}$/; const defaultInputLabelProps = { shrink: true }; /** @@ -234,14 +234,21 @@ const defaultInputLabelProps = { shrink: true }; * - a Linux timestamp * - an empty string * + * When it's not a bare date string (YYYY-MM-DD), the value is converted to + * this format using the JS Date object. + * THIS MAY CHANGE THE DATE VALUE depending on the browser locale. + * For example, the string "09/11/2021" may be converted to "2021-09-10" + * in Honolulu. This is expected behavior. + * If this is not what you want, you should provide your own parse method. + * * The output is always a string in the "YYYY-MM-DD" format. * * @example * defaultFormat('2021-09-11'); // '2021-09-11' - * defaultFormat('09/11/2021'); // '2021-09-11' - * defaultFormat('2021-09-11T20:46:20.000Z'); // '2021-09-11' - * defaultFormat(new Date('2021-09-11T20:46:20.000Z')); // '2021-09-11' - * defaultFormat(1631385980000); // '2021-09-11' + * defaultFormat('09/11/2021'); // '2021-09-11' (may change depending on the browser locale) + * defaultFormat('2021-09-11T20:46:20.000Z'); // '2021-09-11' (may change depending on the browser locale) + * defaultFormat(new Date('2021-09-11T20:46:20.000Z')); // '2021-09-11' (may change depending on the browser locale) + * defaultFormat(1631385980000); // '2021-09-11' (may change depending on the browser locale) * defaultFormat(''); // null */ const defaultFormat = (value: string | Date | number) => { @@ -256,11 +263,10 @@ const defaultFormat = (value: string | Date | number) => { return convertDateToString(value); } - // Valid date strings should be stripped of their time and timezone parts. + // Valid date strings (YYYY-MM-DD) should be considered as is if (typeof value === 'string') { - const matches = dateRegex.exec(value); - if (matches) { - return matches[1]; + if (dateRegex.test(value)) { + return value; } }