0
0
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:
silverwind 2026-02-16 11:58:04 +01:00 committed by GitHub
parent 0e99932530
commit cfc60b2142
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 11 additions and 81 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -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>

View File

@ -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;
});

View File

@ -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;
}
});

View File

@ -2,4 +2,3 @@ import './polyfills.ts';
import '@github/relative-time-element';
import './origin-url.ts';
import './overflow-menu.ts';
import './absolute-date.ts';

View File

@ -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',