moment-range
Fancy date ranges for Moment.js.
Hey there! After 5 months of inactivity, we're reviewing pull requests and issues to bring moment-range up to date. Get in touch with us in this thread if you have any feedback.
Installation
moment-range works in both the browser and node.js.
Node / NPM
Install via npm:
npm install --save moment-range
ES6:
import Moment from 'moment';
import { extendMoment } from 'moment-range';
const moment = extendMoment(Moment);
CommonJS:
const Moment = require('moment');
const MomentRange = require('moment-range');
const moment = MomentRange.extendMoment(Moment);
Browser
<script src="moment.js"></script>
<script src="moment-range.js"></script>
window['moment-range'].extendMoment(moment);
Thanks to the fine people at cdnjs, you can link to moment-range from
the cdnjs servers.
Examples
Create
Create a date range:
const start = new Date(2012, 0, 15);
const end = new Date(2012, 4, 23);
const range = moment.range(start, end);
You can also create a date range with moment objects:
const start = moment('2011-04-15', 'YYYY-MM-DD');
const end = moment('2011-11-27', 'YYYY-MM-DD');
const range = moment.range(start, end);
Arrays work too:
const dates = [moment('2011-04-15', 'YYYY-MM-DD'), moment('2011-11-27', 'YYYY-MM-DD')];
const range = moment.range(dates);
You can also create a range from an ISO 8601 time interval string:
const timeInterval = '2015-01-17T09:50:04+00:00/2015-04-17T08:29:55+00:00';
const range = moment.range(timeInterval);
You can also create a range from the start until the end of a named interval:
const date = moment('2011-04-15', 'YYYY-MM-DD');
const range = date.range('month');
You can also create open-ended ranges which go to the earliest or latest possible date:
const rangeUntil = moment.range(null, '2011-05-05');
const rangeFrom = moment.range('2011-03-05');
const rangeAllTime = moment.range();
Note that any falsy value except 0 is treated as a missing date, resulting in an open-ended range.
Note: Dates and moment objects both use a timestamp of 00:00:000 if none is
provided. To ensure your range includes any timestamp for the given end date,
use .setHours(23,59,59,999)
when constructing a Date object, or
.endOf('day')
when constructing a moment object.
rangeFromInterval
You can also create a range between an interval and a specified date. This accepts positive or negative values
for count
and the date will default to now if not provided.
const interval = 'month';
const count = 4;
const date = moment('2017-07-20');
const range1 = moment.rangeFromInterval(interval, count, date);
const range2 = moment.rangeFromInterval('month', -2, date);
Note: The date can be provided as a Date, String, or Moment.
When using a negative interval, the date provided will be set as the end of the range.
parseZoneRange
Parses an ISO 8601 time interval into a date range while
preserving the time zones using moment.parseZone.
const interval = '2015-01-17T09:50:00+03:00/2015-04-17T08:29:55-04:00';
const range = moment.parseZoneRange(interval);
range.toString();
Attributes
You can access the start and end moments of the range easily enough:
const start = new Date(2012, 0, 15);
const end = new Date(2012, 4, 23);
const range = moment.range(start, end);
range.start
range.end
Querying
Many of the following examples make use of these moments:
const a = moment('2016-03-10');
const b = moment('2016-03-15');
const c = moment('2016-03-29');
const d = moment('2016-04-01');
Adjacent
Check if two ranges are touching but not overlapping:
const range1 = moment.range(a, b);
const range2 = moment.range(b, c);
const range3 = moment.range(c, d);
range1.adjacent(range2)
range1.adjacent(range3)
Center
Calculate the center of a range:
const start = new Date(2011, 2, 5);
const end = new Date(2011, 3, 5);
const dr = moment.range(start, end);
dr.center();
Contains
Check to see if your range contains a date/moment:
const range = moment.range(a, c);
range.contains(b);
range.contains(d);
The exclusive
options is used to indicate if the end of the range should be
excluded when testing for inclusion:
range.contains(c)
range.contains(c, { exclusive: false })
range.contains(c, { exclusive: true })
Within
Find out if your moment falls within a date range:
const range = moment.range(a, c);
b.within(range);
Overlaps
Does it overlap another range?
const range1 = moment.range(a, c);
const range2 = moment.range(b, d);
range1.overlaps(range2);
Include adjacent ranges:
const range1 = moment.range(a, b);
const range2 = moment.range(b, c);
range1.overlaps(range2)
range1.overlaps(range2, { adjacent: false })
range1.overlaps(range2, { adjacent: true })
Intersect
What is the intersecting range?
const range1 = moment.range(a, c);
const range2 = moment.range(b, d);
range1.intersect(range2);
IsRange
Is it a Range?
moment.isRange(range);
moment.isRange(IamNotRange);
Manipulation
Add
Add/combine/merge overlapping or adjacent ranges.
const range1 = moment.range(a, c);
const range2 = moment.range(b, d);
range1.add(range2);
const range3 = moment.range(a, b);
const range4 = moment.range(c, d);
range3.add(range4);
Include adjacent ranges:
const range1 = moment.range(a, b);
const range2 = moment.range(b, c);
range1.add(range2);
range1.add(range2, { adjacent: false });
range1.add(range2, { adjacent: true });
Clone
Deep clone a range
const range1 = moment.range(a, d);
const range2 = range1.clone();
range2.start.add(2, 'days');
range1.start.toDate().getTime() === range2.start.toDate().getTime()
Subtract
Subtracting one range from another.
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);
range_ac.subtract(range_bc);
range_ab.subtract(range_cd);
range_bc.subtract(range_bd);
Iteration
Each of the iteration methods returns an Iterable, providing
a convenient and performant interface to iterating over your ranges by a given
period.
by
Iterate over your range by a given period. Any of the units accepted by
moment.js' add
method may be used. E.g.: 'years' | 'quarters' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds'
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 == 5
years.map(m => m.format('YYYY'))
Iteration also supports excluding the end value of the range by setting the
exclusive
option to true
.
const start = new Date(2012, 2, 1);
const end = new Date(2012, 2, 5);
const range1 = moment.range(start, end);
const acc = Array.from(range1.by('day', { exclusive: true }));
acc.length == 4
Additionally it's possible to iterate by a given step that defaults to 1
:
const start = new Date(2012, 2, 2);
const end = new Date(2012, 2, 6);
const range1 = moment.range(start, end);
let acc = Array.from(range1.by('day', { step: 2 }));
acc.map(m => m.format('DD'))
acc = Array.from(range1.by('day', { exclusive: true, step: 2 }));
acc.map(m => m.format('DD'))
byRange
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);
Iterate by another range:
const acc = Array.from(range1.by(range2));
acc.length == 5
Exclude the end value:
const acc = Array.from(range1.by(range2, { exclusive: true }));
acc.length == 4
By step:
let acc = Array.from(range1.by(range2, { step: 2 }));
acc.map(m => m.format('DD'))
acc = Array.from(range1.by(range2, { exlusive, true, step: 2 }));
acc.map(m => m.format('DD'))
reverseBy
Iterate over a range in reverse:
const range = moment.range('2012-01-01', '2015-01-01');
const acc = Array.from(range.reverseBy('years'));
acc.map(m => m.format('YYYY'))
Exclude the end value:
const range = moment.range('2012-01-01', '2015-01-01');
const acc = Array.from(range.reverseBy('years', { exclusive: true }));
acc.map(m => m.format('YYYY'))
By step:
const start = new Date(2012, 2, 2);
const end = new Date(2012, 2, 6);
const range1 = moment.range(start, end);
let acc = Array.from(range1.reverseBy('day', { step: 2 }));
acc.map(m => m.format('DD'))
acc = Array.from(range1.reverseBy('day', { exclusive: true, step: 2 }));
acc.map(m => m.format('DD'))
reverseByRange
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);
Iterate by another range in reverse:
const acc = Array.from(range1.by(range2));
acc.length == 5
acc.map(m => m.format('DD'))
Exclude the end value:
const acc = Array.from(range1.by(range2, { exclusive: true }));
acc.length == 4
acc.map(m => m.format('DD'))
By step:
let acc = Array.from(range1.reverseByRange(range2, { step: 2 }));
acc.map(m => m.format('DD'))
acc = Array.from(range1.reverseByRange(range2, { exlusive, true, step: 2 }));
acc.map(m => m.format('DD'))
Compare
Compare range lengths or add them together with simple math:
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
range1 + range2
Math.abs(range1 - range2);
Equality
Check if two ranges are the same, i.e. their starts and ends are the same:
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);
range2.isSame(range3);
range1.isEqual(range2);
range2.isEqual(range3);
Difference
The difference of the entire range given various units.
Any of the units accepted by moment.js' add
method may be used.
const start = new Date(2011, 2, 5);
const end = new Date(2011, 5, 5);
const dr = moment.range(start, end);
dr.diff('months');
dr.diff('days');
dr.diff();
Optionally you may specify if the difference should be rounded, by default it
mimics moment-js' behaviour and rounds the values:
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);
dr.diff('days')
dr.diff('days', false)
dr.diff('days', true)
#duration
is an alias for #diff
and they may be used interchangeably.
Conversion
toDate
Converts the DateRange
to an Array
of the start and end Date
objects.
const start = new Date(2011, 2, 5);
const end = new Date(2011, 5, 5);
const dr = moment.range(start, end);
dr.toDate();
toString
Converting a DateRange
to a String
will format it as an ISO 8601 time
interval:
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()
valueOf
The difference between the end date and start date in milliseconds.
const start = new Date(2011, 2, 5);
const end = new Date(2011, 5, 5);
const range = moment.range(start, end);
range.valueOf();
Running Tests
Clone this bad boy:
git clone https://git@github.com/rotaready/moment-range.git
Install the dependencies:
yarn install
Do all the things!
yarn run test
yarn run lint
Contributors
License
moment-range is UNLICENSED.