|
|
|
|
@ -0,0 +1,110 @@
|
|
|
|
|
/**
|
|
|
|
|
* 相对时间工具函数
|
|
|
|
|
* 将时间字符串转换为相对时间显示文本
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
export interface RelativeTimeResult {
|
|
|
|
|
/** 相对时间显示文本 */
|
|
|
|
|
displayText: string
|
|
|
|
|
/** 完整时间字符串(hover显示) */
|
|
|
|
|
fullTime: string
|
|
|
|
|
/** 是否需要自动刷新(10分钟内) */
|
|
|
|
|
needRefresh: boolean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 格式化相对时间
|
|
|
|
|
* @param timeStr 时间字符串,支持格式:'yyyy-MM-dd HH:mm:ss' | Date | number(时间戳)
|
|
|
|
|
* @returns 相对时间结果
|
|
|
|
|
*/
|
|
|
|
|
export function formatRelativeTime(timeStr: string | Date | number | null | undefined): RelativeTimeResult {
|
|
|
|
|
if (!timeStr) {
|
|
|
|
|
return { displayText: '--', fullTime: '--', needRefresh: false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let date: Date
|
|
|
|
|
if (timeStr instanceof Date) {
|
|
|
|
|
date = timeStr
|
|
|
|
|
} else if (typeof timeStr === 'number') {
|
|
|
|
|
date = new Date(timeStr)
|
|
|
|
|
} else {
|
|
|
|
|
date = new Date(timeStr.replace(/-/g, '/')) // 兼容iOS Safari的日期解析
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isNaN(date.getTime())) {
|
|
|
|
|
return { displayText: '--', fullTime: String(timeStr), needRefresh: false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const now = Date.now()
|
|
|
|
|
const diff = now - date.getTime()
|
|
|
|
|
const absDiff = Math.abs(diff)
|
|
|
|
|
|
|
|
|
|
// 完整时间
|
|
|
|
|
const fullTime = formatFullTime(date)
|
|
|
|
|
|
|
|
|
|
// 未来时间直接返回完整时间
|
|
|
|
|
if (diff < -60000) {
|
|
|
|
|
return { displayText: fullTime, fullTime, needRefresh: false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 10秒内
|
|
|
|
|
if (absDiff < 10 * 1000) {
|
|
|
|
|
return { displayText: '刚刚', fullTime, needRefresh: true }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1分钟内
|
|
|
|
|
if (absDiff < 60 * 1000) {
|
|
|
|
|
const seconds = Math.floor(absDiff / 1000)
|
|
|
|
|
return { displayText: `${seconds}秒前`, fullTime, needRefresh: true }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1小时内
|
|
|
|
|
if (absDiff < 60 * 60 * 1000) {
|
|
|
|
|
const minutes = Math.floor(absDiff / (60 * 1000))
|
|
|
|
|
return { displayText: `${minutes}分钟前`, fullTime, needRefresh: true }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 24小时内
|
|
|
|
|
if (absDiff < 24 * 60 * 60 * 1000) {
|
|
|
|
|
const hours = Math.floor(absDiff / (60 * 60 * 1000))
|
|
|
|
|
const minutes = Math.floor((absDiff % (60 * 60 * 1000)) / (60 * 1000))
|
|
|
|
|
if (minutes > 0) {
|
|
|
|
|
return { displayText: `${hours}小时${minutes}分钟前`, fullTime, needRefresh: true }
|
|
|
|
|
}
|
|
|
|
|
return { displayText: `${hours}小时前`, fullTime, needRefresh: true }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2天内(昨天/前天)
|
|
|
|
|
if (absDiff < 2 * 24 * 60 * 60 * 1000) {
|
|
|
|
|
return { displayText: '1天前', fullTime, needRefresh: false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7天内
|
|
|
|
|
if (absDiff < 7 * 24 * 60 * 60 * 1000) {
|
|
|
|
|
const days = Math.floor(absDiff / (24 * 60 * 60 * 1000))
|
|
|
|
|
return { displayText: `${days}天前`, fullTime, needRefresh: false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 超过7天:显示 MM-DD HH:mm
|
|
|
|
|
return { displayText: formatShortDate(date), fullTime, needRefresh: false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 格式化完整时间 yyyy-MM-dd HH:mm:ss */
|
|
|
|
|
function formatFullTime(date: Date): string {
|
|
|
|
|
const y = date.getFullYear()
|
|
|
|
|
const m = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
const d = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
const h = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
const min = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
const s = String(date.getSeconds()).padStart(2, '0')
|
|
|
|
|
return `${y}-${m}-${d} ${h}:${min}:${s}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 格式化短日期 MM-DD HH:mm */
|
|
|
|
|
function formatShortDate(date: Date): string {
|
|
|
|
|
const m = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
const d = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
const h = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
const min = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
return `${m}-${d} ${h}:${min}`
|
|
|
|
|
}
|