Fix timezone crash (#8485) (#8489)

* Fix timezone crash by adding all timezones to the polyfill

* update snapshots; be careful about undefined: diff on local vs CI

* one more that's different locally vs CI

* i18n

---------

Co-authored-by: Christopher Poile <cpoile@gmail.com>
This commit is contained in:
Daniel Espino García 2025-01-17 09:25:06 +01:00 committed by GitHub
parent 5615405e3a
commit 32e038fe10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 24340 additions and 23600 deletions

View file

@ -1,5 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<FormattedDate/> should default when timezone is not found 1`] = `
<Text>
10:01 AM
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'bg' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26.10.2024 г.
@ -797,3 +803,645 @@ exports[`<FormattedDate/> should render with a manual user time 1`] = `
Oct 26, 2024
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Abidjan 1`] = `
<Text>
10:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Addis_Ababa 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Algiers 1`] = `
<Text>
11:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Blantyre 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Cairo 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Casablanca 1`] = `
<Text>
11:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Ceuta 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Tripoli 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Africa/Windhoek 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Anchorage 1`] = `
<Text>
2:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Anguilla 1`] = `
<Text>
6:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Araguaina 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Argentina/Buenos_Aires 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Asuncion 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Bahia 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Bahia_Banderas 1`] = `
<Text>
4:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Belize 1`] = `
<Text>
4:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Bogota 1`] = `
<Text>
5:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Boise 1`] = `
<Text>
4:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Campo_Grande 1`] = `
<Text>
6:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Caracas 1`] = `
<Text>
6:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Chicago 1`] = `
<Text>
5:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Chihuahua 1`] = `
<Text>
4:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Creston 1`] = `
<Text>
3:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Danmarkshavn 1`] = `
<Text>
10:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Detroit 1`] = `
<Text>
6:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Glace_Bay 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Godthab 1`] = `
<Text>
9:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Havana 1`] = `
<Text>
6:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Indiana/Marengo 1`] = `
<Text>
6:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Los_Angeles 1`] = `
<Text>
3:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Montevideo 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Noronha 1`] = `
<Text>
8:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Regina 1`] = `
<Text>
4:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Santa_Isabel 1`] = `
<Text>
3:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Santiago 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Sao_Paulo 1`] = `
<Text>
7:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Scoresbysund 1`] = `
<Text>
9:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/St_Johns 1`] = `
<Text>
7:31 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone America/Tijuana 1`] = `
<Text>
3:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/Casey 1`] = `
<Text>
6:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/Davis 1`] = `
<Text>
5:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/DumontDUrville 1`] = `
<Text>
8:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/Macquarie 1`] = `
<Text>
9:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/Mawson 1`] = `
<Text>
3:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/McMurdo 1`] = `
<Text>
11:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Antarctica/Vostok 1`] = `
<Text>
3:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Arctic/Longyearbyen 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Aden 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Amman 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Anadyr 1`] = `
<Text>
10:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Baghdad 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Baku 1`] = `
<Text>
2:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Beirut 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Brunei 1`] = `
<Text>
6:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Chita 1`] = `
<Text>
7:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Choibalsan 1`] = `
<Text>
6:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Colombo 1`] = `
<Text>
3:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Damascus 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Dhaka 1`] = `
<Text>
4:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Dili 1`] = `
<Text>
7:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Dubai 1`] = `
<Text>
2:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Hong_Kong 1`] = `
<Text>
6:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Irkutsk 1`] = `
<Text>
6:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Jerusalem 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Kabul 1`] = `
<Text>
2:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Kamchatka 1`] = `
<Text>
10:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Karachi 1`] = `
<Text>
3:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Kathmandu 1`] = `
<Text>
3:46 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Kolkata 1`] = `
<Text>
3:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Krasnoyarsk 1`] = `
<Text>
5:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Nicosia 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Nicosia 2`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Novokuznetsk 1`] = `
<Text>
5:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Pyongyang 1`] = `
<Text>
7:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Rangoon 1`] = `
<Text>
4:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Sakhalin 1`] = `
<Text>
9:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Taipei 1`] = `
<Text>
6:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Tbilisi 1`] = `
<Text>
2:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Tehran 1`] = `
<Text>
1:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Yekaterinburg 1`] = `
<Text>
3:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Asia/Yerevan 1`] = `
<Text>
2:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Atlantic/Canary 1`] = `
<Text>
11:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Atlantic/Cape_Verde 1`] = `
<Text>
9:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Australia/Adelaide 1`] = `
<Text>
8:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Australia/Brisbane 1`] = `
<Text>
8:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Australia/Currie 1`] = `
<Text>
9:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Australia/Darwin 1`] = `
<Text>
7:31 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Australia/Melbourne 1`] = `
<Text>
9:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Etc/GMT+10 1`] = `
<Text>
12:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Etc/GMT+11 1`] = `
<Text>
11:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Etc/GMT+12 1`] = `
<Text>
10:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Etc/GMT-12 1`] = `
<Text>
10:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Etc/GMT-13 1`] = `
<Text>
11:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Astrakhan 1`] = `
<Text>
2:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Belgrade 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Guernsey 1`] = `
<Text>
11:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Helsinki 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Isle_of_Man 1`] = `
<Text>
11:01 AM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Istanbul 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Kaliningrad 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Kirov 1`] = `
<Text>
1:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Europe/Sarajevo 1`] = `
<Text>
12:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Indian/Mahe 1`] = `
<Text>
2:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Pacific/Apia 1`] = `
<Text>
11:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone Pacific/Fiji 1`] = `
<Text>
10:01 PM
</Text>
`;
exports[`<FormattedDate/> should render with timezone undefined 1`] = `
<Text>
10:01 AM
</Text>
`;

View file

@ -2,13 +2,19 @@
// See LICENSE.txt for license information.
import React from 'react';
import timezones from 'timezones.json';
import {renderWithIntl} from '@test/intl-test-helper';
import {logDebug} from '@utils/log';
import locales from '../../i18n/languages';
import FormattedDate, {type FormattedDateFormat} from './index';
jest.mock('@utils/log', () => ({
logDebug: jest.fn(),
}));
const DATE = new Date('2024-10-26T10:01:04.653Z');
const FORMATS = [
undefined,
@ -34,6 +40,24 @@ const TEST_MATRIX = Object.keys(locales).
map((locale) => FORMATS.map<[string, FormattedDateFormat | undefined]>((format) => [locale, format])).
flat(1);
function getTimezoneTestsCases() {
// Mimics the logic for the timezones offered by the web app
// in webapp/channels/src/components/user_settings/display/manage_timezones/manage_timezones.tsx
let index = 0;
const testCases = [];
let previousTimezone = '';
for (const timezone of timezones) {
if (timezone.utc[index] === previousTimezone) {
index++;
} else {
index = 0;
}
testCases.push([timezone.utc[index]]);
previousTimezone = timezone.utc[index];
}
return testCases;
}
describe('<FormattedDate/>', () => {
it.each(TEST_MATRIX)("should match snapshot for '%s' locale and '%p' format", (locale, format) => {
const wrapper = renderWithIntl(
@ -76,4 +100,42 @@ describe('<FormattedDate/>', () => {
// Just check that the component render as automatic timezone is environment dependant
expect(wrapper.toJSON()).toBeTruthy();
});
it.each(getTimezoneTestsCases())('should render with timezone %s', (timezone) => {
const wrapper = renderWithIntl(
<FormattedDate
value={DATE}
timezone={timezone}
format={{hour: 'numeric', minute: 'numeric'}}
/>,
);
expect(wrapper.queryByText('Unknown')).not.toBeTruthy();
expect(logDebug).not.toHaveBeenCalled();
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should default when timezone is not found', () => {
const wrapper = renderWithIntl(
<FormattedDate
value={DATE}
timezone={'not valid timezone'}
format={{hour: 'numeric', minute: 'numeric'}}
/>,
);
expect(wrapper.queryByText('Unknown')).not.toBeTruthy();
expect(logDebug).toHaveBeenCalledTimes(1);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should show unknown on other errors', () => {
const wrapper = renderWithIntl(
<FormattedDate
value={DATE}
timezone={undefined}
format={{hour: 'numeric', minute: 'invalid' as any}}
/>,
);
expect(wrapper.queryByText('Unknown')).toBeTruthy();
expect(logDebug).toHaveBeenCalledTimes(2);
});
});

View file

@ -5,6 +5,8 @@ import React from 'react';
import {useIntl} from 'react-intl';
import {Text, type TextProps} from 'react-native';
import {logDebug} from '@utils/log';
export type FormattedDateFormat = Exclude<Intl.DateTimeFormatOptions, 'timeZone'>;
type FormattedDateProps = TextProps & {
@ -21,7 +23,7 @@ const FormattedDate = ({
value,
...props
}: FormattedDateProps) => {
const {locale} = useIntl();
const {locale, formatMessage} = useIntl();
let timeZone: string | undefined;
if (timezone && typeof timezone === 'object') {
@ -30,10 +32,29 @@ const FormattedDate = ({
timeZone = timezone ?? undefined;
}
const formattedDate = new Intl.DateTimeFormat(locale, {
...format,
timeZone,
}).format(new Date(value));
let formattedDate;
try {
formattedDate = new Intl.DateTimeFormat(locale, {
...format,
timeZone,
}).format(new Date(value));
} catch (error) {
logDebug('Failed to format date', {locale, timezone}, error);
}
if (!formattedDate) {
try {
formattedDate = new Intl.DateTimeFormat(locale, {
...format,
}).format(new Date(value));
} catch (error) {
logDebug('Failed to format default date', {locale}, error);
}
}
if (!formattedDate) {
formattedDate = formatMessage({id: 'date.unknown', defaultMessage: 'Unknown'});
}
return <Text {...props}>{formattedDate}</Text>;
};

View file

@ -293,6 +293,7 @@
"custom_status.suggestions.working_from_home": "Working from home",
"date_separator.today": "Today",
"date_separator.yesterday": "Yesterday",
"date.unknown": "Unknown",
"default_skin_tone": "Default Skin Tone",
"display_settings.clock.military": "24-hour",
"display_settings.clock.standard": "12-hour",

View file

@ -46,7 +46,7 @@ if (global.HermesInternal) {
require('@formatjs/intl-pluralrules/polyfill-force');
require('@formatjs/intl-numberformat/polyfill-force');
require('@formatjs/intl-datetimeformat/polyfill-force');
require('@formatjs/intl-datetimeformat/add-golden-tz');
require('@formatjs/intl-datetimeformat/add-all-tz');
require('@formatjs/intl-listformat/polyfill-force');
}

47195
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -162,6 +162,7 @@
"nock": "13.5.6",
"patch-package": "8.0.0",
"react-devtools-core": "6.0.1",
"timezones.json": "1.7.1",
"tough-cookie": "5.0.0",
"ts-jest": "29.2.5",
"typescript": "5.7.2",