一篇moment.js使用与坑点的总结

765 阅读39分钟

1 现状和问题

moment.js 是大而全的时间处理的js时间库,现已广泛应用于数百万个项目中。它所提供的功能基本上能覆盖所有关于时间转换和解析相关的问题,而且它的API介绍清晰简单,使用起来和写法也是相当简单。但是缺点也是很明显的,这个在最后面捎带着说两句。今天主要是给大家总结一下它的一些使用方法和一些坑点。

2 moment 使用和安装

目前moment的最新版本是一年前发布的2.29.4。

安装 npm i moment.js -S 或者 yarn add moment.js -S

使用 import moment from 'moment';

3 moment 的API使用

3.1 解析方面

moment(string?) 解析成当前所在时区的时间,不管你传入参数是否是否包含offset。

moment().format()
'2023-08-07T16:57:13+08:00'
moment('2023-08-07T16:57:13-08:00').format()
'2023-08-08T08:57:13+08:00'

该字符串需符合 ISO8601 strings 格式,如果不是如何以上,将会moment('not data').isValid() -false

moment(string?).format(token?) 可以解析成你需要的格式的时间 其中toke有以下几种表示方法 

Y- year.  M-month D-day H-hour m-Minutes s-Seconds X-unix  timestamp x-Unix ms timestamp

moment().format('YYYY-MM-DD HH:mm:ss') '2023-08-07 17:26:26'

moment().format('YYYY-MM-DDTHH:mm:ss.SSS') '2023-08-07T17:27:12.378'

moment().format('YYYY-MM-DDTHH:mm:ss.SSSZ') '2023-08-07T17:27:44.665+08:00'

moment({unit:value,...}) 可以传入一个带时间单位的对象生成一个moment

moment({ year :2010, month :3, day :5, hour :15, minute :10, second :3, millisecond :123});

moment(Number)  传入毫秒级timestamp  moment.unix(Number)传入秒级timestamp

moment(1318781876406).format() '2011-10-17T00:17:56+08:00' moment.unix(1318781876).format() '2011-10-17T00:17:56+08:00' moment.unix(1318781876.721).format() '2011-10-17T00:17:56+08:00'

moment(Date) 参数为js内置对象类型

moment(new Date()).format() '2023-08-07T17:37:34+08:00'

moment(Moment) 参数为moment 类型,由于moment 数据类型是可变的,所以在某些场景下做出了更改,不想要更改原始数据,需要clone。也可以用moment().clone(),来实现。

var a = moment([2012]);
var b = moment(a);
a.year(2000);
b.year(); // 2012
2012

2.1 moment.utc 默认状态下moment是以本地时间去解析和呈现的utc时间

moment().format()
'2023-08-07T18:00:29+08:00'
moment.utc().format()
'2023-08-07T10:00:40Z'

moment.parseZone 与moment.utcOffset 作用一致  解析提供的字符串,并将生成的时刻保留在固定的时区中。将生成的 Moment 对象保留在固定偏移量时区中,并在字符串中提供偏移量。

moment().utcOffset(number| string,boolean?:false) 设置utc偏移量(其中是分钟为单位的)

当设置的参数-16 to 16时,此时输入的参数单位是以hour计算,如果不在这个范围时,将以分钟为单位计算如下例子,传入8或480得到的偏移量时一样的;

moment().utcOffset(8).utcOffset()
480
moment().utcOffset(480).utcOffset()
480

设置的参数也可以为字符串,像 "+08:00" 这种类型,比如

moment().utcOffset("+08:00").utcOffset(); 480

注意:如果需要传入是时区名称的话,比如‘America/Los_Angeles’ 就要使用moment-timezone.插件

此方法还有第二个参数,是为了显示是否保留一天中的现有时间,参数默认为false

moment().utcOffset("+09:00").format() '2023-08-08T16:11:23+09:00' moment().utcOffset("+09:00", true).format() '2023-08-08T15:11:26+09:00'

如以上例子所示,当前浏览器所在时间为15:11:26 当参数为true,时得到的时间与本地相同,但是世界时间确实不同的,我们可以通过时间戳来看一下,时间一样的话,时间戳肯定是一样的,如下举例所示,果然在世界时间是不同的两个时间。

moment('2023-08-08 16:17:25').utcOffset("+09:00").valueOf()
1691482645000
moment('2023-08-08 16:17:25').utcOffset("+09:00", true).valueOf()
1691479045000

3 Get/Set

获取小时、分钟、秒、毫秒

moment().hour(number?) 参数范围0-23 超出将会累加到day上

moment().minute(number?) 参数范围0-59 超出将会累加hour上

moment().second(number?) 参数范围0-59 超出将会累加minute上

moment().millisecond(number?) 参数范围0-999 超出将会累加second上以此类推

月里的第几天 

moment().date(number?) 参数范围1-31 超出将会累加到month上

周里的第几天

moment().day(number?)  参数范围0-7 超出将会累加另外一周

年里的第几天

moment().dayOfYear(number?) 参数范围1-366 超出将会累加另外一年

年里的第几周

moment().week(number?) 不同地区的,会不一样

获取或设置第几月

moment().month(string? | number?) 这里需要注意的是,月是从0开始的,所以1月Januarymonth()为0,以此类推。参数可以为字符串时,January、Feb。参数也可以为数字时范围是0-11 超出将会累积到另外一年。

获取或设置第几季度

moment().quarter() 范围是1-4

获取或设置年

moment().year() 范围是 -270,000 to 270,000

也可以通过get或者set来获取对应时间和设置对应时间

moment().get(unit)     unit其中可以为 --> year(y)、month(M)、date (D), hour (h), minute (m), second (s), millisecond (ms).

moment().set(unit, number) unit同get 一致

比较大小

moment.max(Moment[]| Moment[,Moment...]) 和moment.min(Moment[]| Moment[,Moment...])

操作

增加时间

moment().add(numer, unit) unit 是时间单位缩写,

moment().subtract(number,unit) 缩短时间

moment().startOf(string) 设置时间到一个单位的开始时间,下面是具体例子,注意看下划线的部分。

moment().startOf('year').format() '2023-01-01T00:00:00+08:00' moment().startOf('month').format() '2023-08-01T00:00:00+08:00' moment().startOf('quarter').format() '2023-07-01T00:00:00+08:00' moment().startOf('week').format() '2023-08-06T00:00:00+08:00' moment().startOf('day').format() '2023-08-08T00:00:00+08:00' moment().startOf('date').format() '2023-08-08T00:00:00+08:00' moment().startOf('hour').format() '2023-08-08T14:00:00+08:00' moment().startOf('minute').format() '2023-08-08T14:40:00+08:00' moment().startOf('second').format() '2023-08-08T14:41:04+08:00'

moment().endOf(string) 设置时间到一个单位的结束时间,如:

moment().endOf('day').format()
'2023-08-08T23:59:59+08:00'

展现

format 默认是ISOString 格式去展现的,参数可以传入对应的格式

moment().format(string?);

moment().diff(Moment|String|number|Date|Array,string?,boolean)  测量两个时间测差值,通常以毫秒为单位。如果以具体的单位粒度去测量差值,只需要传入第二个对应的参数即可。

moment('2023-08-09').diff(moment('2023-08-08'))
86400000
moment('2023-08-09').diff(moment('2023-08-08'), 'd')
1

第三个参数是指 是否将结果四舍五入带接近的整数的数字,默认情况下是取整的。如以下例子:

var a = moment([2008, 9]);
var b = moment([2007, 0]);
a.diff(b, 'years');       // 1
a.diff(b, 'years', true); // 1.75

时间戳

moment().valueOf() 获取毫秒级时间戳

moment().unix() 获取妙级时间戳

转换格式

转换成ISO8601String moment().toISOString() '2023-08-08T10:56:54.993Z'

转换成 js 原生Date类型  moment().toDate(); Tue Aug 08 2023 18:57:42 GMT+0800 (中国标准时间)

转换成Object moment().toObject(); {years: 2023, months: 7, date: 8, hours: 18, minutes: 59, …}

转换成数组 moment().toArray(); (7) [2023, 7, 8, 19, 0, 51, 935]

查询

moment().isBefore(Moment|String|number|Date|Array,string) 检查一个时间是否在另一个时间之前

moment('2010-10-20').isBefore('2010-10-21'); // true

如果时间粒度的限制是毫秒以外的单位,可以通过第二个参数进行传递

moment('2010-10-20').isBefore('2010-12-31', 'year'); // false

moment().isSame(Moment|String|number|Date|Array,string) 和moment().isAfter(Moment|String|number|Date|Array,string) 就表示相同或者是否在之后

moment().isSameOrBefore()  和moment().isSameOrAfter() 则表示,时间的对比<= 或者>=

moment().isBetween(Moment|String|Number|Date|Array, Moment|String|Number|Date|Array)用来判断目标时间是否一个时间范围内

moment('2010-10-20').isBetween('2010-10-19', '2010-10-25'); // true

注意:isBetween中的参数顺序有大小前后顺序,时间小的应放在最前面。

moment.isData(obj) 判断对象是不是原生js Date类型

moment.isMoment(obj) 判断对象是不是moment类型

3 webpack 打包减少moment体积 按需引入

 使用插件IgnorePlugin插件,它会移除moment的所有本地文件

使用方式 第一步在webpack plugins 配置

const webpack = require('webpack');
module.exports = {
  //...
  plugins: [
    // 忽略 moment.js的所有本地文件
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  ],
};

4 展望

moment.js 官方文档中Project Status说他们正式宣布进入维护期,已不在开发新功能了。这就意味着他们不会解决moment的痛点问题了。

1 不在添加新功能

2 不会讲API变为immutable

3 不会解决tree shaking 和宝体积的问题

4 不会进行任何重大修改(v3)

5 可能不会修复bug,特别是长期存在的已知问题

但是官方也推荐了兄弟库Day.js ,它被设计为了moment.js的缩减版本,它的api与moment极其类似,可以快读入门。