Files
RenewCMS/web/src/lib/activityTime.ts
T

133 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { locale, t } from '@/i18n'
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
const MINUTE_MS = 60 * 1000
const HOUR_MS = 60 * MINUTE_MS
const ISO_DAY = /^(\d{4})-(\d{2})-(\d{2})$/
export interface DayGroup<T> {
key: string
label: string
items: T[]
}
function pad(value: number) {
return String(value).padStart(2, '0')
}
function monthShort(date: Date, timeZone?: 'UTC') {
if (locale.value === 'fr') {
return new Intl.DateTimeFormat('fr', {
month: 'short',
timeZone,
}).format(date)
}
return MONTHS[timeZone ? date.getUTCMonth() : date.getMonth()]
}
export function formatAbsoluteDay(date: Date, timeZone?: 'UTC') {
const day = timeZone ? date.getUTCDate() : date.getDate()
const year = timeZone ? date.getUTCFullYear() : date.getFullYear()
return `${day} ${monthShort(date, timeZone)} ${year}`
}
function clock(date: Date, withSeconds: boolean, utc: boolean) {
const hours = utc ? date.getUTCHours() : date.getHours()
const minutes = utc ? date.getUTCMinutes() : date.getMinutes()
const seconds = utc ? date.getUTCSeconds() : date.getSeconds()
const base = `${pad(hours)}:${pad(minutes)}`
return withSeconds ? `${base}:${pad(seconds)}` : base
}
export function localDayKey(date: Date) {
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
}
function startOfDay(date: Date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
}
export function dayLabel(iso: string, now: Date = new Date()) {
const date = new Date(iso)
const today = startOfDay(now)
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1)
const key = localDayKey(date)
if (key === localDayKey(today)) return t('Today')
if (key === localDayKey(yesterday)) return t('Yesterday')
return formatAbsoluteDay(date)
}
export function whenLabel(iso: string, now: Date = new Date()) {
const date = new Date(iso)
if (localDayKey(date) !== localDayKey(now)) return clock(date, false, false)
const elapsed = Math.max(0, now.getTime() - date.getTime())
if (elapsed < HOUR_MS) {
return t('{0} min ago', [Math.max(1, Math.floor(elapsed / MINUTE_MS))])
}
return t('{0} h ago', [Math.floor(elapsed / HOUR_MS)])
}
export function recentWhenLabel(iso: string, now: Date = new Date()) {
const date = new Date(iso)
const key = localDayKey(date)
if (key === localDayKey(now)) return whenLabel(iso, now)
const today = startOfDay(now)
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1)
if (key === localDayKey(yesterday)) return `${t('Yesterday')}, ${clock(date, false, false)}`
return localDayTime(iso)
}
export function localDayTime(iso: string) {
const date = new Date(iso)
return `${formatAbsoluteDay(date)}, ${clock(date, false, false)}`
}
export function fullLocalTime(iso: string) {
const date = new Date(iso)
return `${formatAbsoluteDay(date)}, ${clock(date, true, false)}`
}
export function fullUtcTime(iso: string) {
const date = new Date(iso)
return `${formatAbsoluteDay(date, 'UTC')}, ${clock(date, true, true)}`
}
export function groupByDay<T extends { created_at: string }>(
items: T[],
now: Date = new Date(),
): DayGroup<T>[] {
const groups: DayGroup<T>[] = []
for (const item of items) {
const key = localDayKey(new Date(item.created_at))
const last = groups[groups.length - 1]
if (last && last.key === key) {
last.items.push(item)
continue
}
groups.push({
key,
label: dayLabel(item.created_at, now),
items: [item],
})
}
return groups
}
function parseIsoDay(value: string) {
const match = ISO_DAY.exec(value)
if (!match) return null
return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]), 12)
}
export function formatDateRange(from: string, to: string) {
const start = parseIsoDay(from)
const end = parseIsoDay(to)
if (!start || !end) return ''
const sameYear = start.getFullYear() === end.getFullYear()
const sameMonth = sameYear && start.getMonth() === end.getMonth()
if (sameMonth && start.getDate() === end.getDate()) return formatAbsoluteDay(end)
if (sameMonth) return `${start.getDate()} – ${formatAbsoluteDay(end)}`
if (sameYear) return `${start.getDate()} ${monthShort(start)} – ${formatAbsoluteDay(end)}`
return `${formatAbsoluteDay(start)} – ${formatAbsoluteDay(end)}`
}