Luxon入门 —— 一个轻量的日期库

4,174 阅读7分钟

几个灵魂拷问

  • 你是否一看到日期和时间戳就有生理不适
  • 你是否到现在都分不清楚 getDay 和 getDate 有什么区别
  • 你是否还记得 getMonth 是从 0 开始还是从 1 开始,其他 API 呢
  • 虽然场景比较少,但是怎么切换时区显示呢

Luxon 是什么

Luxon 是一个轻量的 JavaScript 日期库(压缩后的大小约为 21.1KB),它隶属于 Moment 项目组,在 moment 基础上优化了 API 的语义、时区,统一了 API 的命名规范,让开发者更容易上手,让你彻底摆脱原生难用的方法,从此爱上日期时间

DateTime.local().setZone('America/New_York').minus({ weeks: 1 }).endOf('day').toISO()

Luxon 的语法非常的语义化,甚至不用了解他的 API 语法也能看懂上面的输出结果

诞生背景

Luxon 的作者名为 icambron,他也是 Moment 的开发者之一,在开发的过程中,他逐渐有了些新想法想去优化,但是却未能成功,一是时区,Moment 的底层架构使得这部分功能难以改善,二是链式调用,这部分与 Moment 现有的 API 完全不兼容,icambron 为了不影响现有的 Moment 功能,于是就新开了一个项目,在吸收了 Moment 的设计思想之上,创造了 Luxon,他断断续续写了 2 年,最终被 Moment 队伍接受,并将 Luxon 放在了 Moment 组织之下,而现在,Moment 虽然还在维护,但是官方已经声明,他们了解了 Moment 的缺陷,虽然项目没有死,但是也不会有更新的东西了(It is not dead, but it is indeed done.),而首推的其他库就是 Luxon

Moment.js 的缺点

  1. 因为底层设计的缺陷,体积越来越大,尤其是加上国际化之后,甚至需要通过特定的 webpack 插件优化
  2. 时区的功能和本身的库独立,需要另行加载,而且文档不够清晰,对使用时区的开发者,非常不友好
  3. 本身设计的对象是可变的,这样在使用中会造成很多困扰,例如:
    let a = moment()
    let b = a.add(10, 'days')
    console.log(a === b)  // true
    

Day.js

这是国人开发的一个日期插件,也是 Moment 推荐的另一个小而美库,他解决了上述的一些缺陷,同时保持着 Moment 一致的 API 规范,可以方便的迁移过去,且有完善的中文文档,是很多国人开发者的不二选择

Luxon 安装

npm install --save luxon

其他还有 CDN 加载,RequireJS 加载等多种方式,因为 npm 安装普适性较高,这里就不一一列举了,如果需要可以去官网检索

Luxon 快速上手

下面通过一些简单的例子,展示一下 Luxon 都有哪些功能

import { DateTime, Interval, Duration } from 'luxon'

// 获取当前时间
DateTime.local()

// 格式化时间 '2020 Dec 10'
DateTime.local().format('yyyy LLL dd')

// 加减运算
DateTime.local().plus({ hours: 2, minutes: 30 })

// 表示一个时间段
Interval.fromDateTimes(dt1, dt2)

// 表示一个时间周期,这个功能一般会结合 Interval 和 DateTime 的运算使用
const dur = Duration.fromObject({hours: 2, minutes: 7})
DateTime.local().plus(dur)
Interval.fromDateTimes(dt1, dt2).toDuration()

// Interval 和 Duration 的区别主要是一个是具体的时间(哪天到哪天),一个是时间段(多少小时)

// 国际化 '2020 12月 10'
DateTime.local().setLocale('en-US').toFormat('yyyy LLL dd')

// 时区
DateTime.local().setZone('America/Los_Angeles')

上述有些方法是静态方法,例如: DateTime.local(), Interval.merge(),有些是实例方法:例如:dt.format(), dt.plus() 接下来展开说明 Luxon 的各部分 API

DateTime

DateTime 可以生成一个无法被改变的日期对象,通过这个对象可以进行一系列的变化,例如:生成新的日期,格式化显示日期等

  • 创建 DateTime 对象
    DateTime.local()
    DateTime.fromJSDate()
    DateTime.fromObject({ year: 2020, month: 10, day: 5 })
    DateTime.fromFormat('2020-12-10', 'yyyy-LL-dd')
    DateTime.fromSQL()
    ...
    
  • 获取日期对象基本属性
    const dt = DateTime.local()
    dt.year
    dt.month
    dt.day
    ...
    
  • 转换日期
    DateTime.local().plus({ days: 1 })
    DateTime.local().set({ hour: 8, minute: 30 })
    DateTime.local().endOf('day')
    ...
    
  • 按指定格式输出日期
    DateTime.local().toFormat('yyyy-LL-dd')
    DateTime.local().plus({ days: 1 }).toJSDate()
    ...
    

以下部分 API 和 DateTime 有异曲同工之处,因此省略,只列出一些特殊的接口

Duration

Duration 代表一个时间段,例如:2 小时 30 分,这个时间段可以用来页面展示,例如:2 小时前,也可以用来转换现有的 DateTime 对象,例如:在现有基础上加 2 小时

  • 创建 Duration 对象
    Duration.fromObject({ hour: 2, minute: 30 })
    ...
    
  • 获取基本属性
  • 转换 Duration
  • 按指定格式输出

Duration 也可以结合 DateTime 操作日期的转换,例如:

const dur = Duration.fromObject({ hour: 2 })
const dt = DateTime.local()
dt.plus(dur)  // 在当前时间增加 2 小时

Interval

Interval 是一个具体的时间段,例如:2020 年 12 月 10 日 至 2020 年 12 月 14 日,可能乍一看不知道用在什么地方,但是不要急,看完 API 之后你就会对他有个合适的应用场景

  • 创建 Interval 对象
    // Interval 内部需要两个 DateTime 对象,start 和 end
    // 这里需要传两个 DateTime 对象,标明 开始 和 结尾
    Interval.fromDateTimes(dt1, dt2)
    // 也可以传一个 DateTime 和 Duration 对象
    Interval.after(startDt, dur)
    
  • 一些 Interval 特殊数据
    • 获取 Interval 的长度,例如:两个日期中间有多少天、多少秒,length 和 count 的区别是:length 表示两个日期中间跨度有几天,是一个精确的数字,而 count 则不太精确,表示这两个日期中有几个完整的天
      const dt1 = DateTime.fromObject({ day: 10, hour: 12 })
      const dt2 = DateTime.fromObject({ day: 14 })
      const it = Interval.fromDateTimes(dt1, dt2)
      it.length('days') // 3.5
      it.count('days')  // 5
      
    • 判断某个日期是否在给定的两个日期之间
      it.contains(somedt)
      
  • 转换 Interval
    // 返回不同的时间段的并集
    Interval.merge([ it1, it2 ])
    // 返回不同的时间段的交集
    Interval.xor([ it1, it2 ])
    
  • 比较 Interval
    it.equals(otherit)
    // 是否和其他时间段有交集
    it.overlaps(otherit)
    // 是否包含其他时间段
    it.engulfs(otherit)
    // 是否本时间段的 end 是其他时间段的 start
    it.abutsStart(otherit)
    // 是否本时间段的 start 是其他时间段的 end
    // 这几个函数可以想象成 大于等于、小于等于 功能
    it.abutsEnd(otherit)
    
  • 按指定格式输出
    // 这里 separator 选项可以设置两个日期之间的连接符,例如:2020 ~ 2023 中间的 ~
    it.toFormat('yyyy-LL-dd', { separator: '~' })
    // 直接输出为 Duration 对象
    it.toDuration('days') 
    

国际化

Luxon 国际化用的是原生的 API 接口 Intl,因此无需引入其他文件,默认会根据系统语言自动展示

// 通过 DateTime 设置国家
DateTime.fromISO('2020-12-12', { locale: 'fr' })
DateTime.local().setLocale('en_US')
// 全局设置
Settings.defaultLocale = 'fr'

时区

设置时区无需引入其他的文件,默认会根据系统所在的时区自动选择,有多种设置方式

// 通过字符串设置
DateTime.fromFormat('2020-12-20T09:10:23 Europe/Paris', 'yyyy-MM-dd"T"HH:mm:ss z', { setZone: true})
// DateTime 对象设置
DateTime.local().setZone('America/Los_Angeles')
// 全局设置
Setting.defaultZoneName = 'Asia/Tokyo'
// 获取 DateTime 的其他时区,而不改变原有的对象
const local = DateTime.local()
const rezone = local.setZone('America/Los_Angeles', { keepLocalTime: true })
local.valueOf() === rezone.valueOf() // false

其他类

  • Setting

    可以展示全局设置、也可以进行一些全局设置,如上面所述,全局的时区、全局的语言、全局的数字显示字符(1、2、3 显示为 一、二、三)等等

  • Info

    Info 类用来展示系统静态信息,例如当前的月份格式化字符(一月、二月)等等

总结

通过上面的概览,大家可以看出 Luxon 的 API 语言非常的规范,例如:国际化都用 locale 这个单词表示,而且语义很明确,例如:setZone、toJSDate、fromObject等等,而且根据 API 名称还可以简单的了解到传参需要的数据格式,例如:fromObject 可以传一个对象参数,fromDateTimes(注意这里有一个 s),可以传两个 DateTime 格式的数据,而且官方的文档阅读起来也非常的有条理(虽然目前还没有中文),API 文档也列举的很详细,所有你想用到的方法和属性都会列举出来,不像某些国内的文档(例如:小程序文档),某些功能在这个页面分类里,某些在另一个页面,当然这也和作者只有一个人有关系,总之,这款小而美的日期库,非常值得一用,并且用过之后你一定会喜欢上它