date-utils.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. function normalizeTimestamp(ts) {
  2. let value = parseInt(ts, 10)
  3. if (!value) value = Date.now()
  4. if (String(value).length === 10) value *= 1000
  5. return value
  6. }
  7. function padUnit(value, width) {
  8. const text = String(value)
  9. return width > 1 ? text.padStart(width, '0') : text
  10. }
  11. export function formatTimestamp(ts, pattern = 'yyyy-mm-dd') {
  12. const date = new Date(normalizeTimestamp(ts))
  13. const tokens = {
  14. 'y+': String(date.getFullYear()),
  15. 'm+': String(date.getMonth() + 1),
  16. 'd+': String(date.getDate()),
  17. 'h+': String(date.getHours()),
  18. 'M+': String(date.getMinutes()),
  19. 's+': String(date.getSeconds())
  20. }
  21. let output = pattern
  22. for (const token of Object.keys(tokens)) {
  23. const match = new RegExp(`(${token})`).exec(output)
  24. if (!match) continue
  25. const width = match[1].length
  26. output = output.replace(match[1], padUnit(tokens[token], width))
  27. }
  28. return output
  29. }
  30. export function createRelativeTimeFormatter(i18n) {
  31. const translate = (key, fallback) => {
  32. try {
  33. return i18n ? i18n.t(key) : fallback
  34. } catch (_) {
  35. return fallback
  36. }
  37. }
  38. return { translate }
  39. }
  40. export function socialRelativeTime(ts, pattern = 'yyyy-mm-dd', i18n) {
  41. const { translate } = createRelativeTimeFormatter(i18n)
  42. const seconds = Math.floor((Date.now() - normalizeTimestamp(ts)) / 1000)
  43. if (seconds < 300) return translate('find.just-now', '刚刚')
  44. if (seconds < 3600) return `${Math.floor(seconds / 60)}${translate('find.minutes-ago', '分钟前')}`
  45. if (seconds < 86400) return `${Math.floor(seconds / 3600)}${translate('find.hours-ago', '小时前')}`
  46. if (seconds < 2592000) return `${Math.floor(seconds / 86400)}${translate('find.days-ago', '天前')}`
  47. if (pattern === false) {
  48. if (seconds < 365 * 86400) {
  49. return `${Math.floor(seconds / (86400 * 30))}${translate('find.months-ago', '个月前')}`
  50. }
  51. return `${Math.floor(seconds / (86400 * 365))}${translate('find.years-ago', '年前')}`
  52. }
  53. return formatTimestamp(ts, pattern)
  54. }
  55. export function chatBubbleTime(ts, i18n) {
  56. const { translate } = createRelativeTimeFormatter(i18n)
  57. const seconds = Math.floor((Date.now() - normalizeTimestamp(ts)) / 1000)
  58. if (seconds < 86400) return formatTimestamp(ts, 'hh:MM')
  59. const weekdays = (() => {
  60. try {
  61. const list = i18n.t('find.weekdays', { returnObjects: true })
  62. if (Array.isArray(list)) return list
  63. } catch (_) {}
  64. return ['日', '一', '二', '三', '四', '五', '六']
  65. })()
  66. const locale = i18n ? i18n.locale : 'zh-Hans'
  67. const chineseStyle = locale.startsWith('zh') || locale === 'ja'
  68. const clock = formatTimestamp(ts, 'hh:MM')
  69. const when = new Date(normalizeTimestamp(ts))
  70. const dayGap = new Date().getDate() - when.getDate()
  71. if (seconds < 86400 * 7) {
  72. if (dayGap === 1) return `${translate('find.yesterday', '昨天')} ${clock}`
  73. if (dayGap === 2) return `${translate('find.dayBeforeYesterday', '前天')} ${clock}`
  74. const weekLabel = weekdays[when.getDay()]
  75. return chineseStyle
  76. ? `${translate('find.week', '星期')}${weekLabel} ${clock}`
  77. : `${weekLabel} ${clock}`
  78. }
  79. if (seconds >= 86400 * 7) return formatTimestamp(ts, 'mm-dd hh:MM')
  80. return formatTimestamp(ts, 'yyyy-mm-dd hh:MM')
  81. }