什么,你还在用 momentJs 处理相对时间
我想,下面这段代码,你是不是在开发中常常这样使用来计算距离现在过去了多长时间:
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`
}
别,请别这么做。虽然相对时间暂时看起来像是一个简单的问题,但你应该要意识到相对时间有很多复杂的问题需要解决,比如:
- 缩写:一般不会显示 “1天前”? 一般会显示 “昨天”、“明天” 或 “明年” 这样的词
- 将来和过去: 比如我们不会显示“在 -2 天内”,而是显示 “2天前”
还可能存在其他问题,例如时区问题。这些复杂的问题一旦来到,往往开发者会采用像 momentJs 和 dayjs 这样的库来解决问题。虽然,下面这段代码也可以试着解决你的问题:
function nativeGetRelativeTime(locale, unit, amount) {if (locale === 'zh') {const isPlural = amount !== 1 && amount !== -1const isPast = amount < 0if (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"
现在,回到我们最初的例子,我们用 Intl.RelativeTimeFormat 来改写一下:
首先,我们先写一个简单的包装函数来处理相对时间的转换:
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]);
}
Using date-fns
但是如果你不想自己的解决方案,那么也有一个开源的解决方案。date-fns 是一个很棒的 JavaScript 日期工具库,每个日期都支持以 树摇 的方式单独导出。
其中,date-fns 中内置了一个 intlFormatDistance 函数,它是 Intl.RelativeTimeFormat 的一个小包装器,这个函数做的正是我们需要的。并且,它的大小在2kb以下。
看下面的代码,是不是代码简单了许多:

Intl.DateTimeFormat
除此之前,Intl.DateTimeformat 还提供格式化日期和时间:
new Intl.DateTimeFormat('en-US').format(new Date())
// -> 1/23/2023new Intl.DateTimeFormat('en-US', {dateStyle: 'full'
}).format(new Date())
// -> Monday, January 23, 2023new Intl.DateTimeFormat('en-US', {timeStyle: 'medium'
}).format(new Date())
// -> 4:17:23 PMnew Intl.DateTimeFormat('en-US', {dayPeriod: 'short', hour: 'numeric'
}).format(new Date())
// -> 4 in the afternoon
Intl.NumberFormat
同时,Intl.NumberFormat 这个对象还能为你格式化数字:
new Intl.NumberFormat('en', { style: 'currency', currency: 'USD'
}).format(123456.789)
// -> $123,456.79new 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 这些对象,能不能帮你解决你面临的问题。

这里是编程轨迹,下篇文章再见~
相关文章:
什么,你还在用 momentJs 处理相对时间
我想,下面这段代码,你是不是在开发中常常这样使用来计算距离现在过去了多长时间: import moment from moment // 61k (gzipped:19.k) function Relative(props) {const timeString moment(props.date).fromNow()return <>{timeString…...
三维模型 工程图
飞机 Crankshaft飞机发动机手动冲压机包装成型机械-充填机械设备10数控等离子切割机床铜线缠绕机机床-磨床08机床-磨床04(附工程图)机床-车床数字纤维缠绕机机械臂液压钳机床-车床06挤出机机械手-09机械手模型库六柴油发动机中央空调机柜空调机机床-钻床三维设计电脑服务器机箱…...
我用ChatGPT写2023高考语文作文(二):全国乙卷
2023年 全国乙卷 适用地区:河南、江西、甘肃、青海、内蒙古、宁夏、新疆、陕西 吹灭别人的灯,并不会让自己更加光明;阻挡别人的路,也不会让自己行得更远。 “一花独放不是春,百花齐放春满园。”如果世界上只有一种花朵…...
java版本工程项目管理系统平台源码,助力工程企业实现数字化管理
鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管…...
代码随想录第55天
1.判断子序列: 动态规划五部曲分析如下: 确定dp数组(dp table)以及下标的含义 dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。 注意这里是判断s是否…...
算法设计与分析(填空专题)
文章目录 填空题填空题 设有一稀疏图 G,则 G 采用 邻接表 存储较省空间。 算法的时间复杂性是指算法中 元运算 执行次数。 分治法的基本思想是将一个规模为 n 的问题分解为与原问题 相同 的 k 个规模较小且互相独立的子问题。 贪心算法中每次做出的贪心选择都是 当前的 最优选…...
Ubuntu22.04 K8s1.27.2
Ubuntu22.04 && K8s1.27.2 1. 服务器配置 IpServerMEM192.168.56.11k8smaster6G192.168.56.16k8snode14G192.168.56.17k8snode24G 2. 获取源 $ sudo apt-get update $ sudo apt-get install -y apt-transport-https ca-certificates curl# packages.cloud.google.c…...
卡尔曼滤波与组合导航原理(十二)扩展卡尔曼滤波:EKF、二阶EKF、迭代EKF
文章目录 一、多元向量的泰勒级数展开二、扩展Kalman滤波三、二阶滤波四、迭代EKF滤波 一、多元向量的泰勒级数展开 { y 1 f 1 ( X ) f 1 ( x 1 , x 2 , ⋯ x n ) y 2 f 2 ( X ) f 2 ( x 1 , x 2 , ⋯ x n ) ⋮ y m f m ( X ) f m ( x 1 , x 2 , ⋯ x n ) \left\{\begin{…...
基于蒙特卡洛模拟法的电动汽车充电负荷研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
自学黑客【网络安全】,一般人我劝你还是算了吧
一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员(以编程为基础的学习)再开始学习 我在之前的回答中,我都一再强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而…...
编程中的心理策略:如何从错误中学习并实现自我成长
在日复一日的工作中,我们免不了会产生一些失误,会因此感到沮丧和失望。但如何正确地对待和处理这些失误才是最重要的,它直接影响到我们的工作表现和个人成长。 一、面对失误而带来的指责和沮丧的策略 在程序设计领域,我们经常面临…...
Rocket面试(五)Rocketmq发生流量控制的情况有哪些?
在使用rocketmq过程中总能看见一下异常 [TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: 206ms, size of queue: 5这是因为Rocketmq出发了流量控制。 触发流量控制就是为了防止Broker压力过大挂掉。主要分为Broker流控,Consu…...
Tableau招聘信息数据可视化
获取的招聘信息数据为某招聘网站发布的大数据及数据分析相关岗位,对其他计算机相关岗位的招聘信息数据分析也有一定的参考价值。因为所获取的招聘信息数据数量只有1万左右,实际的招聘信息数量肯定不止1万,所以可能会与实际信息有一定的误差。…...
游戏服务器开发指南(八):合理应对异常
大家好!我是长三月,一位在游戏行业工作多年的老程序员,专注于分享服务器开发相关的文章。 本文是通用程序设计主题下的第二篇。这个主题主要探讨如何编写高效、健壮、易读的游戏业务代码,每篇从一个小点切入。本次讨论的要点是&a…...
【g】聚类算法之K-means算法
聚类算法是一种无监督学习方法,它将相似的数据样本划分为一组,同时将不相似的数据样本划分为另一组。这个过程由计算机自动完成,不需要任何人为的干预。 K-means算法是一种经典的聚类算法,它的主要思想是把数据集分成k个簇&#…...
scala内建控制结构
一、条件表达式 (一)语法格式 - if (条件) 值1 else 值2(二)执行情况 条件为真,结果是值1;条件为假,结果是值2。如果if和else的返回结果同为某种类型,那么条件表达式结果也是那种类…...
Linux SSH命令实战教程,提升你的服务器管理基本功!
前言 大家好,又见面了,我是沐风晓月,本文是专栏【linux基本功-基础命令实战】的第62篇文章。 专栏地址:[linux基本功-基础命令专栏] , 此专栏是沐风晓月对Linux常用命令的汇总,希望能够加深自己的印象&am…...
【Python】Python进阶系列教程-- Python3 CGI编程(二)
文章目录 前言什么是CGI网页浏览CGI架构图Web服务器支持及配置第一个CGI程序HTTP头部CGI环境变量GET和POST方法使用GET方法传输数据简单的表单实例:GET方法使用POST方法传递数据通过CGI程序传递checkbox数据通过CGI程序传递Radio数据通过CGI程序传递 Textarea 数据通…...
do..while、while、for循环反汇编剖析
1、循环语句重要特征提取 循环语句最重要的特点就是执行的过程中会往上跳!!! 箭头往上跳的一般都是循环语句,比如下面的for循环: 2、do..while语句反汇编 #include<iostream> using namespace std; #pragma …...
【代码随想录】刷题Day53
1.最长公共子序列 1143. 最长公共子序列 和之前的一道题目的区别就是这个子序列不需要每个字符相邻。那么条件就变成两种了,一种是当前的字符相同,一种是不同。相同跟之前的条件一样;不同则需要继承上次比较的较大值。if (text1[i - 1] tex…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架
1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...
