Skip to content

Day.js 是一个极简的 JavaScript 库,可以为现代浏览器解析、验证、操作和显示日期和时间。所有更改 Day.js 对象的 API 操作都将返回一个新的实例。这有助于防止错误和避免长时间的调试会话。 下面罗列的是我用到的 dayjs 的功能。

官网:https://day.js.org/zh-CN/

基础知识点

解析

Day.js 并没有对原生 Date.prototype 做任何修改, 而是给 Date 对象做了一层封装。 使用支持的数据格式调用 dayjs() 即可取到这个封装的对象。

Day.js 对象是不可变的,所有的 API 操作都将返回一个全新的实例。

当前时间

直接调用 dayjs() 将返回一个包含当前日期和时间的 Day.js 对象。

js
var now = dayjs()
var now = dayjs()

等同于 dayjs(new Date()) 的调用。

当没有传入参数时,参数默认值是 undefined,所以调用 dayjs(undefined) 就相当于调用 dayjs()。

Day.js 将 dayjs(null) 视为无效的输入。

string + Format

如果知道输入字符串的格式,您可以用它来解析日期。

这依赖 CustomParseFormat 插件,才能正常运行

js
dayjs.extend(customParseFormat)
dayjs('12-25-1995', 'MM-DD-YYYY')
dayjs.extend(customParseFormat)
dayjs('12-25-1995', 'MM-DD-YYYY')

如果想解析包含本地化语言的日期字符串,可以传入第三个参数。

js
require('dayjs/locale/zh-cn')
dayjs('2018 三月 15', 'YYYY MMMM DD', 'zh-cn')
require('dayjs/locale/zh-cn')
dayjs('2018 三月 15', 'YYYY MMMM DD', 'zh-cn')

最后一个参数可传入布尔值来启用严格解析模式。 严格解析要求格式和输入内容完全匹配,包括分隔符。

js
dayjs('1970-00-00', 'YYYY-MM-DD').isValid() // true
dayjs('1970-00-00', 'YYYY-MM-DD', true).isValid() // false
dayjs('1970-00-00', 'YYYY-MM-DD', 'es', true).isValid() // false
dayjs('1970-00-00', 'YYYY-MM-DD').isValid() // true
dayjs('1970-00-00', 'YYYY-MM-DD', true).isValid() // false
dayjs('1970-00-00', 'YYYY-MM-DD', 'es', true).isValid() // false

如果您不知道输入字符串的确切格式,但知道它可能是几种中的一种,可以使用数组传入多个格式。

js
dayjs('12-25-2001', ['YYYY', 'YYYY-MM-DD'], 'es', true)
dayjs('12-25-2001', ['YYYY', 'YYYY-MM-DD'], 'es', true)

支持的解析占位符列表

输入例子详情
YY01两位数的年份
YYYY2001四位数的年份
M1-12月份,从 1 开始
MM01-12月份,两位数
MMMJan-Dec缩写的月份名称
MMMMJanuary-December
D1-31月份里的一天
DD01-31月份里的一天,两位数
H0-23小时
HH00-23小时,两位数
h1-12小时, 12 小时制
hh01-12小时, 12 小时制, 两位数
m0-59分钟
mm00-59分钟,两位数
s0-59
ss00-59秒 两位数
S0-9毫秒,一位数
SS00-99毫秒,两位数
SSS000-999毫秒,三位数
Z-05:00UTC 的偏移量
ZZ-0500UTC 的偏移量,两位数
AAM PM上午 下午 大写
aam pm上午 下午 小写
Do1st... 31st带序数词的月份里的一天
X1410715640.579Unix 时间戳
x1410715640579Unix 时间戳

Date 对象

使用原生 Javascript Date 对象创建一个 Day.js 对象。

js
var d = new Date(2018, 8, 18)
var day = dayjs(d)
var d = new Date(2018, 8, 18)
var day = dayjs(d)

这将克隆 Date 对象。 对传入的 Date 对象做进一步更改不会影响 Day.js 对象,反之亦然。

验证

返回 布尔值 表示 Dayjs 的日期是否通过校验

  • 不严格的校验 只检查传入的值能否被解析成一个时间日期
    js
    dayjs('2022-01-33').isValid()
    // true, parsed to 2022-02-02
    dayjs('some invalid string').isValid()
    // false
    dayjs('2022-01-33').isValid()
    // true, parsed to 2022-02-02
    dayjs('some invalid string').isValid()
    // false
  • 严格校验 检查传入的值能否被解析,且是否是一个有意义的日期。 最后两个参数 format 和 strict 必须提供。

    这依赖 CustomParseFormat 插件,才能正常运行

    js
    dayjs('2022-02-31', 'YYYY-MM-DD', true).isValid()
    // false
    dayjs('2022-02-31', 'YYYY-MM-DD', true).isValid()
    // false

插件

在引入插件后,使用dayjs.extend(xxx)就可以使用插件,相关的实用的插件,参考官网

取值/赋值

在设计上 Day.js 的 getter 和 setter 使用了相同的 API,也就是说,不传参数调用方法即为 getter,调用并传入参数为 setter。

由于 dayjs 对象是不可变的,所有设置操作将返回一个新的 dayjs 实例。

这些 API 调用了对应原生 Date 对象的方法。

js
dayjs().second(30).valueOf() // => new Date().setSeconds(30)
dayjs().second() // => new Date().getSeconds()
dayjs().second(30).valueOf() // => new Date().setSeconds(30)
dayjs().second() // => new Date().getSeconds()

如果您处于 UTC 模式,将会调用对应的 UTC 方法。

js
dayjs.utc().second(30).valueOf() // => new Date().setUTCSeconds(30)
dayjs.utc().second() // => new Date().getUTCSeconds()
dayjs.utc().second(30).valueOf() // => new Date().setUTCSeconds(30)
dayjs.utc().second() // => new Date().getUTCSeconds()

操作

您可能需要一些方法来操作 Day.js 对象。

Day.js 支持像这样的链式调用:

js
dayjs('2019-01-25').add(1, 'day').subtract(1, 'year').year(2009).toString()
dayjs('2019-01-25').add(1, 'day').subtract(1, 'year').year(2009).toString()

Add

返回增加一定时间的复制的 Day.js 对象。

js
const a = dayjs()
const b = a.add(7, 'day')

// a -> the original value and will not change
// b -> the manipulation result
const a = dayjs()
const b = a.add(7, 'day')

// a -> the original value and will not change
// b -> the manipulation result

各个传入的单位对大小写不敏感,支持缩写和复数。 请注意,缩写是区分大小写的。

支持的单位列表

单位缩写详情
dayd
weekw
monthM
quarterQ季度 ( 依赖 QuarterOfYear 插件 )
yeary
hourh小时
minutem分钟
seconds
millisecondms毫秒
或者,也可以给 Day.js 对象增加一个 持续时间 。
js
result = dayjs().add(dayjs.duration({ days: 1 }))
console.log(dayjs.duration().days(1))
result = dayjs().add(dayjs.duration({ days: 1 }))
console.log(dayjs.duration().days(1))

Subtract

返回减去一定时间的复制的 Day.js 对象。

js
dayjs().subtract(7, 'year')
dayjs().subtract(7, 'year')

各个传入的单位对大小写不敏感,支持缩写和复数。 支持的单位列表

显示

当解析和操作完成后,您需要一些方式来展示 Day.js 对象。

Format

根据传入的占位符返回格式化后的日期。

将字符放在方括号中,即可原样返回而不被格式化替换 (例如, [MM])。

js
dayjs().format()
// 默认返回的是 ISO8601 格式字符串 '2020-04-02T08:02:17-05:00'

dayjs('2019-01-25').format('[YYYYescape] YYYY-MM-DDTHH:mm:ssZ[Z]')
// 'YYYYescape 2019-01-25T00:00:00-02:00Z'

dayjs('2019-01-25').format('DD/MM/YYYY') // '25/01/2019'
dayjs().format()
// 默认返回的是 ISO8601 格式字符串 '2020-04-02T08:02:17-05:00'

dayjs('2019-01-25').format('[YYYYescape] YYYY-MM-DDTHH:mm:ssZ[Z]')
// 'YYYYescape 2019-01-25T00:00:00-02:00Z'

dayjs('2019-01-25').format('DD/MM/YYYY') // '25/01/2019'

支持的格式化占位符列表

Difference

返回指定单位下两个日期时间之间的差异。

要获得以毫秒为单位的差异,请使用 dayjs#diff

js
const date1 = dayjs('2019-01-25')
const date2 = dayjs('2018-06-05')
date1.diff(date2) // 20214000000 默认单位是毫秒
const date1 = dayjs('2019-01-25')
const date2 = dayjs('2018-06-05')
date1.diff(date2) // 20214000000 默认单位是毫秒

要获取其他单位下的差异,则在第二个参数传入相应的单位。

js
const date1 = dayjs('2019-01-25')
date1.diff('2018-06-05', 'month') // 7
const date1 = dayjs('2019-01-25')
date1.diff('2018-06-05', 'month') // 7

默认情况下 dayjs#diff 会将结果进位成整数。 如果要得到一个浮点数,将 true 作为第三个参数传入。

js
const date1 = dayjs('2019-01-25')
date1.diff('2018-06-05', 'month', true) // 7.645161290322581
const date1 = dayjs('2019-01-25')
date1.diff('2018-06-05', 'month', true) // 7.645161290322581

支持的单位列表 各个传入的单位对大小写不敏感,支持缩写和复数。 请注意,缩写是区分大小写的。

单位缩写详情
dayd
weekwWeek of Year
quarterQQuarter
monthM月份 (一月 0, 十二月 11)
yearyYear
hourhHour
minutemMinute
secondsSecond
millisecondmsMillisecond

查询

Is Before

这表示 Day.js 对象是否在另一个提供的日期时间之前。

js
dayjs().isBefore(dayjs('2011-01-01')) // 默认毫秒
dayjs().isBefore(dayjs('2011-01-01')) // 默认毫秒

如果想使用除了毫秒以外的单位进行比较,则将单位作为第二个参数传入。 在这种情况下,会使用传入的单位以及比其范围大的单位进行比较。

js
dayjs().isBefore('2011-01-01', 'month') // compares month and year
dayjs().isBefore('2011-01-01', 'month') // compares month and year

各个传入的单位对大小写不敏感,支持缩写和复数。 支持的单位列表

Is Same

这表示 Day.js 对象是否和另一个提供的日期时间相同。

js
dayjs().isSame(dayjs('2011-01-01')) // 默认毫秒
dayjs().isSame(dayjs('2011-01-01')) // 默认毫秒

如果想使用除了毫秒以外的单位进行比较,则将单位作为第二个参数传入。

当使用第二个参数时,将会连同去比较更大的单位。 如传入 month 将会比较 month 和 year。 传入 day 将会比较 day、 month 和 year。

js
dayjs().isSame('2011-01-01', 'year')
dayjs().isSame('2011-01-01', 'year')

各个传入的单位对大小写不敏感,支持缩写和复数。 支持的单位列表

Is After

这表示 Day.js 对象是否在另一个提供的日期时间之后。

js
dayjs().isAfter(dayjs('2011-01-01')) // 默认毫秒
dayjs().isAfter(dayjs('2011-01-01')) // 默认毫秒

如果想使用除了毫秒以外的单位进行比较,则将单位作为第二个参数传入。 在这种情况下,会使用传入的单位以及比其范围大的单位进行比较。

js
dayjs().isAfter('2011-01-01', 'month') // compares month and year
dayjs().isAfter('2011-01-01', 'month') // compares month and year

各个传入的单位对大小写不敏感,支持缩写和复数。 支持的单位列表

Is Same or Before

这表示 Day.js 对象是否和另一个提供的日期时间相同或在其之前。

这依赖 IsSameOrBefore 插件,才能正常运行

js
dayjs.extend(isSameOrBefore)
dayjs().isSameOrBefore(dayjs('2011-01-01')) // 默认毫秒
dayjs.extend(isSameOrBefore)
dayjs().isSameOrBefore(dayjs('2011-01-01')) // 默认毫秒

如果想使用除了毫秒以外的单位进行比较,则将单位作为第二个参数传入。

js
dayjs().isSameOrBefore('2011-01-01', 'year')
dayjs().isSameOrBefore('2011-01-01', 'year')

各个传入的单位对大小写不敏感,支持缩写和复数。

支持的单位列表

Is Same or After

这表示 Day.js 对象是否和另一个提供的日期时间相同或在其之后。

这依赖 IsSameOrAfter 插件,才能正常运行

js
dayjs.extend(isSameOrAfter)
dayjs().isSameOrAfter(dayjs('2011-01-01')) // 默认毫秒
dayjs.extend(isSameOrAfter)
dayjs().isSameOrAfter(dayjs('2011-01-01')) // 默认毫秒

如果想使用除了毫秒以外的单位进行比较,则将单位作为第二个参数传入。

js
dayjs().isSameOrAfter('2011-01-01', 'year')
dayjs().isSameOrAfter('2011-01-01', 'year')

各个传入的单位对大小写不敏感,支持缩写和复数。 支持的单位列表

Is Between

这表示 Day.js 对象是否在其他两个的日期时间之间。

这依赖 IsBetween 插件,才能正常运行

js
dayjs.extend(isBetween)
dayjs('2010-10-20').isBetween('2010-10-19', dayjs('2010-10-25'))
// 默认毫秒
dayjs.extend(isBetween)
dayjs('2010-10-20').isBetween('2010-10-19', dayjs('2010-10-25'))
// 默认毫秒

如果想使用除了毫秒以外的单位进行比较,则将单位作为第三个参数传入。 在这种情况下,会使用传入的单位以及比其范围大的单位进行比较。

js
dayjs().isBetween('2010-10-19', '2010-10-25', 'month') // compares month and year
dayjs().isBetween('2010-10-19', '2010-10-25', 'month') // compares month and year

各个传入的单位对大小写不敏感,支持缩写和复数。

支持的单位列表

第四个参数是设置包容性。 [ 表示包含。 ( 表示排除。

要使用包容性参数,必须同时传入两个指示符。

js
dayjs('2016-10-30').isBetween('2016-01-01', '2016-10-30', null, '[)')
dayjs('2016-10-30').isBetween('2016-01-01', '2016-10-30', null, '[)')

Is a Dayjs

这表示一个变量是否为 Day.js 对象。

js
dayjs.isDayjs(dayjs()) // true
dayjs.isDayjs(new Date()) // false
dayjs.isDayjs(dayjs()) // true
dayjs.isDayjs(new Date()) // false

这和使用 instanceof 的结果是一样的:

js
dayjs() instanceof dayjs // true
dayjs() instanceof dayjs // true

帖子发布时间

在做评论之类的帖子类型的需求的时候,会在每个帖子上附带发帖时间,不同网站的显示时间的规则是有差异的。
比如:

时间显示文字示例
十分钟内刚刚发布刚刚发布
大于 10 分钟,小于 1 小时xxx 分钟前19 分钟前
大于 1 小时,不超过当日xxx 小时前4 小时前
昨天昨日 hh:mm昨日 13:44
当年MM-DD hh:mm01-31 11:26
跨年YYYY-MM-DD hh:mm2020-12-12 13:44
又如,项目另一个模块:
时间显示文字示例
:----::----::----:
十分钟内刚刚更新刚刚更新
大于 10 分钟,小于 1 小时xxx 分钟前19 分钟前
大于 1 小时,不超过当日xxx 小时前更新4 小时前更新
昨天昨日 hh:mm 更新昨日 13:44 更新
当年MM-DD hh:mm 更新01-31 11:26 更新
跨年YYYY-MM-DD hh:mm 更新2020-12-12 13:44 更新

解决思路

用多个条件分支进行判断:跨年、当日、昨日 判断方法采用 dayjs 自带的查询功能。

我最开始想到的是使用relativeTime插件,然后自定义输出内容,这样就实现了不同时间段的不同前缀和后缀的配置。但是这样有一个缺陷,这个前后缀的配置是全局的,我配置完成后,在项目另一个地方也采用这种配置前后缀的方法,就会相互影响。不清楚是否可以进行局部配置,我后续尝试一下

dayjs 自带查询功能

判断是否跨年

js
dayjs(inputTime).isSameOrBefore(dayjs(dayjs().subtract(1, 'year')))
dayjs(inputTime).isSameOrBefore(dayjs(dayjs().subtract(1, 'year')))

判断是否昨日

js
dayjs(inputTime).isSame(dayjs().subtract(1, 'day'), 'day')
dayjs(inputTime).isSame(dayjs().subtract(1, 'day'), 'day')

判断是否当日

js
dayjs(inputTime).isSame(dayjs(), 'day')
dayjs(inputTime).isSame(dayjs(), 'day')

判断是否超过一小时

js
dayjs(inputTime).isSameOrBefore(dayjs().subtract(1, 'hour'), 'hour')
dayjs(inputTime).isSameOrBefore(dayjs().subtract(1, 'hour'), 'hour')

判断是否超过 10 分钟

js
dayjs(inputTime).isSameOrBefore(dayjs().subtract(10, 'minute'), 'minute')
dayjs(inputTime).isSameOrBefore(dayjs().subtract(10, 'minute'), 'minute')

整合逻辑判断后的代码

js
function getPostPublishTime(inputTime) {
    const isOneYearBefore = dayjs(inputTime).isSameOrBefore(
        dayjs(dayjs().subtract(1, 'year'))
    )
    const isYesterday = dayjs(inputTime).isSame(
        dayjs().subtract(1, 'day'),
        'day'
    )
    const isToday = dayjs(inputTime).isSame(dayjs(), 'day')
    const isMoreThanAnHour = dayjs(inputTime).isSameOrBefore(
        dayjs().subtract(1, 'hour'),
        'hour'
    )
    const isMoreThan10Mins = dayjs(inputTime).isSameOrBefore(
        dayjs().subtract(10, 'minute'),
        'minute'
    )

    if (isOneYearBefore) {
        // 是否跨年
        return dayjs(inputTime).format('YYYY-MM-DD HH:mm')
    } else if (isYesterday) {
        // 是否昨日
        return dayjs(inputTime).format('昨日 HH:mm')
    } else if (isToday) {
        // 是否今天
        if (isMoreThanAnHour) {
            // 超过1小时
            return dayjs().diff(dayjs(inputTime), 'hour') + '小时前'
        } else if (isMoreThan10Mins) {
            // 超过10分钟
            return dayjs().diff(dayjs(inputTime), 'minute') + '分钟前'
        } else {
            // 10分钟以内
            return '刚刚发布'
        }
    } else {
        // 今年发布的
        return dayjs(inputTime).format('MM-DD HH:mm')
    }
}
function getPostPublishTime(inputTime) {
    const isOneYearBefore = dayjs(inputTime).isSameOrBefore(
        dayjs(dayjs().subtract(1, 'year'))
    )
    const isYesterday = dayjs(inputTime).isSame(
        dayjs().subtract(1, 'day'),
        'day'
    )
    const isToday = dayjs(inputTime).isSame(dayjs(), 'day')
    const isMoreThanAnHour = dayjs(inputTime).isSameOrBefore(
        dayjs().subtract(1, 'hour'),
        'hour'
    )
    const isMoreThan10Mins = dayjs(inputTime).isSameOrBefore(
        dayjs().subtract(10, 'minute'),
        'minute'
    )

    if (isOneYearBefore) {
        // 是否跨年
        return dayjs(inputTime).format('YYYY-MM-DD HH:mm')
    } else if (isYesterday) {
        // 是否昨日
        return dayjs(inputTime).format('昨日 HH:mm')
    } else if (isToday) {
        // 是否今天
        if (isMoreThanAnHour) {
            // 超过1小时
            return dayjs().diff(dayjs(inputTime), 'hour') + '小时前'
        } else if (isMoreThan10Mins) {
            // 超过10分钟
            return dayjs().diff(dayjs(inputTime), 'minute') + '分钟前'
        } else {
            // 10分钟以内
            return '刚刚发布'
        }
    } else {
        // 今年发布的
        return dayjs(inputTime).format('MM-DD HH:mm')
    }
}