forked from segmentio/chunk-date-range
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
178 lines (157 loc) · 4.06 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
var Dates = require('date-math');
var DAYS = {
week: 7,
month: 31,
// TODO do we care about leap years? This is a rough estimate to break the chunks
year: 365
};
var OFFSETS = {
day: addDays
// TODO ADD month, year, week .. maybe more
};
/**
* Module exports
*
* @param {Date} start
* @param {Date} end
* @param {Number|String} chunks amount of chunks to split into, or duration
* @return {Array} array
*
* {
* start: Date,
* end: Date
* } ...
*/
module.exports = function(options) {
var start = options.start;
var end = options.end;
var chunks = options.chunks;
var offset = options.offset;
start = new Date(start);
end = new Date(end);
return _offset(
typeof chunks === 'number'
? number(start, end, chunks)
: duration(start, end, chunks),
offset
);
};
function _offset(chunks, offset) {
if (!offset) return chunks;
var offsetFn = OFFSETS[offset.duration];
var by = offset.by;
if (!offsetFn || by == null) return chunks;
// sometimes your chunks should be offset where the next start should be incremented from
// the last end
/*
Where:
[
{ start: 'February 13, 2018', end: 'March 7, 2018' },
{ start: 'March 7, 2018', end: 'March 31, 2018' }
];
Need to be:
[
{ start: 'February 13, 2018', end: 'March 7, 2018' },
{ start: 'March 8, 2018', end: 'March 31, 2018' }
];
*/
var nextStart;
for (var i in chunks) {
var chunk = chunks[i];
if (nextStart) {
chunk.start = nextStart;
}
nextStart = offsetFn(by, chunk.end);
}
return chunks;
}
/**
* Return an array of new start and end dates split into the number of chunks
*
* @param {Date} start
* @param {Date} end
* @param {Number} chunks number of chunks to split into
*/
function number(start, end, size) {
var diff = end - start;
var interval = diff / size;
var dates = range(size).map(function(i) {
var base = +start + i * interval;
var floor = new Date(Math.floor(base));
var ceil = new Date(Math.floor(base + interval));
return entry(floor, ceil);
});
// make sure we still end at the right time with rounding errors
dates[dates.length - 1].end = end;
return dates;
}
/**
* @param {Date} closerToNow (usually now)
* @param {Date} past
*/
function daysBetween(closerToNow, past) {
return Math.round(Math.abs(closerToNow - past) / 8.64e7);
}
/**
* Chunk the dates based upon `duration`
*
* @param {Date} start
* @param {Date} end
* @param {String} duration 'day', 'week', 'year', etc.
*/
function duration(start, end, duration) {
var days = DAYS[duration];
if (days) {
var slices = Math.ceil(daysBetween(end, start) / days);
return number(start, end, slices);
}
var math = Dates[duration];
if (!math) throw new Error('unsupported duration ' + duration);
var roundStart = math.ceil(start);
var roundEnd = math.floor(end);
var diff = roundEnd < roundStart ? 0 : math.diff(roundStart, roundEnd);
if (!diff) return [entry(start, end)]; // they are on the same interval
var arr = [];
if (+start !== +roundStart) arr.push(entry(start, roundStart));
for (var i = roundStart; i < roundEnd; i = math.shift(i, 1)) {
arr.push(entry(i, math.shift(i, 1)));
}
if (+roundEnd !== +end) arr.push(entry(roundEnd, end));
return arr;
}
/**
* Create a new entry in our array
*
* @param {Date} start
* @param {Date} end
* @return {Object}
*/
function entry(start, end) {
return { start: start, end: end };
}
/**
* Return an array of size len from 0-len
*
* @param {Number} len
* @return {Array} arr
*/
function range(len) {
var arr = [];
for (var i = 0; i < len; i++) arr.push(i);
return arr;
}
function getDay(time) {
return time.getDate();
}
function fromDays(days, _day) {
var day = _day ? new Date(_day) : new Date();
return new Date(day.setDate(getDay(day) - days));
}
function addDays(days, day) {
return fromDays(days * -1, day);
}
module.exports.fromDays = fromDays;
module.exports.addDays = addDays;
module.exports.daysBetween = daysBetween;
// EXPOSE BUT PROTECT
module.exports.DAYS = Object.assign({}, DAYS);