过滤器中不能直接用 dayjs.relativeTime 插件,因 iOS Safari 不支持 ISO 格式(如 "2026-04-21T10:30:00Z"),需先将输入统一转为毫秒时间戳再调用;推荐使用 uni-dateformat 组件并正确配置 threshold 数组。

过滤器里不能直接用 dayjs 的相对时间插件
很多人装了 dayjs,又引入了 relativeTime 插件,然后在过滤器里写 dayjs(value).fromNow() —— 这在 H5 端可能正常,但真机(尤其 iOS)会报 Invalid date 或静默返回空字符串。根本原因是:过滤器接收的 value 类型不确定(可能是字符串、时间戳、Date 对象),而 dayjs 在不同平台对 ISO 字符串(如 "2026-04-21T10:30:00Z")解析行为不一致;iOS Safari 不支持带 T 和 Z 的格式,必须先归一化。
正确做法是:所有输入统一转成时间戳再传给 dayjs:
- 先判断类型:
typeof value === 'string'时,用replace(/[-T]/g, '/')把-和T换成/和空格,再用Date.parse()提取毫秒数 - 如果是数字,检查是否为 10 位(秒级)还是 13 位(毫秒级),10 位要乘以 1000
- 最终只传入毫秒数给
dayjs(),避免任何字符串解析歧义
filters 中实现“刚刚/几分钟前”的最小可行代码
别封装太重,一个过滤器只做一件事:输入任意时间源 → 输出相对描述。下面这段可直接塞进页面的 filters 选项里(注意:需提前在 main.js 全局引入并使用 relativeTime 插件):
relativeTime: function (value) {
if (!value) return ''
let ts
if (typeof value === 'number') {
ts = value > 9999999999 ? value : value * 1000 // 区分秒/毫秒
} else if (typeof value === 'string') {
ts = Date.parse(value.replace(/[-T]/g, '/').replace(/\.\d+Z$/, ''))
} else if (value instanceof Date) {
ts = value.getTime()
} else {
return ''
}
return dayjs(ts).fromNow()
}
调用方式:{{ item.create_time | relativeTime }}。它能处理后端返回的 "2026-09-03T15:22:10Z"、1756939330(秒)、1756939330123(毫秒)三种常见格式。
uni-dateformat 组件比过滤器更稳,但 threshold 必须写对
如果你只是想显示“刚刚”“2小时前”这类,别自己写过滤器,直接用官方 <uni-dateformat>。它内部用 Date.now() 计算差值,不依赖本地时区,也不走 JS Date 构造逻辑,iOS 兼容性好得多。
关键陷阱:很多人以为 :threshold="false" 或 :threshold="0" 就能关掉人性化显示,其实无效。它只认数组:
- 完全禁用相对时间(只走
format):写成:threshold="[0, 0]" - 让“1小时内”显示为“xx分钟前”,“1小时外”回退到自定义格式:写成
:threshold="[0, 3600000]" -
threshold[0]是“刚刚”的上限(毫秒),threshold[1]是“xx分钟前”的上限(毫秒)
示例:<uni-dateformat :date="item.time" :threshold="[0, 3600000]" format="MM-dd hh:mm"></uni-dateformat>
真正难的不是写函数,而是统一所有时间输入源
你可能在 API 响应里同时遇到:1756939330(秒)、"2026/09/03 15:22:10"、"2026.09.03"、甚至 "2026年9月3日"。过滤器里每次重复写类型判断和归一化,既冗余又容易漏 case。
建议把解析逻辑抽成工具函数(如 parseDate(value)),在 utils/date.js 里统一维护,然后在过滤器、methods、computed 里都 import 它。不要为了“省一次 import”而在每个过滤器里 copy-paste 解析代码——一旦后端改格式,你得改遍全项目。


















