英文 | https://blog.logrocket.com/javascript-date-libraries/从理论上讲,向您的应用程序添加工作日期似乎总是一件容易的事,除非您尝试这样做。总是会遇到麻烦,无论是在尝试使您的网站国际化时的时区问题,还是以所需格式显示日期的问题,甚至试图对它们进行一些基本的算术运算。不幸的是,JavaScript并没有真正准备好处理日期。这有点讽刺,因为它确实有一个日期对象,我们可以用它来进行一些基本的操作。我说JavaScript还没准备好是什么意思呢?我的意思是,这个对象的API不够丰富,不能满足我们的日常需求,它没有您所期望的来自这样一个对象的高级支持来处理国际化、时区支持等问题。这就是我将要回顾的库发挥作用的地方。这七个日期库在JavaScript的日期对象之上添加了抽象层,并使其实际有用。Moment.js是Node.js和Vanilla JavaScript(使其与浏览器兼容)的最古老,最知名的日期相关库之一。因此,他们有时间使其与许多最常见的Web开发实用程序兼容,例如:Bower
requireJS
Nuget
Browsify
TypeScript
meteor
它提供了一个很棒的API,而不需要修改Date对象的原始原型,相反,他们在Date对象周围创建了一个包装器对象,以确保不破坏原始名称空间。作为一个简单的示例,下面是如何设置当前日期。加上7天,然后减去一个月,最后设置年份和时间值。所有这些都在一行易于阅读的代码中,就像这样:moment().add(7, 'days').subtract(1, 'months').year(2009).hours(0).minutes(0).seconds(0);
它们支持20多个不同的地区,因此如果您想解决国际化问题,最好使用moment.js。一个特定库的成功和整体实用性的一个很好的衡量标准是检查存在于该库周围的插件/扩展生态系统。就moment.js而言,有22个官方支持的版本。乍一看,这可能不是一个很大的数目,但是如果您考虑所有这些库都是围绕一个对象(Date对象)设计的,那么拥有超过20个插件无疑是一个很好的迹象。你可以在他们的文档页面上找到完整的插件列表,但其中一些最有趣的是:它允许你像Twitter一样格式化你的日期和时间(注意他们是如何用一个字母缩写他们的时间日期的,比如1h表示“1小时前”,2d表示“2天前”)。moment().subtract(6, 'days').twitterShort();
当你试图以一种特定的方式显示你的日期时,你写了多少次日期格式?类似于YYYY-MM-dd或类似的变体。但是我们总是手动编写它,然后库相应地格式化日期。这个插件不是格式化日期,而是解析一个日期字符串并返回实际的格式供您重用。var format = moment.parseFormat('Thursday, February 6th, 2014 9:20pm');
这个特别的插件非常适合将动态行为添加到您的格式化逻辑中,例如,使格式化成为动态的,并允许您的用户输入一个日期示例,而不是让他们学习如何配置自己的格式。这实际上是对JavaScript的setTimeInterval和setTimeout函数的完全重写,允许您混合moment的语法并创建一个更强大的计时器。var timer = moment.duration(5, "seconds").timer({loop: true},
function() {
setInterval(function() {
}, 5000)
date-fns被宣传为大量的数据库,它试图提供比Moment.js更好的体验。它的API非常广泛,有140多个不同的与时间相关的函数,它们的创建者和贡献者希望让您从使用Moment切换到自己的时间管理解决方案。所有函数都是按文件分组的,允许您导入您需要的东西,而不必为您真正使用的两个方法而使项目变得臃肿。这对于需要优化每js行字节数的前端开发人员特别有用,因为每个字节都很重要。对于Node.js开发人员来说,这对于保持导入仍然很有用,并且需要更有条理。
与其他库(查看您的Moment.js)不同,date-fns返回的日期对象是不可变的,这有助于避免不必要的修改和无数个小时的调试。
FP子模块提供了一组与FP相关的功能,可以帮助您轻松地通过几行代码来组成复杂的行为。
它们总共支持57个不同的地区,所以如果您的目标是国际化,这里是另一个不错的选择!
他们有TypeScript和流支持。
最后但并非最不重要的是,他们的文档非常详细,这是我从一个库中一直欣赏的东西,特别是那些具有如此广泛的API的文档。
让我们快速运行一些代码示例,让您了解这个库的独特之处。const { addYears, formatWithOptions } = require('date-fns/fp')
const { es } = require('date-fns/locale')
const addFiveYears = addYears(5)
const dateToString = formatWithOptions({ locale: es }, 'd MMMM yyyy')
const dates = [
new Date(2017, 0, 1),
new Date(2017, 1, 11),
new Date(2017, 6, 2)
]
const toUpper = arg => String(arg).toUpperCase()
const formattedDates = dates.map(addFiveYears).map(dateToString).map(toUpper)
这个例子展示了我上面提到的两点:每个文件的功能机制,使您只需要实际需要的位(两个导入都利用了示例中的位)和功能编程辅助函数。请注意,如何使用这两个导入的函数(addYears和formatWithOptions)在最后一行(这两个函数以及toUpper匿名函数)中组成整个过程。关于代码的一个简短说明:尽管它与库主页上显示的示例相同,但我必须使它与Node.js兼容。luxon是一个非常有趣的项目,因为如果您查看它的url,它就位于moment.js项目下,那么它为什么会存在呢?你可以从作者自己那里读到整个故事,但主要的要点是,它试图成为一个更好的版本,然而:与它的前辈(如果我们可以调用Moment.js)相比,Luxon的主要区别之一是,所有对象都是不可变的,你可能会说Moment.js做出了一个糟糕的决定,让他们的对象发生了变化,而社区里的每个人都想尽办法去解决这个问题。var m1 = moment();
var m2 = m1.add(1, 'hours');
m1.valueOf() === m2.valueOf();
var d1 = DateTime.local();
var d2 = d1.plus({ hours: 1 });
d1.valueOf() === d2.valueOf();
在上面的例子中,您可以看到这种不同的作用,而对于moment.js(第一个例子),您会遇到那种“问题”(这里有引号,因为问题只在您不注意的情况下才会发生),因为add方法会变异m1,而不是返回m2上的新值,luxon的api会阻止您遇到这个问题,因为plus在d2上返回一个新对象,而不是修改d1。与moments .js的另一个重大区别是,国际化是基于来自浏览器的Intl API。本质上,这意味着:如果您是Moment.js用户,您可能会对其他更改感兴趣,所以一定要查看他们的文档。DayJS试图成为Moment.js的缩小版(人们在这里看到这些模式了吗?)。对于声称拥有Moment.js相同API并将其文件大小减少97%的库,可以说些什么。没错,Moment.js完整压缩文件的总重量为67,9Kb,而DayJS压缩文件的大小仅为2Kb。太疯狂了,但是他们支持国际化,插件和其他所有功能。当每个人都在使用下划线时,你可以把dayjs看作lodash。lodash出现在图片中,提出了一个类似的声明,他们有一个非常相似的api,占用空间更小,他们总是试图节省尽可能多的字节,以减少加载时间。就像Moment.js一样,该库有大约20个正式支持的插件,您可以在其文档中查看。最后,虽然这个库似乎是它所声称的一切,开发人员开始采用它,如下面的下载趋势图所示:MomentJS的月下载量仍然很高,因为它已经有8年多的历史了(与DayJS 1年多的历史相比)。这将需要一些时间,但如果MomentJS不适应(也许Luxon可能是一个选择?)它将最终被这个新来的家伙所取代。由于API实际上与MomentJS是一样的,所以展示代码示例是没有意义的,如果您需要特殊的东西,只需检查他们的官方文档,如果您担心加载时间和数据使用(对于移动web开发人员来说是一个大问题),则切换到DayJS。为了稍微改变一下,下一个库并不是作为MomentJS的替代品创建的(我知道,令人震惊!),相反,它只有一个任务,而且做得非常好。MS的目标是将任何类型的日期格式转换为毫秒并返回。这是一个非常狭窄的用例,我知道,但你知道,日期转化为毫秒都有其优点,尤其是如果你想做比较,和某些形式的算术运算(这很容易添加1000毫秒数,比说你需要添加1秒约会对象)。ms('2 days')
ms('1d')
ms('10h')
ms('2.5 hrs')
ms('2h')
ms('1m')
ms('5s')
ms(60000)
ms(2 * 60000)
ms(-3 * 60000)
考虑到这个库每周有超过3000万次的下载,我认为它涵盖了一个非常具体但又很常见的用例。所以,如果这是你的时间相关逻辑所需要的全部,考虑检查一下。另一个通用时间管理库,旨在取代MomentJS和其他从上述名单。它试图通过避免对Date对象技术使用相同的包装来区别于MomentJS等其他技术,相反,它从头实现了整个逻辑。这样更好吗?这个问题还没有定论,它可能会给维护人员更多的空间来处理基本概念,并以日期对象无法处理的方式扭曲它们。也就是说,这个库比momentjs(大约40kb)要小一些,而momentjs离dayjs的2kb很小。但是,它确实提供了不变性(冲击)和特定于域的类,从而帮助开发人员编写更清晰的OOP代码。js-joda的一个非常有趣的特性是它提供的可扩展性。由于不可变对象实际上没有setter,这个库提供了with方法,它返回一个带有新值集的新对象。如果不设置一个值,而是设置一个特殊对象,那么可以扩展获取值的方式。
var nextOrSameEvenDay = {
adjustInto: function(t) {
return t.dayOfMonth() % 2 === 0 ? t : t.plusDays(1);
}
};
LocalDateTime.parse("2012-12-23T12:00").with(nextOrSameEvenDay);
LocalDate.parse("2012-12-24").with(nextOrSameEvenDay);
基本上,我们正在解析一个日期字符串(没什么特别的),然后使用自定义对象(请注意特殊的into方法),我们正在添加一个非常随机的行为,但仍然有效。如果您正在寻找这种灵活性,我个人会推荐该库,否则,上面已经介绍了一些更好的方法。我不能漏掉一个叫做时空的时间库,这一个有一个非常有趣的重点:时区。是的,它提供了一个类似于momentjs的API,还提供了不可变的对象(哎呀!),但是这个库的主要目的是帮助您轻松地处理时区。请记住,在处理任何类型的国际化开发时,时区往往是主要问题之一,所以这看起来很有前途。const spacetime = require('spacetime')
let d = spacetime('March 1 2012', 'America/New_York')
d = d.time('4:20pm')
console.log(d.time())
d = d.goto('America/Los_Angeles')
console.log(d.time())
那非常容易,不是吗?不过,有关时空的说明是,它不像Luxon那样依赖Intl API,因此该库本身并不是真正的轻量级设备,大约40Kb。话虽如此,时区仍然遵循IANA命名约定,这对于标准化代码非常有用。关于我们最后一个图书馆的另一件很酷的事情是,它可以在适用的情况下遵守夏令时,所以会发生以下情况:d = d.goto('Eastern Time') // "America/New_York"
d = d.goto('PST') // automatically becomes 'PDT' in the summer
还有其他的方法,如赛季返回当前季节为特定日期(夏季/冬季/返回春季/秋季)以及hasDST inDST它返回一个时区是否使用日光节约时间和如果它活跃在特定日期/时间配置。最后,扩展时空非常简单,这有助于添加额外的功能,如:spacetime.extend({
isHappyHour: function() {
return this.hour() === 16
}
})
d = d.time('4:15pm')
console.log(d.isHappyHour())
这是处理日期和时间的7种最常见的JavaScript库。鉴于在线上已有的现有信息和使用情况数据,如果您必须处理非常复杂的与日期时间相关的功能。那么我的建议是使用MomentJS(因为它是经过时间验证的经典库),或者只是尝试全新的和 更快的模型:DayJS,相同的API,更好的对象管理和更小的占地面积。另一方面,如果您有非常特定的需求,请考虑使用ms或Spacetime。