-
Notifications
You must be signed in to change notification settings - Fork 198
Description
It would be nice if we could create a range using a Duration or Object argument in addition to (or instead of) the (String, Number) format.
'Problem'
For now we can only use a String interval type with number to create a range from an interval. For example:
moment.rangeFromInterval('year', 3, date);
This works great for very simple intervals, but what if we want to create a range using a more specific interval? e.g: 3 years, 4 months, and 5 days
Suggested change
The add() function from moment already allows this kind of flexibility (function signature of add):
moment().add(Number, String);
moment().add(Duration);
moment().add(Object);
Using the same kind of format we could do something like:
moment.rangeFromInterval({'year': 3, 'month': 4, 'day': 5}, date);
or moment.rangeFromInterval(duration, date);
As of now a simple workaround is to create the second date with the add() function and use it to create a regular range as so:
moment.range(date, date.clone().add({'year': 3, 'month': 4, 'day': 5}));
or moment.range(date, date.clone().add(duration));
but it would be nice to do it directly from the rangeFromInterval() constructor instead. Not a world changing feature but would look nicer in constructor calls than adding the extra add() and clone() calls.
Execution
Knowing that rangeFromInterval() uses the add() function internally anyway, it would be quite straightforward to add the same argument flexibility. The only issue would be caused by the current argument order (interval, count, date) which would mean one of two things would have to be changed:
- Option 1: The
interval, countarguments would have to always be replaced by the equivalent Object call to always have only one argument in beforedate - Option 2: Change the argument order to place
datefirst, which would allow a variable number of trailing arguments
Both options would require refactoring, but the first one would only require adding some curly brackets and colon on existing arguments, whereas the second option would require reordering call arguments AND explicitly adding moment() when the default value of date is needed.
Examples of changes:
- Option 1:
rangeFromInterval('year', 3, date)=>rangeFromInterval({'year': 3}, date)rangeFromInterval('month', 2)=>rangeFromInterval({'month': 2})
- Option 2:
rangeFromInterval('year', 3, date)=>rangeFromInterval(date, 'year', 3)rangeFromInterval('month', 2)=>rangeFromInterval(moment(), 'month', 2)
There's also always a 3rd option which would be to create a separate function for this purpose, but the code change would be so minimal that it would barely make sense to separate it.
I personally prefer Option 1, which would maintain the same structure and readability for the user, while also requiring only a tiny implementation change of the rangeFromInterval() function.
Code Proposal
For Option 1:
/**
* Build a date range between a date (or now) and a specified interval.
* @param {Object|Duration} interval The interval to use
* @param {Moment|Date} [date=moment()] The date to use
* @return {DateRange}
*/
moment.rangeFromInterval = function(interval, date = moment()) {
if (!moment.isMoment(date)) date = moment(date);
if (!date.isValid()) throw new Error('Invalid date.');
const dateWithInterval = date.clone().add(interval);
// Handle negative interval counts by assembling the dates in chronological order.
const dates = [];
dates.push(moment.min(date, dateWithInterval));
dates.push(moment.max(date, dateWithInterval));
return new DateRange(dates);
};
For Option 2:
/**
* Build a date range between a date and a specified interval.
* @param {Moment|Date} date The date to use
* @param {String|Object|Duration} interval The type of interval or interval object to use
* @param {Number} [count=1] The number of intervals (positive or negative)
* @return {DateRange}
*/
moment.rangeFromInterval = function(date, interval, count = 1) {
if (!moment.isMoment(date)) date = moment(date);
if (!date.isValid()) throw new Error('Invalid date.');
if (typeof interval === 'object') {
const dateWithInterval = date.clone().add(interval);
} else {
const dateWithInterval = date.clone().add(count, interval);
}
// Handle negative interval counts by assembling the dates in chronological order.
const dates = [];
dates.push(moment.min(date, dateWithInterval));
dates.push(moment.max(date, dateWithInterval));
return new DateRange(dates);
};