feat: Switch FormattedDate component to Intl.DateTimeFormat (#8276)

* feat: Switch FormattedDate component to Intl.DateTimeFormat

* feat: Revert stylistic only changes

* Add tests

* Take code review into account

* Fix tests by forcing timezone

* Use constant formats in DateSeparator to avoid uneeded rerenders

* Add UserTimezone tests
This commit is contained in:
Nicolas Le Cam 2024-10-30 21:30:17 +01:00 committed by GitHub
parent f2198137aa
commit 0c587ab427
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 944 additions and 35 deletions

View file

@ -8,7 +8,7 @@ import {Text, type TextStyle} from 'react-native';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import FormattedDate from '@components/formatted_date';
import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date';
import FormattedText from '@components/formatted_text';
import FormattedTime from '@components/formatted_time';
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
@ -43,6 +43,12 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
const DATE_FORMATS = {
withinWeek: {weekday: 'long'},
withinYear: {month: 'short', day: 'numeric'},
afterYear: {dateStyle: 'medium'},
} satisfies Record<string, FormattedDateFormat>;
const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCompulsory, showToday, testID = '', textStyles = {}, theme, time, withinBrackets}: Props) => {
const userTimezone = getUserTimezoneProps(currentUser);
const timezone = userTimezone.useAutomaticTimezone ? userTimezone.automaticTimezone : userTimezone.manualTimezone;
@ -72,11 +78,11 @@ const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCo
/>
);
} else if (expiryMomentTime.isAfter(tomorrowEndTime)) {
let format = 'dddd';
let format: FormattedDateFormat = DATE_FORMATS.withinWeek;
if (expiryMomentTime.isAfter(plusSixDaysEndTime) && isCurrentYear) {
format = 'MMM DD';
format = DATE_FORMATS.withinYear;
} else if (!isCurrentYear) {
format = 'MMM DD, YYYY';
format = DATE_FORMATS.afterYear;
}
dateComponent = (

View file

@ -4,7 +4,7 @@
import React from 'react';
import {Text, TouchableOpacity, View} from 'react-native';
import FormattedDate from '@components/formatted_date';
import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date';
import {useTheme} from '@context/theme';
import {getFormattedFileSize} from '@utils/file';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@ -14,10 +14,15 @@ type FileInfoProps = {
disabled?: boolean;
file: FileInfo;
showDate: boolean;
channelName?: string ;
channelName?: string;
onPress: () => void;
}
const FORMAT = ' • MMM DD HH:MM A';
};
const FORMAT: FormattedDateFormat = {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
};
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
@ -91,13 +96,16 @@ const FileInfo = ({disabled, file, channelName, showDate, onPress}: FileInfoProp
<Text style={style.infoText}>
{`${getFormattedFileSize(file.size)}`}
</Text>
{showDate &&
<FormattedDate
style={style.infoText}
format={FORMAT}
value={file.create_at as number}
/>
}
{showDate && file.create_at != null && (
<>
{' • '}
<FormattedDate
style={style.infoText}
format={FORMAT}
value={file.create_at}
/>
</>
)}
</View>
</View>
</TouchableOpacity>

View file

@ -3,7 +3,7 @@
import React from 'react';
import {View, Text} from 'react-native';
import FormattedDate from '@components/formatted_date';
import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date';
import {useTheme} from '@context/theme';
import {getFormattedFileSize} from '@utils/file';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@ -11,7 +11,13 @@ import {typography} from '@utils/typography';
import Icon, {ICON_SIZE} from './icon';
const format = 'MMM DD YYYY HH:MM A';
const FORMAT: FormattedDateFormat = {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
};
const HEADER_MARGIN = 8;
const FILE_ICON_MARGIN = 8;
@ -78,7 +84,7 @@ const Header = ({fileInfo}: Props) => {
<Text style={style.infoText}>{`${size}`}</Text>
<FormattedDate
style={style.date}
format={format}
format={FORMAT}
value={fileInfo.create_at as number}
/>
</View>

View file

@ -0,0 +1,799 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<FormattedDate/> should match snapshot for 'bg' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26.10.2024 г.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'bg' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26.10.2024 г., 10:01 ч.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'bg' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26.10, 10:01 ч.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'bg' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26.10
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'bg' locale and '{"weekday": "long"}' format 1`] = `
<Text>
събота
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'bg' locale and 'undefined' format 1`] = `
<Text>
26.10.2024 г.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'de' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26.10.2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'de' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26. Okt. 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'de' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26. Okt., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'de' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26. Okt.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'de' locale and '{"weekday": "long"}' format 1`] = `
<Text>
Samstag
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'de' locale and 'undefined' format 1`] = `
<Text>
26.10.2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
Oct 26, 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
Oct 26, 2024, 10:01 AM
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
Oct 26, 10:01 AM
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
Oct 26
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en' locale and '{"weekday": "long"}' format 1`] = `
<Text>
Saturday
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en' locale and 'undefined' format 1`] = `
<Text>
Oct 26, 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en-AU' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 Oct 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en-AU' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 Oct 2024, 10:01 am
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en-AU' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 Oct, 10:01 am
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en-AU' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 Oct
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en-AU' locale and '{"weekday": "long"}' format 1`] = `
<Text>
Saturday
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'en-AU' locale and 'undefined' format 1`] = `
<Text>
26 Oct 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'es' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 oct 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'es' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 oct 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'es' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 oct, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'es' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 oct
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'es' locale and '{"weekday": "long"}' format 1`] = `
<Text>
sábado
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'es' locale and 'undefined' format 1`] = `
<Text>
26 oct 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fa' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
۵ آبان ۱۴۰۳
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fa' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
۵ آبان ۱۴۰۳، ۱۰:۰۱
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fa' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
۵ آبان، ۱۰:۰۱
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fa' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
۵ آبان
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fa' locale and '{"weekday": "long"}' format 1`] = `
<Text>
شنبه
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fa' locale and 'undefined' format 1`] = `
<Text>
۵ آبان ۱۴۰۳
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fr' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 oct. 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 oct. 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 oct., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fr' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 oct.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fr' locale and '{"weekday": "long"}' format 1`] = `
<Text>
samedi
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'fr' locale and 'undefined' format 1`] = `
<Text>
26 oct. 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'hu' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
2024. okt. 26.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'hu' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
2024. okt. 26. 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'hu' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
okt. 26. 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'hu' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
okt. 26.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'hu' locale and '{"weekday": "long"}' format 1`] = `
<Text>
szombat
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'hu' locale and 'undefined' format 1`] = `
<Text>
2024. okt. 26.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'it' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 ott 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'it' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 ott 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'it' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 ott, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'it' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 ott
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'it' locale and '{"weekday": "long"}' format 1`] = `
<Text>
sabato
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'it' locale and 'undefined' format 1`] = `
<Text>
26 ott 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ja' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
2024/10/26
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ja' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
2024年10月26日 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ja' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
10月26日 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ja' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
10月26日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ja' locale and '{"weekday": "long"}' format 1`] = `
<Text>
土曜日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ja' locale and 'undefined' format 1`] = `
<Text>
2024/10/26
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
2024. 10. 26.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
2024년 10월 26일 오전 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
10월 26일 오전 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
10월 26일
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"weekday": "long"}' format 1`] = `
<Text>
토요일
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ko' locale and 'undefined' format 1`] = `
<Text>
2024. 10. 26.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'nl' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 okt 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'nl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 okt 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'nl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 okt, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'nl' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 okt
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'nl' locale and '{"weekday": "long"}' format 1`] = `
<Text>
zaterdag
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'nl' locale and 'undefined' format 1`] = `
<Text>
26 okt 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pl' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 paź 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 paź 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pl' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 paź, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pl' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 paź
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pl' locale and '{"weekday": "long"}' format 1`] = `
<Text>
sobota
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pl' locale and 'undefined' format 1`] = `
<Text>
26 paź 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pt-BR' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 de out. de 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pt-BR' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 de out. de 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pt-BR' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 de out., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pt-BR' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 de out.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pt-BR' locale and '{"weekday": "long"}' format 1`] = `
<Text>
sábado
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'pt-BR' locale and 'undefined' format 1`] = `
<Text>
26 de out. de 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ro' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 oct. 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ro' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 oct. 2024, 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ro' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 oct., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ro' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 oct.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ro' locale and '{"weekday": "long"}' format 1`] = `
<Text>
sâmbătă
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ro' locale and 'undefined' format 1`] = `
<Text>
26 oct. 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ru' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 окт. 2024 г.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ru' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 окт. 2024 г., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ru' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 окт., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ru' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 окт.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ru' locale and '{"weekday": "long"}' format 1`] = `
<Text>
суббота
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'ru' locale and 'undefined' format 1`] = `
<Text>
26 окт. 2024 г.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'sv' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 okt. 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'sv' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 okt. 2024 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'sv' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 okt. 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'sv' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 okt.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'sv' locale and '{"weekday": "long"}' format 1`] = `
<Text>
lördag
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'sv' locale and 'undefined' format 1`] = `
<Text>
26 okt. 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'tr' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 Eki 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'tr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 Eki 2024 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'tr' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 Eki 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'tr' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 Eki
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'tr' locale and '{"weekday": "long"}' format 1`] = `
<Text>
Cumartesi
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'tr' locale and 'undefined' format 1`] = `
<Text>
26 Eki 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'uk' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 жовт. 2024 р.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'uk' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
26 жовт. 2024 р., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'uk' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
26 жовт., 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'uk' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 жовт.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'uk' locale and '{"weekday": "long"}' format 1`] = `
<Text>
субота
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'uk' locale and 'undefined' format 1`] = `
<Text>
26 жовт. 2024 р.
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'vi' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
26 thg 10, 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'vi' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
10:01 26 thg 10, 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'vi' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
10:01 26 thg 10
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'vi' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
26 thg 10
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'vi' locale and '{"weekday": "long"}' format 1`] = `
<Text>
Thứ Bảy
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'vi' locale and 'undefined' format 1`] = `
<Text>
26 thg 10, 2024
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-CN' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
2024年10月26日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-CN' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
2024年10月26日 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-CN' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
10月26日 10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-CN' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
10月26日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-CN' locale and '{"weekday": "long"}' format 1`] = `
<Text>
星期六
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-CN' locale and 'undefined' format 1`] = `
<Text>
2024年10月26日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-TW' locale and '{"dateStyle": "medium"}' format 1`] = `
<Text>
2024年10月26日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-TW' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
<Text>
2024年10月26日 上午10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-TW' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
<Text>
10月26日 上午10:01
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-TW' locale and '{"day": "numeric", "month": "short"}' format 1`] = `
<Text>
10月26日
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-TW' locale and '{"weekday": "long"}' format 1`] = `
<Text>
星期六
</Text>
`;
exports[`<FormattedDate/> should match snapshot for 'zh-TW' locale and 'undefined' format 1`] = `
<Text>
2024年10月26日
</Text>
`;
exports[`<FormattedDate/> should render with a manual user time 1`] = `
<Text>
Oct 26, 2024
</Text>
`;

View file

@ -0,0 +1,79 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {renderWithIntl} from '@test/intl-test-helper';
import locales from '../../i18n/languages';
import FormattedDate, {type FormattedDateFormat} from './index';
const DATE = new Date('2024-10-26T10:01:04.653Z');
const FORMATS = [
undefined,
{weekday: 'long'},
{dateStyle: 'medium'},
{month: 'short', day: 'numeric'},
{
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
},
{
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
},
] satisfies Array<FormattedDateFormat | undefined>;
const TEST_MATRIX = Object.keys(locales).
map((locale) => FORMATS.map<[string, FormattedDateFormat | undefined]>((format) => [locale, format])).
flat(1);
describe('<FormattedDate/>', () => {
it.each(TEST_MATRIX)("should match snapshot for '%s' locale and '%p' format", (locale, format) => {
const wrapper = renderWithIntl(
<FormattedDate
format={format}
value={DATE}
timezone='UTC'
/>,
{locale},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should render with a manual user time', () => {
const wrapper = renderWithIntl(
<FormattedDate
value={DATE}
timezone={{
automaticTimezone: '',
manualTimezone: 'Indian/Mauritius',
useAutomaticTimezone: '',
}}
/>,
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should render with an automatic user time', () => {
const wrapper = renderWithIntl(
<FormattedDate
value={DATE}
timezone={{
automaticTimezone: 'Indian/Mauritius',
manualTimezone: '',
useAutomaticTimezone: 'true',
}}
/>,
);
// Just check that the component render as automatic timezone is environment dependant
expect(wrapper.toJSON()).toBeTruthy();
});
});

View file

@ -1,34 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import moment from 'moment';
import mtz from 'moment-timezone';
import React from 'react';
import {useIntl} from 'react-intl';
import {Text, type TextProps} from 'react-native';
import {getLocaleFromLanguage} from '@i18n';
export type FormattedDateFormat = Exclude<Intl.DateTimeFormatOptions, 'timeZone'>;
type FormattedDateProps = TextProps & {
format?: string;
format?: FormattedDateFormat;
timezone?: string | UserTimezone | null;
value: number | string | Date;
}
const FormattedDate = ({format = 'MMM DD, YYYY', timezone, value, ...props}: FormattedDateProps) => {
const DEFAULT_FORMAT: FormattedDateFormat = {dateStyle: 'medium'};
const FormattedDate = ({
format = DEFAULT_FORMAT,
timezone,
value,
...props
}: FormattedDateProps) => {
const {locale} = useIntl();
moment.locale(getLocaleFromLanguage(locale).toLowerCase());
let formattedDate = mtz(value).format(format);
if (timezone) {
let zone: string;
if (typeof timezone === 'object') {
zone = timezone.useAutomaticTimezone ? timezone.automaticTimezone : timezone.manualTimezone;
} else {
zone = timezone;
}
formattedDate = mtz.tz(value, zone).format(format);
let timeZone: string | undefined;
if (timezone && typeof timezone === 'object') {
timeZone = timezone.useAutomaticTimezone ? timezone.automaticTimezone : timezone.manualTimezone;
} else {
timeZone = timezone ?? undefined;
}
const formattedDate = new Intl.DateTimeFormat(locale, {
...format,
timeZone,
}).format(new Date(value));
return <Text {...props}>{formattedDate}</Text>;
};

View file

@ -4,7 +4,7 @@
import React from 'react';
import {type StyleProp, View, type ViewStyle} from 'react-native';
import FormattedDate from '@components/formatted_date';
import FormattedDate, {type FormattedDateFormat} from '@components/formatted_date';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {isSameYear, isToday, isYesterday} from '@utils/datetime';
@ -38,6 +38,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
const DATE_FORMATS = {
withinYear: {month: 'short', day: 'numeric'},
afterYear: {dateStyle: 'medium'},
} satisfies Record<string, FormattedDateFormat>;
const RecentDate = (props: DateSeparatorProps) => {
const {date, ...otherProps} = props;
const when = new Date(date);
@ -60,7 +65,7 @@ const RecentDate = (props: DateSeparatorProps) => {
);
}
const format = isSameYear(when, new Date()) ? 'MMM DD' : 'MMM DD, YYYY';
const format: FormattedDateFormat = isSameYear(when, new Date()) ? DATE_FORMATS.withinYear : DATE_FORMATS.afterYear;
return (
<FormattedDate