// 堆代码 duidaima.com import moment from 'moment' // 61k (gzipped:19.k) function Relative(props) { const timeString = moment(props.date).fromNow() return <> {timeString} </> }可是,你竟然用一个大小为 20k (还是压缩过的,没压缩 61k)的包,只用来做日期的转换? really? 你想要的只是进行一个日期上的转换啊。
function nativeGetRelativeTime(unit = 'days', amount = 2) { return `in ${amount} ${unit}s` }别,请别这么做。虽然相对时间暂时看起来像是一个简单的问题,但你应该要意识到相对时间有很多复杂的问题需要解决,比如:
function nativeGetRelativeTime(locale, unit, amount) { if (locale === 'zh') { const isPlural = amount !== 1 && amount !== -1 const isPast = amount < 0 if (amount === 1 && unit === 'day') return '明天' if (amount === -1 && unit === 'day') return '昨天' if (amount === 1 && unit === 'year') return '明年' if (isPast) { return `${amount} ${unit}${isPlural ? 's' : ''} 前` } return `in ${amount} ${day}${isPlural ? 's' : ''}` } }
但是,还是请你别这么做。因为这看起来似乎变得复杂了。而我向你推荐的一个内置对象能帮助你解决相对时间的问题。
Intl.RelativeTimeFormat重申一遍,当你遇到这些情况时,要记住,目前现代前端中已经有有很多解决常见问题的内置解决方案了,可以方便的进行使用。而面对本文提到的相对时间问题,我要说的就是 Intl.RelativeTimeFormat 这个对象。
我先用下面一段代码来样式它的便捷性:
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) rtf.format(1, 'day') // 'tomorrow' rtf.format(-2, 'year') // '2 years ago' rtf.format(10, 'minute') // 'in 10 minutes'而且,它能很好地处理不同时区的问题:
const rtf = new Intl.RelativeTimeFormat('es-ES', { numeric: 'auto' }) rtf.format(1, 'day') // 'mañana' rtf.format(-2, 'year') // 'hace 2 años' rtf.format(10, 'minute') // 'dentro de 10 minutos'你甚至可以只传入navigator.language 作为第一个参数:
const rtf = new Intl.RelativeTimeFormat( navigator.language // ✅ )除此之外,Intl.RelativeTimeFormat 支持的单位包括: "year", "quarter", "month", "week", "day", "hour", "minute", 和 "second"
function Relative(props) { const timeString = getRelativeTimeString(props.date) return <> {timeString} </> }接着,我们使用 Intl.RelativeTimeFormat 对象来实现 getRelativeTimeString 函数:
export function getRelativeTimeString( date: Date | number, lang = navigator.language ): string { const timeMs = typeof date === "number" ? date : date.getTime(); const deltaSeconds = Math.round((timeMs - Date.now()) / 1000); const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity]; const units: Intl.RelativeTimeFormatUnit[] = ["second", "minute", "hour", "day", "week", "month", "year"]; const unitIndex = cutoffs.findIndex(cutoff => cutoff > Math.abs(deltaSeconds)); const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1; const rtf = new Intl.RelativeTimeFormat(lang, { numeric: "auto" }); return rtf.format(Math.floor(deltaSeconds / divisor), units[unitIndex]); }date-fns
new Intl.DateTimeFormat('en-US').format(new Date()) // -> 1/23/2023 new Intl.DateTimeFormat('en-US', { dateStyle: 'full' }).format(new Date()) // -> Monday, January 23, 2023 new Intl.DateTimeFormat('en-US', { timeStyle: 'medium' }).format(new Date()) // -> 4:17:23 PM new Intl.DateTimeFormat('en-US', { dayPeriod: 'short', hour: 'numeric' }).format(new Date()) // -> 4 in the afternoonIntl.NumberFormat
new Intl.NumberFormat('en', { style: 'currency', currency: 'USD' }).format(123456.789) // -> $123,456.79 new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(123456.789) // -> 123.456,79 € new Intl.NumberFormat('pt-PT', { style: 'unit', unit: 'kilometer-per-hour' }).format(50)); // -> 50 km/h (16).toLocaleString('en-GB', { style: 'unit', unit: 'liter', unitDisplay: 'long', })); // -> 16 litres目前,所有主流浏览器以及 Node.js 和 Deno 都支持 Intl.RelativeTimeFormat。如果你还在使用像 momentJs 这样的大型数据处理库,不妨考虑考虑Intl.RelativeTimeFormat, Intl.DateTimeFormat 这些对象,能不能帮你解决你面临的问题。