Files
AstralRinth/packages/ui/src/composables/how-ago.ts
2026-01-16 03:30:00 +00:00

68 lines
1.7 KiB
TypeScript

import { computed, type ComputedRef } from 'vue'
import { injectI18n } from '../providers/i18n'
export type Formatter = (value: Date | number | null | undefined, options?: FormatOptions) => string
export interface FormatOptions {
roundingMode?: 'halfExpand' | 'floor' | 'ceil'
}
const formatters = new Map<string, ComputedRef<Intl.RelativeTimeFormat>>()
export function useRelativeTime(): Formatter {
const { locale } = injectI18n()
const formatterRef = computed(
() =>
new Intl.RelativeTimeFormat(locale.value, {
numeric: 'auto',
style: 'long',
}),
)
if (!formatters.has(locale.value)) {
formatters.set(locale.value, formatterRef)
}
return (value: Date | number | null | undefined) => {
if (value == null) {
return ''
}
const date = value instanceof Date ? value : new Date(value)
if (Number.isNaN(date.getTime())) {
return ''
}
const now = Date.now()
const diff = date.getTime() - now
const seconds = Math.round(diff / 1000)
const minutes = Math.round(diff / 60000)
const hours = Math.round(diff / 3600000)
const days = Math.round(diff / 86400000)
const weeks = Math.round(diff / 604800000)
const months = Math.round(diff / 2629746000)
const years = Math.round(diff / 31556952000)
const rtf = formatterRef.value
if (Math.abs(seconds) < 60) {
return rtf.format(seconds, 'second')
} else if (Math.abs(minutes) < 60) {
return rtf.format(minutes, 'minute')
} else if (Math.abs(hours) < 24) {
return rtf.format(hours, 'hour')
} else if (Math.abs(days) < 7) {
return rtf.format(days, 'day')
} else if (Math.abs(weeks) < 4) {
return rtf.format(weeks, 'week')
} else if (Math.abs(months) < 12) {
return rtf.format(months, 'month')
} else {
return rtf.format(years, 'year')
}
}
}