Skip to content

Commit 5cf3a3d

Browse files
authored
STSMACOM-950 Render DATE_PICKER custom field values in UTC so the displayed day matches the saved value (#1630)
https://folio-org.atlassian.net/browse/STSMACOM-950 ## Purpose For users in timezones west of UTC, `DATE_PICKER` custom field values render one day earlier than the saved value. The stored date string is parsed as UTC midnight and then formatted in the local timezone, which shifts the displayed day backwards. ## Approach Pass the raw value to `formatDate` with `{ timeZone: 'UTC' }` in `ViewCustomFieldsRecord` so the displayed day matches the saved value regardless of the user's timezone.
1 parent afd511f commit 5cf3a3d

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## IN PROGRESS
44

55
* Supply Personal Data Disclosure form. Refs STSMACOM-909.
6+
* `<ViewCustomFieldsRecord>` - render `DATE_PICKER` values in UTC so the displayed day matches the saved value in non-UTC timezones. Fixes STSMACOM-950.
67

78
## 10.1.0 IN PROGRESS
89

lib/CustomFields/pages/ViewCustomFieldsRecord/ViewCustomFieldsRecord.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ const ViewCustomFieldsRecord = ({
168168
return getSelectedOptionLabel(customField);
169169
}
170170
if (type === DATE_PICKER) {
171-
return formatDate(new Date(customFieldValue));
171+
return formatDate(customFieldValue, { timeZone: 'UTC' });
172172
}
173173
if (type === TEXTAREA || type === TEXTFIELD) {
174174
return customFieldValue;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { IntlProvider } from 'react-intl';
2+
3+
import { render, screen } from '@folio/jest-config-stripes/testing-library/react';
4+
5+
import { useCustomFieldsQuery } from '../../utils';
6+
import ViewCustomFieldsRecord from './ViewCustomFieldsRecord';
7+
8+
// The global jest setup mocks react-intl so that `formatDate` returns its
9+
// input unchanged. Restore the real module here so we can exercise actual
10+
// `IntlProvider` timezone handling.
11+
jest.mock('react-intl', () => jest.requireActual('react-intl'));
12+
13+
jest.mock('../../utils', () => ({
14+
...jest.requireActual('../../utils'),
15+
useCustomFieldsQuery: jest.fn(),
16+
useSectionTitleQuery: jest.fn(() => ({
17+
sectionTitle: { value: 'Title' },
18+
isLoadingSectionTitle: false,
19+
isSectionTitleError: false,
20+
})),
21+
useLoadingErrorCallout: jest.fn(() => ({
22+
calloutRef: { current: null },
23+
})),
24+
}));
25+
26+
const dateField = {
27+
id: '1',
28+
refId: 'date1',
29+
name: 'Date',
30+
type: 'DATE_PICKER',
31+
entityType: 'user',
32+
visible: true,
33+
};
34+
35+
const renderWithTimeZone = (timeZone) => render(
36+
<IntlProvider locale="en-US" timeZone={timeZone}>
37+
<ViewCustomFieldsRecord
38+
accordionId="acc"
39+
backendModuleName="users-test"
40+
entityType="user"
41+
customFieldsValues={{ date1: '2026-01-01' }}
42+
expanded
43+
onToggle={() => {}}
44+
showAccordion={false}
45+
/>
46+
</IntlProvider>
47+
);
48+
49+
describe('ViewCustomFieldsRecord - DATE_PICKER timezone handling (STSMACOM-950)', () => {
50+
beforeEach(() => {
51+
useCustomFieldsQuery.mockReturnValue({
52+
customFields: [dateField],
53+
isLoadingCustomFields: false,
54+
isCustomFieldsError: false,
55+
});
56+
});
57+
58+
// Without the fix, an ISO date string is parsed as UTC midnight and then
59+
// formatted in the provider's timezone, which rolls the displayed day back
60+
// by one in any zone west of UTC.
61+
it.each(['UTC', 'America/Los_Angeles', 'Asia/Tokyo'])(
62+
'renders the saved day unchanged when IntlProvider timeZone is %s',
63+
(timeZone) => {
64+
renderWithTimeZone(timeZone);
65+
66+
expect(screen.getByText('1/1/2026')).toBeInTheDocument();
67+
},
68+
);
69+
});

0 commit comments

Comments
 (0)