diff --git a/framework/core/js/src/admin/components/UserListPage.tsx b/framework/core/js/src/admin/components/UserListPage.tsx
index 7d934ccf15..fec51bb615 100644
--- a/framework/core/js/src/admin/components/UserListPage.tsx
+++ b/framework/core/js/src/admin/components/UserListPage.tsx
@@ -273,7 +273,7 @@ export default class UserListPage extends AdminPage {
name: app.translator.trans('core.admin.users.grid.columns.join_time.title'),
content: (user: User) => (
- {dayjs(user.joinTime()).format('LLL')}
+ {app.translator.formatDateTime(dayjs(user.joinTime()), 'core.lib.datetime_formats.userListJoinDate')}
),
},
diff --git a/framework/core/js/src/common/Translator.tsx b/framework/core/js/src/common/Translator.tsx
index 29498fb3a5..b272a4fad7 100644
--- a/framework/core/js/src/common/Translator.tsx
+++ b/framework/core/js/src/common/Translator.tsx
@@ -1,12 +1,15 @@
+import type { Dayjs } from 'dayjs';
import { RichMessageFormatter, mithrilRichHandler, NestedStringArray } from '@askvortsov/rich-icu-message-formatter';
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
import username from './helpers/username';
import User from './models/User';
import extract from './utils/extract';
import extractText from './utils/extractText';
+import ItemList from './utils/ItemList';
type Translations = Record;
type TranslatorParameters = Record;
+type DateTimeFormatCallback = (id?: string) => string | void;
export default class Translator {
/**
@@ -14,6 +17,11 @@ export default class Translator {
*/
translations: Translations = {};
+ /**
+ * A item list of date time format callbacks.
+ */
+ dateTimeFormats: ItemList = new ItemList();
+
/**
* The underlying ICU MessageFormatter util.
*/
@@ -88,4 +96,23 @@ export default class Translator {
return id;
}
+
+ /**
+ * Formats the time.
+ *
+ * The format of the time will be chosen by the following order:
+ * - Custom format defined in the item list.
+ * - The format defined in current locale.
+ * - DayJS default format.
+ */
+ formatDateTime(time: Dayjs, id: string): string {
+ const formatCallback = this.dateTimeFormats.has(id) && this.dateTimeFormats.get(id);
+
+ if (formatCallback) {
+ const result = formatCallback.apply(this, [id]);
+ if (result) return result;
+ }
+
+ return time.format(this.translations[id]);
+ }
}
diff --git a/framework/core/js/src/common/helpers/fullTime.tsx b/framework/core/js/src/common/helpers/fullTime.tsx
index efde8faf45..f9530a007b 100644
--- a/framework/core/js/src/common/helpers/fullTime.tsx
+++ b/framework/core/js/src/common/helpers/fullTime.tsx
@@ -1,5 +1,6 @@
import dayjs from 'dayjs';
import type Mithril from 'mithril';
+import app from '../app';
/**
* The `fullTime` helper displays a formatted time string wrapped in a