From 3c715c5931aa9f2f0e21332233f5d1050ede51aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 25 Oct 2022 12:08:01 +0100 Subject: [PATCH 1/4] Use `strftime` directly in `` Co-authored-by: Keith Cirkel --- src/relative-time-element.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts index be6ee58..c98fd29 100644 --- a/src/relative-time-element.ts +++ b/src/relative-time-element.ts @@ -3,6 +3,7 @@ import RelativeTime from './relative-time.js' import ExtendedTimeElement from './extended-time-element.js' import {localeFromElement} from './utils.js' import {isDuration, withinDuration} from './duration.js' +import {strftime} from './strftime.js' export default class RelativeTimeElement extends ExtendedTimeElement { static get observedAttributes() { @@ -12,15 +13,15 @@ export default class RelativeTimeElement extends ExtendedTimeElement { getFormattedDate(now = new Date()): string | undefined { const date = this.date if (!date) return - const relativeTime = new RelativeTime(date, localeFromElement(this)) const format = this.format if (format !== 'auto' && format !== 'micro') { - return relativeTime.formatDate(format) + return strftime(date, format) } const tense = this.tense const micro = format === 'micro' const inFuture = now.getTime() < date.getTime() const within = withinDuration(now, date, this.threshold) + const relativeTime = new RelativeTime(date, localeFromElement(this)) if (tense === 'past' || (tense === 'auto' && !inFuture && within)) { return micro ? relativeTime.microTimeAgo() : relativeTime.timeAgo() } From a6a9bf81e06484bfe00c92e078496e2f65faece9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 25 Oct 2022 12:09:53 +0100 Subject: [PATCH 2/4] Inline formatDate into `` Co-authored-by: Keith Cirkel --- src/relative-time-element.ts | 10 +++++++--- src/relative-time.ts | 13 ------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts index c98fd29..8125880 100644 --- a/src/relative-time-element.ts +++ b/src/relative-time-element.ts @@ -1,7 +1,7 @@ import type {Tense, Format} from './relative-time.js' import RelativeTime from './relative-time.js' import ExtendedTimeElement from './extended-time-element.js' -import {localeFromElement} from './utils.js' +import {localeFromElement, isDayFirst, isThisYear, isYearSeparator} from './utils.js' import {isDuration, withinDuration} from './duration.js' import {strftime} from './strftime.js' @@ -13,7 +13,7 @@ export default class RelativeTimeElement extends ExtendedTimeElement { getFormattedDate(now = new Date()): string | undefined { const date = this.date if (!date) return - const format = this.format + let format = this.format if (format !== 'auto' && format !== 'micro') { return strftime(date, format) } @@ -28,7 +28,11 @@ export default class RelativeTimeElement extends ExtendedTimeElement { if (tense === 'future' || (tense === 'auto' && inFuture && within)) { return micro ? relativeTime.microTimeUntil() : relativeTime.timeUntil() } - return `${this.prefix ? `${this.prefix} ` : ''}${relativeTime.formatDate()}` + format = isDayFirst() ? '%e %b' : '%b %e' + if (!isThisYear(date)) { + format += isYearSeparator() ? ', %Y' : ' %Y' + } + return `${this.prefix ? `${this.prefix} ` : ''}${strftime(date, format)}` } /** @deprecated */ diff --git a/src/relative-time.ts b/src/relative-time.ts index 2a38db5..0bcac7b 100644 --- a/src/relative-time.ts +++ b/src/relative-time.ts @@ -1,5 +1,3 @@ -import {isDayFirst, isThisYear, isYearSeparator} from './utils.js' -import {strftime} from './strftime.js' import {RelativeTime as RelativeTimePonyfill} from './relative-time-ponyfill.js' export type Format = 'auto' | 'micro' | string @@ -123,17 +121,6 @@ export default class RelativeTime { return '1m' } } - - formatDate(defaultFormat?: string): string { - let format = defaultFormat - if (format == null) { - format = isDayFirst() ? '%e %b' : '%b %e' - if (!isThisYear(this.date)) { - format += isYearSeparator() ? ', %Y' : ' %Y' - } - } - return strftime(this.date, format) - } } function formatRelativeTime(locale: string, value: number, unit: Intl.RelativeTimeFormatUnit): string { From dbb85616926fc131376d04e3cdc25906434032e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 25 Oct 2022 12:43:27 +0100 Subject: [PATCH 3/4] Refactor `` to not need utilities Co-authored-by: Keith Cirkel --- src/relative-time-element.ts | 49 ++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts index 8125880..94aeb0b 100644 --- a/src/relative-time-element.ts +++ b/src/relative-time-element.ts @@ -1,11 +1,11 @@ import type {Tense, Format} from './relative-time.js' import RelativeTime from './relative-time.js' import ExtendedTimeElement from './extended-time-element.js' -import {localeFromElement, isDayFirst, isThisYear, isYearSeparator} from './utils.js' +import {localeFromElement} from './utils.js' import {isDuration, withinDuration} from './duration.js' import {strftime} from './strftime.js' -export default class RelativeTimeElement extends ExtendedTimeElement { +export default class RelativeTimeElement extends ExtendedTimeElement implements Intl.DateTimeFormatOptions { static get observedAttributes() { return [...ExtendedTimeElement.observedAttributes, 'prefix'] } @@ -13,7 +13,7 @@ export default class RelativeTimeElement extends ExtendedTimeElement { getFormattedDate(now = new Date()): string | undefined { const date = this.date if (!date) return - let format = this.format + const format = this.format if (format !== 'auto' && format !== 'micro') { return strftime(date, format) } @@ -28,11 +28,46 @@ export default class RelativeTimeElement extends ExtendedTimeElement { if (tense === 'future' || (tense === 'auto' && inFuture && within)) { return micro ? relativeTime.microTimeUntil() : relativeTime.timeUntil() } - format = isDayFirst() ? '%e %b' : '%b %e' - if (!isThisYear(date)) { - format += isYearSeparator() ? ', %Y' : ' %Y' + if ('Intl' in window && 'DateTimeFormat' in Intl) { + const formatter = new Intl.DateTimeFormat(localeFromElement(this), { + minute: this.minute, + hour: this.hour, + day: this.day, + month: this.month, + year: this.year + }) + return `${this.prefix} ${formatter.format(date)}`.trim() + } + return `${this.prefix} ${strftime(date, `%b %e${this.year === 'numeric' ? ', %Y' : ''}`)}`.trim() + } + + get minute() { + const minute = this.getAttribute('minute') + if (minute === 'numeric' || minute === '2-digit') return minute + } + get hour() { + const hour = this.getAttribute('hour') + if (hour === 'numeric' || hour === '2-digit') return hour + } + + get day() { + const day = this.getAttribute('day') ?? 'numeric' + if (day === 'numeric' || day === '2-digit') return day + } + + get month() { + const month = this.getAttribute('month') ?? 'short' + if (month === 'numeric' || month === '2-digit' || month === 'short' || month === 'long' || month === 'narrow') + return month + } + + get year() { + const year = this.getAttribute('year') + if (year === 'numeric' || year === '2-digit') return year + + if (new Date().getUTCFullYear() !== this.date?.getUTCFullYear()) { + return 'numeric' } - return `${this.prefix ? `${this.prefix} ` : ''}${strftime(date, format)}` } /** @deprecated */ From f3b8df519f63379cdad53ca370ac0003a872efaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 25 Oct 2022 12:43:44 +0100 Subject: [PATCH 4/4] Remove unused utilities Co-authored-by: Keith Cirkel --- src/utils.ts | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 89300f8..d8f191d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -37,38 +37,6 @@ export function isDayFirst(locale = 'default'): boolean { } } -let yearSeparator: boolean | null = null -const yearFormatter = makeFormatter({day: 'numeric', month: 'short', year: 'numeric'}) - -// Private: Determine if the year should be separated from the month and day -// with a comma. For example, `9 Jun 2014` in en-GB and `Jun 9, 2014` in en-US. -// -// Returns true if the date needs a separator. -export function isYearSeparator(): boolean { - if (yearSeparator !== null) { - return yearSeparator - } - - const formatter = yearFormatter() - if (formatter) { - const output = formatter.format(new Date(0)) - yearSeparator = !!output.match(/\d,/) - return yearSeparator - } else { - return true - } -} - -// Private: Determine if the date occurs in the same year as today's date. -// -// date - The Date to test. -// -// Returns true if it's this year. -export function isThisYear(date: Date): boolean { - const now = new Date() - return now.getUTCFullYear() === date.getUTCFullYear() -} - // Private: Get preferred Intl locale for a target element. // // Traverses parents until it finds an explicit `lang` other returns "default".