diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts index be6ee58..94aeb0b 100644 --- a/src/relative-time-element.ts +++ b/src/relative-time-element.ts @@ -3,8 +3,9 @@ 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 { +export default class RelativeTimeElement extends ExtendedTimeElement implements Intl.DateTimeFormatOptions { static get observedAttributes() { return [...ExtendedTimeElement.observedAttributes, 'prefix'] } @@ -12,22 +13,61 @@ 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() } if (tense === 'future' || (tense === 'auto' && inFuture && within)) { return micro ? relativeTime.microTimeUntil() : relativeTime.timeUntil() } - return `${this.prefix ? `${this.prefix} ` : ''}${relativeTime.formatDate()}` + 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' + } } /** @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 { 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".