From 427ab550a6a35e7369bc1b33a188bb3030c32ec0 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 8 Mar 2024 08:57:52 +0100 Subject: [PATCH] Set user's 24h preference from their current OS locale (#29651) Fixes: https://github.com/go-gitea/gitea/issues/28371 Fixed by using a JS solution that formats according to `lang`, but alters the 24h format setting as per user's locale. This will work for all tooltips: Screenshot 2024-03-07 at 23 03 35 Screenshot 2024-03-07 at 23 03 17 Screenshot 2024-03-07 at 23 14 34 I think there is only one other place in the UI where we render such absolute dates, which is in the actions view and which I've also fixed: Screenshot 2024-03-07 at 23 04 00 (cherry picked from commit f86e9a03673b70d660a4b7a1e53748757d7a45fa) --- web_src/js/components/RepoActionView.vue | 4 ++-- web_src/js/modules/tippy.js | 10 +++++++++- web_src/js/utils/time.js | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index e1e5d1bc1..83a10d3fe 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -3,7 +3,7 @@ import {SvgIcon} from '../svg.js'; import ActionRunStatus from './ActionRunStatus.vue'; import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; -import {getCurrentLocale} from '../utils.js'; +import {formatDatetime} from '../utils/time.js'; import {renderAnsi} from '../render/ansi.js'; import {POST, DELETE} from '../modules/fetch.js'; @@ -170,7 +170,7 @@ const sfc = { const logTimeStamp = document.createElement('span'); logTimeStamp.className = 'log-time-stamp'; const date = new Date(parseFloat(line.timestamp * 1000)); - const timeStamp = date.toLocaleString(getCurrentLocale(), {timeZoneName: 'short'}); + const timeStamp = formatDatetime(date); logTimeStamp.textContent = timeStamp; toggleElem(logTimeStamp, this.timeVisible['log-time-stamp']); // for "Show seconds" diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 27f371fd8..489afc0ae 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -1,5 +1,6 @@ import tippy, {followCursor} from 'tippy.js'; import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; +import {formatDatetime} from '../utils/time.js'; const visibleInstances = new Set(); @@ -93,8 +94,15 @@ function attachTooltip(target, content = null) { } function switchTitleToTooltip(target) { - const title = target.getAttribute('title'); + let title = target.getAttribute('title'); if (title) { + // apply custom formatting to relative-time's tooltips + if (target.tagName.toLowerCase() === 'relative-time') { + const datetime = target.getAttribute('datetime'); + if (datetime) { + title = formatDatetime(new Date(datetime)); + } + } target.setAttribute('data-tooltip-content', title); target.setAttribute('aria-label', title); // keep the attribute, in case there are some other "[title]" selectors diff --git a/web_src/js/utils/time.js b/web_src/js/utils/time.js index 3284e893e..1848792c9 100644 --- a/web_src/js/utils/time.js +++ b/web_src/js/utils/time.js @@ -1,4 +1,5 @@ import dayjs from 'dayjs'; +import {getCurrentLocale} from '../utils.js'; // Returns an array of millisecond-timestamps of start-of-week days (Sundays) export function startDaysBetween(startDate, endDate) { @@ -44,3 +45,23 @@ export function fillEmptyStartDaysWithZeroes(startDays, data) { return Object.values(result); } + +let dateFormat; + +// format a Date object to document's locale, but with 24h format from user's current locale because this +// option is a personal preference of the user, not something that the document's locale should dictate. +export function formatDatetime(date) { + if (!dateFormat) { + // TODO: replace `hour12` with `Intl.Locale.prototype.getHourCycles` once there is broad browser support + dateFormat = new Intl.DateTimeFormat(getCurrentLocale(), { + day: 'numeric', + month: 'short', + year: 'numeric', + hour: 'numeric', + hour12: !Number.isInteger(Number(new Intl.DateTimeFormat([], {hour: 'numeric'}).format())), + minute: '2-digit', + timeZoneName: 'short', + }); + } + return dateFormat.format(date); +}