Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Use Intl to localise dates and times #11422

Merged
merged 26 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b8a005d
Use Intl to generate better internationalised date formats
t3chguy Aug 17, 2023
6039cd9
Get `Yesterday` and `Today` from Intl also
t3chguy Aug 17, 2023
4a8975a
Correct capitalisation blunder
t3chguy Aug 17, 2023
f619f7b
Fix formatTime include weekday
t3chguy Aug 17, 2023
918894f
Iterate
t3chguy Aug 18, 2023
54f9b52
Fix tests
t3chguy Aug 18, 2023
88ab4ab
use jest setSystemTime
t3chguy Aug 18, 2023
8acf2d4
Discard changes to cypress/e2e/settings/general-user-settings-tab.spe…
t3chguy Aug 18, 2023
c1a861d
Discard changes to res/css/_components.pcss
t3chguy Aug 18, 2023
3c7bcbb
Discard changes to res/css/views/elements/_LanguageDropdown.pcss
t3chguy Aug 18, 2023
dd95432
Discard changes to src/components/views/elements/LanguageDropdown.tsx
t3chguy Aug 18, 2023
a7141c4
Add docs & tests for getDaysArray & getMonthsArray
t3chguy Aug 18, 2023
627e29d
Discard changes to test/components/structures/__snapshots__/MatrixCha…
t3chguy Aug 18, 2023
768bbe1
Consolidate consts
t3chguy Aug 18, 2023
ecc1151
Improve testing & documentation
t3chguy Aug 18, 2023
46c8150
Merge remote-tracking branch 'origin/t3chguy/localazy-use-intl' into …
t3chguy Aug 18, 2023
3f05175
Update snapshot
t3chguy Aug 18, 2023
e28d6a9
Apply suggestions from code review
t3chguy Aug 21, 2023
3ac969a
Iterate
t3chguy Aug 21, 2023
6e437f4
Clarify comments
t3chguy Aug 21, 2023
ceba3db
Update src/DateUtils.ts
t3chguy Aug 21, 2023
0257fe3
Specify hourCycle
t3chguy Aug 21, 2023
5a2d223
Merge remote-tracking branch 'origin/t3chguy/localazy-use-intl' into …
t3chguy Aug 21, 2023
8a00cea
Discard changes to test/components/views/settings/devices/DeviceDetai…
t3chguy Aug 21, 2023
f1cbeb2
Update comments
t3chguy Aug 21, 2023
5985b20
Merge remote-tracking branch 'origin/t3chguy/localazy-use-intl' into …
t3chguy Aug 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions src/DateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,25 @@ export function getMonthsArray(month: Intl.DateTimeFormatOptions["month"] = "sho
return [...Array(12).keys()].map((m) => format(Date.UTC(2021, m)));
}

// XXX: Ideally we could just specify `hour12: boolean` but it has issues on Chrome in the `en` locale
// https://support.google.com/chrome/thread/29828561?hl=en
function getTwelveHourOptions(showTwelveHour: boolean): Intl.DateTimeFormatOptions {
return {
hourCycle: showTwelveHour ? "h12" : "h23",
};
}

/**
* Formats a given date to a date & time string with
* variable resolution depending on far away the given date it from now.
* Formats a given date to a date & time string.
*
* The output format depends on how far away the given date is from now.
* Will use the browser's default time zone.
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
* If the date is today it will return a time string excluding seconds. See {@formatTime}.
* If the date is within the last 6 days it will return the name of the weekday along with the time string excluding seconds.
* If the date is within the same year then it will return the weekday, month and day of the month along with the time string excluding seconds.
* Otherwise, it will return a string representing the full date & time in a human friendly manner. See {@formatFullDate}.
* @param date - date object to format
* @param showTwelveHour - whether to use 12-hour mode instead of default 24-hour mode
* @param showTwelveHour - whether to use 12-hour rather than 24-hour time. Defaults to `false` (24 hour mode). Overrides the default from the locale, whether `true` or `false`.
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
export function formatDate(date: Date, showTwelveHour = false, locale?: string): string {
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -61,28 +70,29 @@ export function formatDate(date: Date, showTwelveHour = false, locale?: string):
if (date.toDateString() === now.toDateString()) {
return formatTime(date, showTwelveHour, _locale);
} else if (now.getTime() - date.getTime() < 6 * DAY_MS) {
// Time is less than 6 days into the future.
// Time is within the last 6 days (or in the future)
return new Intl.DateTimeFormat(_locale, {
...getTwelveHourOptions(showTwelveHour),
weekday: "short",
hour: "numeric",
minute: "2-digit",
hour12: showTwelveHour,
}).format(date);
} else if (now.getFullYear() === date.getFullYear()) {
return new Intl.DateTimeFormat(_locale, {
...getTwelveHourOptions(showTwelveHour),
weekday: "short",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
hour12: showTwelveHour,
}).format(date);
}
return formatFullDate(date, showTwelveHour, false, _locale);
}

/**
* Formats a given date to a human-friendly string with short weekday
* Formats a given date to a human-friendly string with short weekday.
* Will use the browser's default time zone.
* @example "Thu, 17 Nov 2022" in en-GB locale
* @param date - date object to format
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
Expand All @@ -97,7 +107,8 @@ export function formatFullDateNoTime(date: Date, locale?: string): string {
}

/**
* Formats a given date to a date & time string, optionally including seconds
* Formats a given date to a date & time string, optionally including seconds.
* Will use the browser's default time zone.
* @example "Thu, 17 Nov 2022, 4:58:32 pm" in en-GB locale with showTwelveHour=true and showSeconds=true
* @param date - date object to format
* @param showTwelveHour - whether to use 12-hour mode instead of default 24-hour mode
Expand All @@ -106,20 +117,20 @@ export function formatFullDateNoTime(date: Date, locale?: string): string {
*/
export function formatFullDate(date: Date, showTwelveHour = false, showSeconds = true, locale?: string): string {
return new Intl.DateTimeFormat(locale ?? getUserLanguage(), {
...getTwelveHourOptions(showTwelveHour),
weekday: "short",
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
second: showSeconds ? "2-digit" : undefined,
hour12: showTwelveHour,
}).format(date);
}

/**
* Formats dates to be compatible with attributes of a `<input type="date">`. Dates
* should be formatted like "2020-06-23" (formatted according to ISO8601)
* should be formatted like "2020-06-23" (formatted according to ISO8601).
*
* @param date The date to format.
* @returns The date string in ISO8601 format ready to be used with an `<input>`
Expand All @@ -132,7 +143,8 @@ export function formatDateForInput(date: Date): string {
}

/**
* Formats a given date to a time string including seconds
* Formats a given date to a time string including seconds.
* Will use the browser's default time zone.
* @example "4:58:32 PM" in en-GB locale with showTwelveHour=true
* @example "16:58:32" in en-GB locale with showTwelveHour=false
* @param date - date object to format
Expand All @@ -141,15 +153,16 @@ export function formatDateForInput(date: Date): string {
*/
export function formatFullTime(date: Date, showTwelveHour = false, locale?: string): string {
return new Intl.DateTimeFormat(locale ?? getUserLanguage(), {
...getTwelveHourOptions(showTwelveHour),
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: showTwelveHour,
}).format(date);
}

/**
* Formats a given date to a time string excluding seconds
* Formats a given date to a time string excluding seconds.
* Will use the browser's default time zone.
* @example "4:58 PM" in en-GB locale with showTwelveHour=true
* @example "16:58" in en-GB locale with showTwelveHour=false
* @param date - date object to format
Expand All @@ -158,9 +171,9 @@ export function formatFullTime(date: Date, showTwelveHour = false, locale?: stri
*/
export function formatTime(date: Date, showTwelveHour = false, locale?: string): string {
return new Intl.DateTimeFormat(locale ?? getUserLanguage(), {
...getTwelveHourOptions(showTwelveHour),
hour: "numeric",
minute: "2-digit",
hour12: showTwelveHour,
}).format(date);
}

Expand Down Expand Up @@ -257,7 +270,8 @@ export function formatFullDateNoDayISO(date: Date): string {
}

/**
* Formats a given date to a string
* Formats a given date to a string.
* Will use the browser's default time zone.
* @example 17/11/2022 in en-GB locale
* @param date - date object to format
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
Expand Down
2 changes: 1 addition & 1 deletion src/languageHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class UserFriendlyError extends Error {

export function getUserLanguage(): string {
const language = SettingsStore.getValue("language", null, /*excludeDefault:*/ true);
if (typeof language === "string" && !!language) {
if (typeof language === "string" && language !== "") {
return language;
} else {
return normalizeLanguageKey(getLanguageFromBrowser());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ exports[`<MessageEditHistory /> should match the snapshot 1`] = `
<span
class="mx_MessageTimestamp"
>
24:00
00:00
</span>
<div
class="mx_EventTile_content"
Expand Down Expand Up @@ -193,7 +193,7 @@ exports[`<MessageEditHistory /> should support events with 1`] = `
<span
class="mx_MessageTimestamp"
>
24:00
00:00
</span>
<div
class="mx_EventTile_content"
Expand Down Expand Up @@ -236,7 +236,7 @@ exports[`<MessageEditHistory /> should support events with 1`] = `
<span
class="mx_MessageTimestamp"
>
24:00
00:00
</span>
<div
class="mx_EventTile_content"
Expand Down Expand Up @@ -290,7 +290,7 @@ exports[`<MessageEditHistory /> should support events with 1`] = `
<span
class="mx_MessageTimestamp"
>
24:00
00:00
</span>
<div
class="mx_EventTile_content"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
<span
class="mx_MessageTimestamp mx_PinnedEventTile_timestamp"
>
Thu, Jan 1, 1970, 24:00
Thu, Jan 1, 1970, 00:00
</span>
<div
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ describe("<DeviceDetails />", () => {

beforeEach(() => {
jest.setSystemTime(now);
jest.spyOn(Date, "now").mockImplementation(() => now);
});

it("renders device without metadata", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/utils/DateUtils-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ describe("formatFullDateNoDayISO", () => {
});

describe("formatFullDateNoDayNoTime", () => {
it("should return en-GB locale", () => {
it("should return a date formatted for en-GB locale", () => {
expect(formatFullDateNoDayNoTime(REPEATABLE_DATE, "en-GB")).toMatchInlineSnapshot(`"17/11/2022"`);
});
});
Expand Down

Large diffs are not rendered by default.

Loading