moment-range 介绍及使用

6 阅读12分钟

moment-range

Node / NPM 环境 通过 npm 安装: npm install --save moment-range

ES6 语法引入

js

import Moment from 'moment';
import { extendMoment } from 'moment-range';

const moment = extendMoment(Moment);

TypeScript 语法引入

js

import * as Moment from 'moment';
import { extendMoment } from 'moment-range';

const moment = extendMoment(Moment);

CommonJS 语法引入

js

const Moment = require('moment');
const MomentRange = require('moment-range');

const moment = MomentRange.extendMoment(Moment);

浏览器环境

直接通过脚本标签引入:

html

预览

<script src="moment.js"></script>
<script src="moment-range.js"></script>

初始化扩展:

js

window['moment-range'].extendMoment(moment);

使用示例

创建日期范围

创建基础日期范围:

js

const start = new Date(2012, 0, 15);
const end   = new Date(2012, 4, 23);
const range = moment.range(start, end);

也可通过 moment 实例创建:

js

const start = moment('2011-04-15', 'YYYY-MM-DD');
const end   = moment('2011-11-27', 'YYYY-MM-DD');
const range = moment.range(start, end);

传入数组形式的起止时间也支持:

js

const dates = [moment('2011-04-15', 'YYYY-MM-DD'), moment('2011-11-27', 'YYYY-MM-DD')];
const range = moment.range(dates);

通过 ISO 8601 时间间隔字符串创建:

js

const timeInterval = '2015-01-17T09:50:04+00:00/2015-04-17T08:29:55+00:00';
const range = moment.range(timeInterval);

基于指定时间,创建其到某时间单位结束的范围:

js

const date = moment('2011-04-15', 'YYYY-MM-DD');
const range = date.range('month'); // 2011-04-15 至 2011-04-30

创建开区间范围(起始 / 结束为最早 / 最晚时间):

js

const rangeUntil = moment.range(null, '2011-05-05'); // 最早时间 至 2011-05-05
const rangeFrom = moment.range('2011-03-05'); // 2011-03-05 至 最晚时间
const rangeAllTime = moment.range(); // 最早时间 至 最晚时间

注意:除 0 外,任何假值都会被视为缺失的日期,从而创建开区间范围。

提示:若未指定时间戳,Date 和 moment 实例都会默认使用 00:00:000。如果需要让范围包含结束日期的所有时间戳,创建 Date 实例时请使用 .setHours(23,59,59,999),创建 moment 实例时请使用 .endOf('day')

rangeFromInterval

根据指定的时间单位数量基准日期创建范围,count 支持正负值,若未传入基准日期则默认使用当前时间

js

const interval = 'month';
const count = 4;
const date = moment('2017-07-20');

const range1 = moment.rangeFromInterval(interval, count, date);  // 2017-07-20 至 2017-11-20
const range2 = moment.rangeFromInterval('month', -2, date);      // 2017-05-20 至 2017-07-20

注意:基准日期可传入 Date 实例、字符串或 Moment 实例;当 count 为负值时,传入的基准日期会作为范围的结束时间

parseZoneRange

4.0.0 版本中已废弃:为遵循命名规范,该方法已被 rangeFromISOString 替代。

rangeFromISOString

ISO 8601 时间间隔字符串转换为日期范围,同时通过 moment.parseZone 保留时区信息:

js

const interval = '2015-01-17T09:50:00+03:00/2015-04-17T08:29:55-04:00';
const range = moment.rangeFromISOString(interval);

range.toString(); // '2015-01-17T09:50:00+03:00/2015-04-17T08:29:55-04:00'

属性访问

可直接访问范围的起始和结束 moment 实例:

js

const start = new Date(2012, 0, 15);
const end   = new Date(2012, 4, 23);
const range = moment.range(start, end);

range.start  // 起始 moment 实例
range.end    // 结束 moment 实例

范围查询

以下示例均基于以下 moment 实例:

js

const a = moment('2016-03-10');
const b = moment('2016-03-15');
const c = moment('2016-03-29');
const d = moment('2016-04-01');
判断相邻

检查两个范围是否相接但不重叠

js

const range1 = moment.range(a, b);
const range2 = moment.range(b, c);
const range3 = moment.range(c, d);

range1.adjacent(range2) // true
range1.adjacent(range3) // false
计算中心时间

计算当前范围的中心时间戳

js

const start = new Date(2011, 2, 5);
const end   = new Date(2011, 3, 5);
const range = moment.range(start, end);

range.center(); // 1300622400000
判断包含

检查范围是否包含指定的日期 /moment 实例,默认包含起始和结束日期

js

const range = moment.range(a, c);

range.contains(a); // true
range.contains(b); // true
range.contains(c); // true
range.contains(d); // false

可通过 excludeStartexcludeEnd 配置,控制是否排除起始 / 结束日期:

js

const range = moment.range(a, c);

range.contains(a); // true
range.contains(a, { excludeStart: true }); // false
range.contains(c); // true
range.contains(c, { excludeEnd: true }); // false

4.0.0 版本中已废弃:原 exclusive 配置用于控制是否排除起止日期:提示:设置 { excludeStart: true, excludeEnd: true } 可实现与原 exclusive: true 相同的效果。

js

range.contains(c); // true
range.contains(c, { exclusive: false }); // true
range.contains(c, { exclusive: true }); // false
判断归属

检查指定的 moment 实例是否位于当前范围内

js

const range = moment.range(a, c);

b.within(range); // true
判断重叠

检查两个范围是否存在重叠

js

const range1 = moment.range(a, c);
const range2 = moment.range(b, d);
range1.overlaps(range2); // true

可配置是否将相邻范围视为重叠:

js

const range1 = moment.range(a, b);
const range2 = moment.range(b, c);

range1.overlaps(range2)                      // false
range1.overlaps(range2, { adjacent: false }) // false
range1.overlaps(range2, { adjacent: true })  // true
获取交集

获取两个范围的交集范围,若无交集则返回 null

js

const range1 = moment.range(a, c);
const range2 = moment.range(b, d);
range1.intersect(range2); // moment.range(b, c)
判断是否为范围对象

检查指定对象是否为 moment-range 范围实例:

js

moment.isRange(range); // true
moment.isRange(IamNotRange); // false

范围操作

合并范围

合并重叠或相邻的两个范围,若无重叠 / 相邻则返回 null

js

const range1 = moment.range(a, c);
const range2 = moment.range(b, d);
range1.add(range2); // moment.range(a, d)

const range3 = moment.range(a, b);
const range4 = moment.range(c, d);
range3.add(range4); // null

可配置是否合并相邻范围

js

const range1 = moment.range(a, b);
const range2 = moment.range(b, c);

range1.add(range2); // null
range1.add(range2, { adjacent: false }); // null
range1.add(range2, { adjacent: true }); // moment.range(a, c)
深克隆范围

对当前范围进行深克隆,修改克隆后的范围不会影响原范围:

js

const range1 = moment.range(a, d);

const range2 = range1.clone();
range2.start.add(2, 'days');

range1.start.toDate().getTime() === range2.start.toDate().getTime() // false
对齐时间区间

将范围的起始和结束时间对齐到指定的时间单位,补全不完整的时间区间:

js

const start = moment('2018-01-25 17:05:33');
const end = moment('2018-01-28 06:10:00');

const range1 = moment.range(start, end);
const range2 = range1.snapTo('day'); // 2018-01-25T00:00:00 -> 2018-01-28T23:59:59

range1.diff('days'); // 2
range2.diff('days'); // 3
减去范围

从当前范围中减去指定的子范围,返回剩余的范围数组(若无剩余则返回 [null]):

js

const range_ab = moment.range(a, b);
const range_bc = moment.range(b, c);
const range_cd = moment.range(c, d);
const range_ad = moment.range(a, d);
range_ad.subtract(range_bc); // [moment.range(a, b), moment.range(c, d)]
range_ac.subtract(range_bc); // [moment.range(a, b)]
range_ab.subtract(range_cd); // [moment.range(a, b)]
range_bc.subtract(range_bd); // [null]

范围迭代

所有迭代方法都会返回一个可迭代对象,可便捷、高效地按指定规则遍历范围。

按时间单位迭代

按指定的时间单位遍历范围,支持 moment.js add 方法的所有单位:'years' | 'quarters' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds'

js

const range = moment.range('2010-01-01', '2015-01-01');

// 按月份遍历
for (let month of range.by('month')) {
  month.format('YYYY-MM-DD');
}

// 按年份遍历并转为数组
const years = Array.from(range.by('year'));
years.length == 6 // true
years.map(m => m.format('YYYY')) // ['2010', '2011', '2012', '2013', '2014', '2015']

设置 excludeEnd: true排除范围的最后一个时间片段

js

const range = moment.range('2018-01-01 00:00', '2018-01-01 05:30');

const hours = Array.from(range.by('hour', { excludeEnd: true }));
hours.length == 5 // true
hours.map(m => m.format('HH:mm')) // ['00:00', '01:00', '02:00', '03:00', '04:00']

支持通过 step 设置迭代步长,默认步长为 1

js

const start  = new Date(2012, 2, 2);
const end    = new Date(2012, 2, 6);
const range1 = moment.range(start, end);

// 步长为2,包含结束时间
let acc = Array.from(range1.by('day', { step: 2 }));
acc.map(m => m.format('DD')) // ['02', '04', '06']

// 步长为2,排除结束时间
acc = Array.from(range1.by('day', { excludeEnd: true, step: 2 }));
acc.map(m => m.format('DD')) // ['02', '04']

对于不完整的时间区间,可结合 snapTo() 方法补全后再迭代:

js

const start = moment("2017-01-01T13:30:00");
const end = moment("2017-01-05T01:45:12");
const r1 = moment.range(start, end);
const r2 = r1.snapTo('day'); // 补全为完整的日期区间

Array.from(r1.by('days')).map(m => m.format('DD')); // ['01', '02', '03', '04']
Array.from(r2.by('days')).map(m => m.format('DD')); // ['01', '02', '03', '04', '05']

4.0.0 版本中已废弃:原 exclusive 配置用于控制是否排除结束时间;提示:设置 { excludeEnd: true } 可实现与原 exclusive: true 相同的效果。

按指定范围迭代

另一个范围的时长遍历当前范围:

js

const start = new Date(2012, 2, 1); // 1号
const two   = new Date(2012, 2, 2); // 2号
const end   = new Date(2012, 2, 5); // 5号
const range1 = moment.range(start, end);
const range2 = moment.range(start, two); // 时长为1天的范围

基础迭代:

js

const acc = Array.from(range1.byRange(range2));

acc.length == 5 // true
acc.map(m => m.format('DD')) // ['01','02','03','04','05']

排除最后一个时间片段:

js

const acc = Array.from(range1.byRange(range2, { excludeEnd: true }));

acc.length == 4 // true
acc.map(m => m.format('DD')) // ['01','02','03','04']

设置迭代步长:

js

let acc = Array.from(range1.byRange(range2, { step: 2 }));
acc.map(m => m.format('DD')) // ['01', '03', '05']

acc = Array.from(range1.byRange(range2, { excludeEnd: true, step: 2 }));
acc.map(m => m.format('DD')) // ['01', '03']

4.0.0 版本中已废弃:原 exclusive 配置用于控制是否排除结束时间;提示:设置 { excludeEnd: true } 可实现与原 exclusive: true 相同的效果。

按时间单位反向迭代

按指定的时间单位倒序遍历范围:

js

const range = moment.range('2012-01-01', '2015-01-01');
const acc = Array.from(range.reverseBy('years'));
acc.map(m => m.format('YYYY')) // ['2015', '2014', '2013', '2012']

设置 excludeStart: true排除范围的起始时间片段

js

const range = moment.range('2012-01-01', '2015-01-01');
const acc = Array.from(range.reverseBy('years', { excludeStart: true }));
acc.map(m => m.format('YYYY')) // ['2015', '2014', '2013']

设置反向迭代步长:

js

const start  = new Date(2012, 2, 2);
const end    = new Date(2012, 2, 6);
const range1 = moment.range(start, end);

// 步长为2,包含起始时间
let acc = Array.from(range1.reverseBy('day', { step: 2 }));
acc.map(m => m.format('DD')) // ['06', '04', '02']

// 步长为2,排除起始时间
acc = Array.from(range1.reverseBy('day', { excludeStart: true, step: 2 }));
acc.map(m => m.format('DD')) // ['06', '04']

4.0.0 版本中已废弃:原 exclusive 配置用于控制是否排除起始时间;提示:设置 { excludeStart: true } 可实现与原 exclusive: true 相同的效果。

按指定范围反向迭代

另一个范围的时长倒序遍历当前范围:

js

const start = new Date(2012, 2, 1);
const two   = new Date(2012, 2, 2);
const end   = new Date(2012, 2, 5);
const range1 = moment.range(start, end);
const range2 = moment.range(start, two); // 时长为1天的范围

基础反向迭代:

js

const acc = Array.from(range1.reverseByRange(range2));

acc.length == 5 // true
acc.map(m => m.format('DD')) // ['05', '04', '03', '02', '01']

排除起始时间片段:

js

const acc = Array.from(range1.reverseByRange(range2, { excludeStart: true }));

acc.length == 4 // true
acc.map(m => m.format('DD')) // ['05', '04', '03', '02']

设置反向迭代步长:

js

let acc = Array.from(range1.reverseByRange(range2, { step: 2 }));
acc.map(m => m.format('DD')) // ['05', '03', '01']

acc = Array.from(range1.reverseByRange(range2, { excludeStart: true, step: 2 }));
acc.map(m => m.format('DD')) // ['05', '03']

4.0.0 版本中已废弃:原 exclusive 配置用于控制是否排除起始时间;提示:设置 { excludeStart: true } 可实现与原 exclusive: true 相同的效果。

范围比较

可通过简单的数学运算比较范围长度计算总时长 / 时长差

js

const range1 = moment.range(new Date(2011, 2, 5), new Date(2011, 3, 15));
const range2 = moment.range(new Date(1995, 0, 1), new Date(1995, 12, 25));

range2 > range1 // true,比较范围时长

range1 + range2 // 两个范围的总时长(毫秒)

Math.abs(range1 - range2); // 两个范围的时长差(毫秒)
判断相等

检查两个范围是否完全相同(起始和结束时间均一致):

js

const range1 = moment.range(new Date(2011, 2, 5), new Date(2011, 3, 15));
const range2 = moment.range(new Date(2011, 2, 5), new Date(2011, 3, 15));
const range3 = moment.range(new Date(2011, 3, 5), new Date(2011, 6, 15));

range1.isSame(range2); // true
range2.isSame(range3); // false

range1.isEqual(range2); // true(isEqual 是 isSame 的别名)
range2.isEqual(range3); // false
计算时间差

计算范围的总时长,支持 moment.js add 方法的所有时间单位,默认返回毫秒数

js

const start = new Date(2011, 2, 5);
const end   = new Date(2011, 5, 5);
const range = moment.range(start, end);

range.diff('months'); // 3
range.diff('days');   // 92
range.diff();         // 7945200000(默认返回毫秒)

可配置是否保留小数,默认会像 moment.js 一样截断小数取整:

js

const d1 = new Date(Date.UTC(2011, 4, 1));
const d2 = new Date(Date.UTC(2011, 4, 5, 12));
const range = moment.range(d1, d2);

range.diff('days')        // 4(默认截断)
range.diff('days', false) // 4(显式截断)
range.diff('days', true)  // 4.75(保留小数)

#duration#diff别名,二者可互换使用。

格式转换

toDate 转换为日期数组

将日期范围转换为包含起止 Date 实例的数组

js

const start = new Date(2011, 2, 5);
const end   = new Date(2011, 5, 5);
const range = moment.range(start, end);

range.toDate(); // [new Date(2011, 2, 5), new Date(2011, 5, 5)]
toString 转换为字符串

将日期范围转换为ISO 8601 时间间隔格式的字符串:

js

const start = '2015-01-17T09:50:04+00:00';
const end   = '2015-04-17T08:29:55+00:00';
const range = moment.range(moment.utc(start), moment.utc(end));

range.toString() // '2015-01-17T09:50:04+00:00/2015-04-17T08:29:55+00:00'
valueOf 获取时间戳差

返回范围结束时间与起始时间的毫秒差(与直接调用 diff() 效果一致):

js

const start = new Date(2011, 2, 5);
const end   = new Date(2011, 5, 5);
const range = moment.range(start, end);

range.valueOf(); // 7945200000