mirror of
https://github.com/go-gitea/gitea.git
synced 2026-02-20 13:37:18 +01:00
Use relative-time to render absolute dates (#36238)
`<relative-time>` can render absolute dates when passed [`threshold="P0Y"`](https://github.com/github/relative-time-element#threshold-string-default-p30d) and `prefix=""`, so remove the previously used `<absolute-date>` element in its favor. Devtest before: <img width="324" height="210" alt="Screenshot 2025-12-23 at 20 22 44" src="https://github.com/user-attachments/assets/cf78e0e7-f480-415f-98d5-09b25f9d5a8b" /> Devtest after: <img width="274" height="184" alt="Screenshot 2025-12-23 at 20 22 49" src="https://github.com/user-attachments/assets/5e7d25f6-eea1-4a8c-ba71-02b570804b95" /> Repo activity (rendering unchanged): <img width="1023" height="67" alt="image" src="https://github.com/user-attachments/assets/2c4fd6cb-14ab-43c6-ae4b-f86946b28288" /> --------- Signed-off-by: silverwind <me@silverwind.io> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
0e99932530
commit
cfc60b2142
@ -93,14 +93,14 @@ func dateTimeFormat(format string, datetime any) template.HTML {
|
||||
attrs := []string{`weekday=""`, `year="numeric"`}
|
||||
switch format {
|
||||
case "short", "long": // date only
|
||||
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
|
||||
return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
|
||||
attrs = append(attrs, `threshold="P0Y"`, `month="`+format+`"`, `day="numeric"`, `prefix=""`)
|
||||
case "full": // full date including time
|
||||
attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
|
||||
return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
|
||||
default:
|
||||
panic("Unsupported format " + format)
|
||||
}
|
||||
|
||||
return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
|
||||
}
|
||||
|
||||
func timeSinceTo(then any, now time.Time) template.HTML {
|
||||
|
||||
@ -32,10 +32,10 @@ func TestDateTime(t *testing.T) {
|
||||
assert.EqualValues(t, "-", du.AbsoluteShort(timeutil.TimeStamp(0)))
|
||||
|
||||
actual := du.AbsoluteShort(refTime)
|
||||
assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual)
|
||||
assert.EqualValues(t, `<relative-time weekday="" year="numeric" threshold="P0Y" month="short" day="numeric" prefix="" datetime="2018-01-01T00:00:00Z">2018-01-01</relative-time>`, actual)
|
||||
|
||||
actual = du.AbsoluteShort(refTimeStamp)
|
||||
assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2017-12-31T19:00:00-05:00">2017-12-31</absolute-date>`, actual)
|
||||
assert.EqualValues(t, `<relative-time weekday="" year="numeric" threshold="P0Y" month="short" day="numeric" prefix="" datetime="2017-12-31T19:00:00-05:00">2017-12-31</relative-time>`, actual)
|
||||
|
||||
actual = du.FullTime(refTimeStamp)
|
||||
assert.EqualValues(t, `<relative-time weekday="" year="numeric" format="datetime" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" data-tooltip-content data-tooltip-interactive="true" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
|
||||
|
||||
@ -118,13 +118,12 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>GiteaAbsoluteDate</h1>
|
||||
<div><absolute-date date="2024-03-11" year="numeric" day="numeric" month="short"></absolute-date></div>
|
||||
<div><absolute-date date="2024-03-11" year="numeric" day="numeric" month="long"></absolute-date></div>
|
||||
<div><absolute-date date="2024-03-11" year="" day="numeric" month="numeric"></absolute-date></div>
|
||||
<div><absolute-date date="2024-03-11" year="" day="numeric" month="numeric" weekday="long"></absolute-date></div>
|
||||
<div><absolute-date date="2024-03-11T19:00:00-05:00" year="" day="numeric" month="numeric" weekday="long"></absolute-date></div>
|
||||
<div class="tw-text-text-light-2">relative-time: <relative-time format="datetime" datetime="2024-03-11" year="" day="numeric" month="numeric"></relative-time></div>
|
||||
<h1>Absolute Dates</h1>
|
||||
<div><relative-time datetime="2024-03-11" threshold="P0Y" prefix="" weekday="" year="numeric" month="short"></relative-time></div>
|
||||
<div><relative-time datetime="2024-03-11" threshold="P0Y" prefix="" weekday="" year="numeric" month="long"></relative-time></div>
|
||||
<div><relative-time datetime="2024-03-11" threshold="P0Y" prefix="" weekday="" year="" day="numeric" month="numeric"></relative-time></div>
|
||||
<div><relative-time datetime="2024-03-11" threshold="P0Y" prefix="" weekday="long" year="" month="numeric"></relative-time></div>
|
||||
<div><relative-time datetime="2024-03-11T19:00:00-05:00" threshold="P0Y" prefix="" weekday="long" year="" month="numeric"></relative-time></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
import {toAbsoluteLocaleDate} from './absolute-date.ts';
|
||||
|
||||
test('toAbsoluteLocaleDate', () => {
|
||||
expect(toAbsoluteLocaleDate('2024-03-15', 'en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})).toEqual('March 15, 2024');
|
||||
|
||||
expect(toAbsoluteLocaleDate('2024-03-15T01:02:03', 'de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})).toEqual('15. März 2024');
|
||||
|
||||
// these cases shouldn't happen
|
||||
expect(toAbsoluteLocaleDate('2024-03-15 01:02:03', '', {})).toEqual('Invalid Date');
|
||||
expect(toAbsoluteLocaleDate('10000-01-01', '', {})).toEqual('Invalid Date');
|
||||
|
||||
// test different timezone
|
||||
const oldTZ = import.meta.env.TZ;
|
||||
import.meta.env.TZ = 'America/New_York';
|
||||
expect(new Date('2024-03-15').toLocaleString('en-US')).toEqual('3/14/2024, 8:00:00 PM');
|
||||
expect(toAbsoluteLocaleDate('2024-03-15', 'en-US')).toEqual('3/15/2024, 12:00:00 AM');
|
||||
import.meta.env.TZ = oldTZ;
|
||||
});
|
||||
@ -1,41 +0,0 @@
|
||||
export function toAbsoluteLocaleDate(date: string, lang?: string, opts?: Intl.DateTimeFormatOptions) {
|
||||
// only use the date part, it is guaranteed to be in ISO format (YYYY-MM-DDTHH:mm:ss.sssZ) or (YYYY-MM-DD)
|
||||
// if there is an "Invalid Date" error, there must be something wrong in code and should be fixed.
|
||||
// TODO: there is a root problem in backend code: the date "YYYY-MM-DD" is passed to backend without timezone (eg: deadline),
|
||||
// then backend parses it in server's timezone and stores the parsed timestamp into database.
|
||||
// If the user's timezone is different from the server's, the date might be displayed in the wrong day.
|
||||
const dateSep = date.indexOf('T');
|
||||
date = dateSep === -1 ? date : date.substring(0, dateSep);
|
||||
return new Date(`${date}T00:00:00`).toLocaleString(lang || [], opts);
|
||||
}
|
||||
|
||||
window.customElements.define('absolute-date', class extends HTMLElement {
|
||||
static observedAttributes = ['date', 'year', 'month', 'weekday', 'day'];
|
||||
|
||||
initialized = false;
|
||||
|
||||
update = () => {
|
||||
const opts: Record<string, string> = {};
|
||||
for (const attr of ['year', 'month', 'weekday', 'day']) {
|
||||
if (this.getAttribute(attr)) {
|
||||
opts[attr] = this.getAttribute(attr)!;
|
||||
}
|
||||
}
|
||||
const lang = this.closest('[lang]')?.getAttribute('lang') ||
|
||||
this.ownerDocument.documentElement.getAttribute('lang') || '';
|
||||
|
||||
if (!this.shadowRoot) this.attachShadow({mode: 'open'});
|
||||
this.shadowRoot!.textContent = toAbsoluteLocaleDate(this.getAttribute('date')!, lang, opts);
|
||||
};
|
||||
|
||||
attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null) {
|
||||
if (!this.initialized || oldValue === newValue) return;
|
||||
this.update();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.initialized = false;
|
||||
this.update();
|
||||
this.initialized = true;
|
||||
}
|
||||
});
|
||||
@ -2,4 +2,3 @@ import './polyfills.ts';
|
||||
import '@github/relative-time-element';
|
||||
import './origin-url.ts';
|
||||
import './overflow-menu.ts';
|
||||
import './absolute-date.ts';
|
||||
|
||||
@ -39,7 +39,6 @@ const webComponents = new Set([
|
||||
// our own, in web_src/js/webcomponents
|
||||
'overflow-menu',
|
||||
'origin-url',
|
||||
'absolute-date',
|
||||
// from dependencies
|
||||
'markdown-toolbar',
|
||||
'relative-time',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user