@js-joda/extra 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/js-joda-extra.esm.js +1991 -22
- package/dist/js-joda-extra.esm.js.map +1 -1
- package/dist/js-joda-extra.js +1994 -20
- package/dist/js-joda-extra.js.map +1 -1
- package/dist/js-joda-extra.min.js +2 -2
- package/package.json +3 -3
- package/src/DayOfMonth.js +54 -54
- package/src/DayOfYear.js +48 -49
- package/src/Interval.js +93 -99
- package/src/LocalDateRange.js +750 -0
- package/src/OffsetDate.js +1279 -0
- package/src/Quarter.js +587 -0
- package/src/YearQuarter.js +1177 -0
- package/src/YearWeek.js +1026 -0
- package/src/_init.js +10 -0
- package/src/assert.js +2 -2
- package/src/js-joda-extra.js +10 -0
- package/src/plug.js +10 -0
- package/typings/js-joda-extra.d.ts +234 -2
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper & Michał Sobkiewicz
|
|
3
|
+
* @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
|
|
4
|
+
* @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ArithmeticException, DateTimeException, DateTimeParseException, IllegalArgumentException, LocalDate, Period } from '@js-joda/core';
|
|
8
|
+
|
|
9
|
+
// TODO: hm... is this a good idea?? copied from joda currently, could we add a js-joda-utils module??
|
|
10
|
+
import { requireInstance, requireNonNull } from './assert';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The day after the MIN date.
|
|
14
|
+
*/
|
|
15
|
+
const MINP1 = LocalDate.MIN.plusDays(1);
|
|
16
|
+
/**
|
|
17
|
+
* The day before the MAX date.
|
|
18
|
+
*/
|
|
19
|
+
const MAXM1 = LocalDate.MAX.minusDays(1);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A range of local dates.
|
|
23
|
+
*
|
|
24
|
+
* A `LocalDateRange` represents a range of dates, from a start date to an end date.
|
|
25
|
+
* Instances can be constructed from either a half-open or a closed range of dates.
|
|
26
|
+
* Internally, the class stores the start and end dates, with the start inclusive and the end exclusive.
|
|
27
|
+
* The end date is always greater than or equal to the start date.
|
|
28
|
+
*
|
|
29
|
+
* The constants `LocalDate.MIN` and `LocalDate.MAX` can be used
|
|
30
|
+
* to indicate an unbounded far-past or far-future. Note that there is no difference
|
|
31
|
+
* between a half-open and a closed range when the end is `LocalDate.MAX`.
|
|
32
|
+
* Empty ranges are allowed.
|
|
33
|
+
*
|
|
34
|
+
* No range can end at `LocalDate.MIN` or `LocalDate.MIN.plusDays(1)`.
|
|
35
|
+
* No range can start at `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`.
|
|
36
|
+
* No empty range can exist at `LocalDate.MIN` or `LocalDate.MAX`.
|
|
37
|
+
*
|
|
38
|
+
* Date ranges are not comparable. To compare the length of two ranges, it is
|
|
39
|
+
* generally recommended to compare the number of days they contain.
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
export class LocalDateRange {
|
|
43
|
+
/**
|
|
44
|
+
* function overloading for {@link LocalDateRange.of}
|
|
45
|
+
* - if called with `LocalDate` and `LocalDate`, {@link LocalDateRange._ofLocalDateLocalDate} is executed,
|
|
46
|
+
* - if called with `LocalDate` and `Period`, {@link LocalDateRange._ofLocalDatePeriod} is executed,
|
|
47
|
+
* - otherwise throws IllegalArgumentException.
|
|
48
|
+
*
|
|
49
|
+
* @param {LocalDate} startInclusive
|
|
50
|
+
* @param {LocalDate|Period} endExclusiveOrPeriod
|
|
51
|
+
* @return {LocalDateRange}
|
|
52
|
+
*/
|
|
53
|
+
static of(startInclusive, endExclusiveOrPeriod) {
|
|
54
|
+
if (startInclusive instanceof LocalDate && endExclusiveOrPeriod instanceof LocalDate) {
|
|
55
|
+
return LocalDateRange._ofLocalDateLocalDate(startInclusive, endExclusiveOrPeriod);
|
|
56
|
+
}
|
|
57
|
+
if (startInclusive instanceof LocalDate && endExclusiveOrPeriod instanceof Period) {
|
|
58
|
+
return LocalDateRange._ofLocalDatePeriod(startInclusive, endExclusiveOrPeriod);
|
|
59
|
+
}
|
|
60
|
+
const messageParts = [];
|
|
61
|
+
if (!(startInclusive instanceof LocalDate)) {
|
|
62
|
+
messageParts.push(`startInclusive must be an instance of LocalDate but is ${startInclusive.constructor.name}`);
|
|
63
|
+
}
|
|
64
|
+
if (!(endExclusiveOrPeriod instanceof LocalDate || endExclusiveOrPeriod instanceof Period)) {
|
|
65
|
+
messageParts.push(`endExclusiveOrPeriod must be an instance of LocalDate or Period but is ${endExclusiveOrPeriod.constructor.name}`);
|
|
66
|
+
}
|
|
67
|
+
throw new IllegalArgumentException(messageParts.join(' and '));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
//-----------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Obtains a half-open range of dates, including the start and excluding the end.
|
|
73
|
+
*
|
|
74
|
+
* The range includes the start date and excludes the end date, unless the end is `LocalDate.MAX`.
|
|
75
|
+
* The end date must be equal to or after the start date.
|
|
76
|
+
* This definition permits an empty range located at a specific date.
|
|
77
|
+
*
|
|
78
|
+
* The constants `LocalDate.MIN` and `LocalDate.MAX` can be used
|
|
79
|
+
* to indicate an unbounded far-past or far-future.
|
|
80
|
+
*
|
|
81
|
+
* The start inclusive date must not be `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`.
|
|
82
|
+
* The end inclusive date must not be `LocalDate.MIN` or `LocalDate.MIN.plusDays(1)`.
|
|
83
|
+
* No empty range can exist at `LocalDate.MIN` or `LocalDate.MAX`.
|
|
84
|
+
*
|
|
85
|
+
* @param {LocalDate} startInclusive - the inclusive start date, not null
|
|
86
|
+
* @param {LocalDate} endExclusive - the exclusive end date, not null
|
|
87
|
+
* @return {LocalDateRange} the half-open range, not null
|
|
88
|
+
* @throws {DateTimeException} if the end is before the start,
|
|
89
|
+
* or the start date is `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`,
|
|
90
|
+
* or the end date is `LocalDate.MIN` or `LocalDate.MIN.plusDays(1)`
|
|
91
|
+
* @protected
|
|
92
|
+
*/
|
|
93
|
+
static _ofLocalDateLocalDate(startInclusive, endExclusive) {
|
|
94
|
+
requireNonNull(startInclusive, 'startInclusive');
|
|
95
|
+
requireNonNull(endExclusive, 'endExclusive');
|
|
96
|
+
requireInstance(startInclusive, LocalDate, 'startInclusive');
|
|
97
|
+
requireInstance(endExclusive, LocalDate, 'endExclusive');
|
|
98
|
+
return new LocalDateRange(startInclusive, endExclusive);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Obtains an instance of `LocalDateRange` from the start and a period.
|
|
103
|
+
*
|
|
104
|
+
* The end date is calculated as the start plus the duration.
|
|
105
|
+
* The period must not be negative.
|
|
106
|
+
*
|
|
107
|
+
* The constant `LocalDate.MIN` can be used to indicate an unbounded far-past.
|
|
108
|
+
*
|
|
109
|
+
* The period must not be zero or one day when the start date is `LocalDate.MIN`.
|
|
110
|
+
*
|
|
111
|
+
* @param {LocalDate} startInclusive - the inclusive start date, not null
|
|
112
|
+
* @param {Period} period - the period from the start to the end, not null
|
|
113
|
+
* @return {LocalDateRange} the range, not null
|
|
114
|
+
* @throws {DateTimeException} if the end is before the start,
|
|
115
|
+
* or if the period addition cannot be made
|
|
116
|
+
* @throws {ArithmeticException} if numeric overflow occurs when adding the period
|
|
117
|
+
* @protected
|
|
118
|
+
*/
|
|
119
|
+
static _ofLocalDatePeriod(startInclusive, period) {
|
|
120
|
+
requireNonNull(startInclusive, 'startInclusive');
|
|
121
|
+
requireNonNull(period, 'period');
|
|
122
|
+
requireInstance(startInclusive, LocalDate, 'startInclusive');
|
|
123
|
+
requireInstance(period, Period, 'period');
|
|
124
|
+
if (period.isNegative()) {
|
|
125
|
+
throw new DateTimeException('Period must not be zero or negative');
|
|
126
|
+
}
|
|
127
|
+
return new LocalDateRange(startInclusive, startInclusive.plus(period));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Obtains a closed range of dates, including the start and end.
|
|
132
|
+
*
|
|
133
|
+
* The range includes the start date and the end date.
|
|
134
|
+
* The end date must be equal to or after the start date.
|
|
135
|
+
*
|
|
136
|
+
* The constants `LocalDate.MIN` and `LocalDate.MAX` can be used
|
|
137
|
+
* to indicate an unbounded far-past or far-future. In addition, an end date of
|
|
138
|
+
* `LocalDate.MAX.minusDays(1)` will also create an unbounded far-future range.
|
|
139
|
+
*
|
|
140
|
+
* The start inclusive date must not be `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`.
|
|
141
|
+
* The end inclusive date must not be `LocalDate.MIN`.
|
|
142
|
+
*
|
|
143
|
+
* @param {LocalDate} startInclusive - the inclusive start date, not null
|
|
144
|
+
* @param {LocalDate} endInclusive - the inclusive end date, not null
|
|
145
|
+
* @return {LocalDateRange} the closed range
|
|
146
|
+
* @throws {DateTimeException} if the end is before the start,
|
|
147
|
+
* or the start date is `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`,
|
|
148
|
+
* or the end date is `LocalDate.MIN`
|
|
149
|
+
*/
|
|
150
|
+
static ofClosed(startInclusive, endInclusive) {
|
|
151
|
+
requireNonNull(startInclusive, 'startInclusive');
|
|
152
|
+
requireNonNull(endInclusive, 'endInclusive');
|
|
153
|
+
requireInstance(startInclusive, LocalDate, 'startInclusive');
|
|
154
|
+
requireInstance(endInclusive, LocalDate, 'endInclusive');
|
|
155
|
+
if (endInclusive.isBefore(startInclusive)) {
|
|
156
|
+
throw new DateTimeException('Start date must be on or before end date');
|
|
157
|
+
}
|
|
158
|
+
const end = (endInclusive.equals(LocalDate.MAX) ? LocalDate.MAX : endInclusive.plusDays(1));
|
|
159
|
+
return new LocalDateRange(startInclusive, end);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Obtains an empty date range located at the specified date.
|
|
164
|
+
*
|
|
165
|
+
* The empty range has zero length and contains no other dates or ranges.
|
|
166
|
+
* An empty range cannot be located at `LocalDate.MIN`, `LocalDate.MIN.plusDays(1)`,
|
|
167
|
+
* `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`.
|
|
168
|
+
*
|
|
169
|
+
* @param {LocalDate} date - the date where the empty range is located, not null
|
|
170
|
+
* @return {LocalDateRange} the empty range, not null
|
|
171
|
+
* @throws {DateTimeException} if the date is `LocalDate.MIN`, `LocalDate.MIN.plusDays(1)`,
|
|
172
|
+
* `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`
|
|
173
|
+
*/
|
|
174
|
+
static ofEmpty(date) {
|
|
175
|
+
requireNonNull(date, 'date');
|
|
176
|
+
requireInstance(date, LocalDate, 'date');
|
|
177
|
+
return new LocalDateRange(date, date);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Obtains a range that is unbounded at the start and end.
|
|
182
|
+
*
|
|
183
|
+
* @return {LocalDateRange} the range, with an unbounded start and unbounded end
|
|
184
|
+
*/
|
|
185
|
+
static ofUnbounded() {
|
|
186
|
+
return LocalDateRange.ALL;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Obtains a range up to, but not including, the specified end date.
|
|
191
|
+
*
|
|
192
|
+
* The range includes all dates from the unbounded start, denoted by `LocalDate.MIN`, to the end date.
|
|
193
|
+
* The end date is exclusive and cannot be `LocalDate.MIN` or `LocalDate.MIN.plusDays(1)`.
|
|
194
|
+
*
|
|
195
|
+
* @param {LocalDate} endExclusive - the exclusive end date, `LocalDate.MAX` treated as unbounded, not null
|
|
196
|
+
* @return {LocalDateRange} the range, with an unbounded start
|
|
197
|
+
* @throws {DateTimeException} if the end date is `LocalDate.MIN` or `LocalDate.MIN.plusDays(1)`
|
|
198
|
+
*/
|
|
199
|
+
static ofUnboundedStart(endExclusive) {
|
|
200
|
+
requireNonNull(endExclusive, 'endExclusive');
|
|
201
|
+
requireInstance(endExclusive, LocalDate, 'endExclusive');
|
|
202
|
+
return LocalDateRange.of(LocalDate.MIN, endExclusive);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Obtains a range from and including the specified start date.
|
|
207
|
+
*
|
|
208
|
+
* The range includes all dates from the start date to the unbounded end, denoted by `LocalDate.MAX`.
|
|
209
|
+
* The start date is inclusive and cannot be `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`.
|
|
210
|
+
*
|
|
211
|
+
* @param {LocalDate} startInclusive - the inclusive start date, `LocalDate.MIN` treated as unbounded, not null
|
|
212
|
+
* @return {LocalDateRange} the range, with an unbounded end
|
|
213
|
+
* @throws {DateTimeException} if the start date is `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`
|
|
214
|
+
*/
|
|
215
|
+
static ofUnboundedEnd(startInclusive) {
|
|
216
|
+
return LocalDateRange.of(startInclusive, LocalDate.MAX);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//-----------------------------------------------------------------------
|
|
220
|
+
/**
|
|
221
|
+
* Obtains an instance of `LocalDateRange` from a text string such as
|
|
222
|
+
* `2007-12-03/2007-12-04`, where the end date is exclusive.
|
|
223
|
+
*
|
|
224
|
+
* The string must consist of one of the following three formats:
|
|
225
|
+
* <ul>
|
|
226
|
+
* <li>a representations of an {@link LocalDate}, followed by a forward slash,
|
|
227
|
+
* followed by a representation of a {@link LocalDate}
|
|
228
|
+
* <li>a representation of an {@link LocalDate}, followed by a forward slash,
|
|
229
|
+
* followed by a representation of a {@link Period}
|
|
230
|
+
* <li>a representation of a {@link Period}, followed by a forward slash,
|
|
231
|
+
* followed by a representation of an {@link LocalDate}
|
|
232
|
+
* </ul>
|
|
233
|
+
*
|
|
234
|
+
* @param {string} text - the text to parse, not null
|
|
235
|
+
* @return {LocalDateRange} the parsed range, not null
|
|
236
|
+
* @throws {DateTimeParseException} if the text cannot be parsed
|
|
237
|
+
*/
|
|
238
|
+
static parse(text) {
|
|
239
|
+
requireNonNull(text, 'text');
|
|
240
|
+
for (let i = 0; i < text.length; i++) {
|
|
241
|
+
if (text[i] === '/') {
|
|
242
|
+
const firstChar = text.charAt(0);
|
|
243
|
+
if (firstChar === 'P' || firstChar === 'p') {
|
|
244
|
+
// period followed by date
|
|
245
|
+
const duration = Period.parse(text.slice(0, i));
|
|
246
|
+
const end = LocalDate.parse(text.slice(i + 1, text.length));
|
|
247
|
+
return LocalDateRange.of(end.minus(duration), end);
|
|
248
|
+
} else {
|
|
249
|
+
// date followed by date or period
|
|
250
|
+
const start = LocalDate.parse(text.slice(0, i));
|
|
251
|
+
if (i + 1 < text.length) {
|
|
252
|
+
const c = text[i + 1];
|
|
253
|
+
if (c === 'P' || c === 'p') {
|
|
254
|
+
const duration = Period.parse(text.slice(i + 1, text.length));
|
|
255
|
+
return LocalDateRange.of(start, start.plus(duration));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const end = LocalDate.parse(text.slice(i + 1, text.length));
|
|
259
|
+
return LocalDateRange.of(start, end);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
throw new DateTimeParseException('LocalDateRange cannot be parsed, no forward slash found', text, 0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
//-----------------------------------------------------------------------
|
|
267
|
+
/**
|
|
268
|
+
* Constructor.
|
|
269
|
+
*
|
|
270
|
+
* @param {LocalDate} startInclusive - the start date, inclusive, validated not null
|
|
271
|
+
* @param {LocalDate} endExclusive - the end date, exclusive, validated not null
|
|
272
|
+
* @private
|
|
273
|
+
*/
|
|
274
|
+
constructor(startInclusive, endExclusive) {
|
|
275
|
+
requireNonNull(startInclusive, 'startInclusive');
|
|
276
|
+
requireNonNull(endExclusive, 'endExclusive');
|
|
277
|
+
requireInstance(startInclusive, LocalDate, 'startInclusive');
|
|
278
|
+
requireInstance(endExclusive, LocalDate, 'endExclusive');
|
|
279
|
+
if (endExclusive.isBefore(startInclusive)) {
|
|
280
|
+
throw new DateTimeException('End date must be on or after start date');
|
|
281
|
+
}
|
|
282
|
+
if (startInclusive.equals(MAXM1)) {
|
|
283
|
+
throw new DateTimeException('Range must not start at LocalDate.MAX.minusDays(1)');
|
|
284
|
+
}
|
|
285
|
+
if (endExclusive.equals(MINP1)) {
|
|
286
|
+
throw new DateTimeException('Range must not end at LocalDate.MIN.plusDays(1)');
|
|
287
|
+
}
|
|
288
|
+
if (endExclusive.equals(LocalDate.MIN) || startInclusive.equals(LocalDate.MAX)) {
|
|
289
|
+
throw new DateTimeException('Empty range must not be at LocalDate.MIN or LocalDate.MAX');
|
|
290
|
+
}
|
|
291
|
+
this._start = startInclusive;
|
|
292
|
+
this._end = endExclusive;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
//-----------------------------------------------------------------------
|
|
296
|
+
/**
|
|
297
|
+
* Gets the start date of this range, inclusive.
|
|
298
|
+
*
|
|
299
|
+
* This will return `LocalDate#MIN` if the range is unbounded at the start.
|
|
300
|
+
* In this case, the range includes all dates into the far-past.
|
|
301
|
+
*
|
|
302
|
+
* This never returns `LocalDate.MAX` or `LocalDate.MAX.minusDays(1)`.
|
|
303
|
+
*
|
|
304
|
+
* @return {LocalDate} the start date
|
|
305
|
+
*/
|
|
306
|
+
start() {
|
|
307
|
+
return this._start;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Gets the end date of this range, exclusive.
|
|
312
|
+
*
|
|
313
|
+
* This will return `LocalDate.MAX` if the range is unbounded at the end.
|
|
314
|
+
* In this case, the range includes all dates into the far-future.
|
|
315
|
+
*
|
|
316
|
+
* This never returns `LocalDate.MIN` or `LocalDate.MIN.plusDays(1)`.
|
|
317
|
+
*
|
|
318
|
+
* @return {LocalDate} the end date, exclusive
|
|
319
|
+
*/
|
|
320
|
+
end() {
|
|
321
|
+
return this._end;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Gets the end date of this range, inclusive.
|
|
326
|
+
*
|
|
327
|
+
* This will return `LocalDate.MAX` if the range is unbounded at the end.
|
|
328
|
+
* In this case, the range includes all dates into the far-future.
|
|
329
|
+
*
|
|
330
|
+
* This returns the date before the end date.
|
|
331
|
+
*
|
|
332
|
+
* This never returns `LocalDate.MIN`.
|
|
333
|
+
*
|
|
334
|
+
* @return {LocalDate} the end date, inclusive
|
|
335
|
+
*/
|
|
336
|
+
endInclusive() {
|
|
337
|
+
if (this.isUnboundedEnd()) {
|
|
338
|
+
return LocalDate.MAX;
|
|
339
|
+
}
|
|
340
|
+
return this._end.minusDays(1);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
//-----------------------------------------------------------------------
|
|
344
|
+
/**
|
|
345
|
+
* Checks if the range is empty.
|
|
346
|
+
*
|
|
347
|
+
* An empty range occurs when the start date equals the end date.
|
|
348
|
+
*
|
|
349
|
+
* An empty range is never unbounded.
|
|
350
|
+
*
|
|
351
|
+
* @return {boolean} true if the range is empty
|
|
352
|
+
*/
|
|
353
|
+
isEmpty() {
|
|
354
|
+
return this._start.equals(this._end);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Checks if the start of the range is unbounded.
|
|
359
|
+
*
|
|
360
|
+
* An unbounded range is never empty.
|
|
361
|
+
*
|
|
362
|
+
* @return {boolean} true if start is unbounded
|
|
363
|
+
*/
|
|
364
|
+
isUnboundedStart() {
|
|
365
|
+
return this._start.equals(LocalDate.MIN);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Checks if the end of the range is unbounded.
|
|
370
|
+
*
|
|
371
|
+
* An unbounded range is never empty.
|
|
372
|
+
*
|
|
373
|
+
* @return {boolean} true if end is unbounded
|
|
374
|
+
*/
|
|
375
|
+
isUnboundedEnd() {
|
|
376
|
+
return this._end.equals(LocalDate.MAX);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
//-----------------------------------------------------------------------
|
|
380
|
+
/**
|
|
381
|
+
* Returns a copy of this range with the start date adjusted.
|
|
382
|
+
*
|
|
383
|
+
* This returns a new instance with the start date altered.
|
|
384
|
+
* Since `LocalDate` implements `TemporalAdjuster` any
|
|
385
|
+
* local date can simply be passed in.
|
|
386
|
+
*
|
|
387
|
+
* For example, to adjust the start to one week earlier:
|
|
388
|
+
* <pre>
|
|
389
|
+
* range = range.withStart(date -> date.minus(1, ChronoUnit.WEEKS));
|
|
390
|
+
* </pre>
|
|
391
|
+
*
|
|
392
|
+
* @param {TemporalAdjuster} adjuster - the adjuster to use, not null
|
|
393
|
+
* @return {LocalDateRange} a copy of this range with the start date adjusted
|
|
394
|
+
* @throws {DateTimeException} if the new start date is after the current end date
|
|
395
|
+
*/
|
|
396
|
+
withStart(adjuster) {
|
|
397
|
+
return LocalDateRange.of(this._start.with(adjuster), this._end);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Returns a copy of this range with the end date adjusted.
|
|
402
|
+
*
|
|
403
|
+
* This returns a new instance with the exclusive end date altered.
|
|
404
|
+
* Since `LocalDate` implements `TemporalAdjuster` any
|
|
405
|
+
* local date can simply be passed in.
|
|
406
|
+
*
|
|
407
|
+
* For example, to adjust the end to one week later:
|
|
408
|
+
* <pre>
|
|
409
|
+
* range = range.withEnd(date -> date.plus(1, ChronoUnit.WEEKS));
|
|
410
|
+
* </pre>
|
|
411
|
+
*
|
|
412
|
+
* @param {TemporalAdjuster} adjuster - the adjuster to use, not null
|
|
413
|
+
* @return {LocalDateRange} a copy of this range with the end date adjusted
|
|
414
|
+
* @throws {DateTimeException} if the new end date is before the current start date
|
|
415
|
+
*/
|
|
416
|
+
withEnd(adjuster) {
|
|
417
|
+
return LocalDateRange.of(this._start, this._end.with(adjuster));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
//-----------------------------------------------------------------------
|
|
421
|
+
/**
|
|
422
|
+
* Checks if this range contains the specified date.
|
|
423
|
+
*
|
|
424
|
+
* This checks if the specified date is within the bounds of this range.
|
|
425
|
+
* If this range is empty then this method always returns false.
|
|
426
|
+
* Else if this range has an unbounded start then `contains(LocalDate#MIN)` returns true.
|
|
427
|
+
* Else if this range has an unbounded end then `contains(LocalDate#MAX)` returns true.
|
|
428
|
+
*
|
|
429
|
+
* @param {LocalDate} date - the date to check for, not null
|
|
430
|
+
* @return {boolean} true if this range contains the date
|
|
431
|
+
*/
|
|
432
|
+
contains(date) {
|
|
433
|
+
requireNonNull(date, 'date');
|
|
434
|
+
return this._start.compareTo(date) <= 0 && (date.compareTo(this._end) < 0 || this.isUnboundedEnd());
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Checks if this range encloses the specified range.
|
|
439
|
+
*
|
|
440
|
+
* This checks if the bounds of the specified range are within the bounds of this range.
|
|
441
|
+
* An empty range encloses itself.
|
|
442
|
+
*
|
|
443
|
+
* @param {LocalDateRange} other - the other range to check for, not null
|
|
444
|
+
* @return {boolean} true if this range contains all dates in the other range
|
|
445
|
+
*/
|
|
446
|
+
encloses(other) {
|
|
447
|
+
requireNonNull(other, 'other');
|
|
448
|
+
return this._start.compareTo(other._start) <= 0 && other._end.compareTo(this._end) <= 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Checks if this range abuts the specified range.
|
|
453
|
+
*
|
|
454
|
+
* The result is true if the end of this range is the start of the other, or vice versa.
|
|
455
|
+
* An empty range does not abut itself.
|
|
456
|
+
*
|
|
457
|
+
* @param {LocalDateRange} other - the other range, not null
|
|
458
|
+
* @return {boolean} true if this range abuts the other range
|
|
459
|
+
*/
|
|
460
|
+
abuts(other) {
|
|
461
|
+
requireNonNull(other, 'other');
|
|
462
|
+
return this._end.equals(other._start) !== this._start.equals(other._end);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Checks if this range is connected to the specified range.
|
|
467
|
+
*
|
|
468
|
+
* The result is true if the two ranges have an enclosed range in common, even if that range is empty.
|
|
469
|
+
* An empty range is connected to itself.
|
|
470
|
+
*
|
|
471
|
+
* This is equivalent to `(overlaps(other) || abuts(other))`.
|
|
472
|
+
*
|
|
473
|
+
* @param {LocalDateRange} other - the other range, not null
|
|
474
|
+
* @return {boolean} true if this range is connected to the other range
|
|
475
|
+
*/
|
|
476
|
+
isConnected(other) {
|
|
477
|
+
requireNonNull(other, 'other');
|
|
478
|
+
return this.equals(other) || (this._start.compareTo(other._end) <= 0 && other._start.compareTo(this._end) <= 0);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Checks if this range overlaps the specified range.
|
|
483
|
+
*
|
|
484
|
+
* The result is true if the two ranges share some part of the time-line.
|
|
485
|
+
* An empty range overlaps itself.
|
|
486
|
+
*
|
|
487
|
+
* This is equivalent to `(isConnected(other) && !abuts(other))`.
|
|
488
|
+
*
|
|
489
|
+
* @param {LocalDateRange} other - the time range to compare to, null means a zero length range now
|
|
490
|
+
* @return {boolean} true if the time ranges overlap
|
|
491
|
+
*/
|
|
492
|
+
overlaps(other) {
|
|
493
|
+
requireNonNull(other, 'other');
|
|
494
|
+
return other.equals(this) || (this._start.compareTo(other._end) < 0 && other._start.compareTo(this._end) < 0);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
//-----------------------------------------------------------------------
|
|
498
|
+
/**
|
|
499
|
+
* Calculates the range that is the intersection of this range and the specified range.
|
|
500
|
+
*
|
|
501
|
+
* This finds the intersection of two ranges.
|
|
502
|
+
* This throws an exception if the two ranges are not {@linkplain #isConnected(LocalDateRange) connected}.
|
|
503
|
+
*
|
|
504
|
+
* @param {LocalDateRange} other - the other range to check for, not null
|
|
505
|
+
* @return {LocalDateRange} the range that is the intersection of the two ranges
|
|
506
|
+
* @throws {DateTimeException} if the ranges do not connect
|
|
507
|
+
*/
|
|
508
|
+
intersection(other) {
|
|
509
|
+
requireNonNull(other, 'other');
|
|
510
|
+
if (this.isConnected(other) === false) {
|
|
511
|
+
throw new DateTimeException(`Ranges do not connect: ${this} and ${other}`);
|
|
512
|
+
}
|
|
513
|
+
const cmpStart = this._start.compareTo(other._start);
|
|
514
|
+
const cmpEnd = this._end.compareTo(other._end);
|
|
515
|
+
if (cmpStart >= 0 && cmpEnd <= 0) {
|
|
516
|
+
return this;
|
|
517
|
+
} else if (cmpStart <= 0 && cmpEnd >= 0) {
|
|
518
|
+
return other;
|
|
519
|
+
} else {
|
|
520
|
+
const newStart = (cmpStart >= 0 ? this._start : other._start);
|
|
521
|
+
const newEnd = (cmpEnd <= 0 ? this._end : other._end);
|
|
522
|
+
return LocalDateRange.of(newStart, newEnd);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Calculates the range that is the union of this range and the specified range.
|
|
528
|
+
*
|
|
529
|
+
* This finds the union of two ranges.
|
|
530
|
+
* This throws an exception if the two ranges are not {@linkplain #isConnected(LocalDateRange) connected}.
|
|
531
|
+
*
|
|
532
|
+
* @param {LocalDateRange} other - the other range to check for, not null
|
|
533
|
+
* @return {LocalDateRange} the range that is the union of the two ranges
|
|
534
|
+
* @throws {DateTimeException} if the ranges do not connect
|
|
535
|
+
*/
|
|
536
|
+
union(other) {
|
|
537
|
+
requireNonNull(other, 'other');
|
|
538
|
+
if (this.isConnected(other) === false) {
|
|
539
|
+
throw new DateTimeException(`Ranges do not connect: ${this} and ${other}`);
|
|
540
|
+
}
|
|
541
|
+
const cmpStart = this._start.compareTo(other._start);
|
|
542
|
+
const cmpEnd = this._end.compareTo(other._end);
|
|
543
|
+
if (cmpStart >= 0 && cmpEnd <= 0) {
|
|
544
|
+
return other;
|
|
545
|
+
} else if (cmpStart <= 0 && cmpEnd >= 0) {
|
|
546
|
+
return this;
|
|
547
|
+
} else {
|
|
548
|
+
const newStart = (cmpStart >= 0 ? other._start : this._start);
|
|
549
|
+
const newEnd = (cmpEnd <= 0 ? other._end : this._end);
|
|
550
|
+
return LocalDateRange.of(newStart, newEnd);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Calculates the smallest range that encloses this range and the specified range.
|
|
556
|
+
*
|
|
557
|
+
* The result of this method will {@linkplain #encloses(LocalDateRange) enclose}
|
|
558
|
+
* this range and the specified range.
|
|
559
|
+
*
|
|
560
|
+
* @param {LocalDateRange} other - the other range to check for, not null
|
|
561
|
+
* @return {LocalDateRange} the range that spans the two ranges
|
|
562
|
+
*/
|
|
563
|
+
span(other) {
|
|
564
|
+
requireNonNull(other, 'other');
|
|
565
|
+
const cmpStart = this._start.compareTo(other._start);
|
|
566
|
+
const cmpEnd = this._end.compareTo(other._end);
|
|
567
|
+
const newStart = (cmpStart >= 0 ? other._start : this._start);
|
|
568
|
+
const newEnd = (cmpEnd <= 0 ? other._end : this._end);
|
|
569
|
+
return LocalDateRange.of(newStart, newEnd);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Function overloading for {@link LocalDateRange.isAfter}
|
|
574
|
+
* - if called with `LocalDate`, {@link LocalDateRange._isAfterLocalDate} is executed,
|
|
575
|
+
* - if called with `LocalDateRange`, {@link LocalDateRange._isAfterLocalDateRange} is executed,
|
|
576
|
+
* - otherwise throws IllegalArgumentException.
|
|
577
|
+
*
|
|
578
|
+
* @param {LocalDate|LocalDateRange} localDateOrLocalDateRange
|
|
579
|
+
* @return {boolean}
|
|
580
|
+
*/
|
|
581
|
+
isAfter(localDateOrLocalDateRange) {
|
|
582
|
+
if (localDateOrLocalDateRange instanceof LocalDate) {
|
|
583
|
+
return this._isAfterLocalDate(localDateOrLocalDateRange);
|
|
584
|
+
}
|
|
585
|
+
if (localDateOrLocalDateRange instanceof LocalDateRange) {
|
|
586
|
+
return this._isAfterLocalDateRange(localDateOrLocalDateRange);
|
|
587
|
+
}
|
|
588
|
+
throw new IllegalArgumentException(`localDateOrLocalDateRange must be an instance of LocalDate or LocalDateRange but is ${localDateOrLocalDateRange.constructor.name}`);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Function overloading for {@link LocalDateRange.isBefore}
|
|
593
|
+
* - if called with `LocalDate`, {@link LocalDateRange._isBeforeLocalDate} is executed,
|
|
594
|
+
* - if called with `LocalDateRange`, {@link LocalDateRange._isBeforeLocalDateRange} is executed,
|
|
595
|
+
* - otherwise throws IllegalArgumentException.
|
|
596
|
+
*
|
|
597
|
+
* @param {LocalDate|LocalDateRange} localDateOrLocalDateRange
|
|
598
|
+
* @return {boolean}
|
|
599
|
+
*/
|
|
600
|
+
isBefore(localDateOrLocalDateRange) {
|
|
601
|
+
if (localDateOrLocalDateRange instanceof LocalDate) {
|
|
602
|
+
return this._isBeforeLocalDate(localDateOrLocalDateRange);
|
|
603
|
+
}
|
|
604
|
+
if (localDateOrLocalDateRange instanceof LocalDateRange) {
|
|
605
|
+
return this._isBeforeLocalDateRange(localDateOrLocalDateRange);
|
|
606
|
+
}
|
|
607
|
+
throw new IllegalArgumentException(`localDateOrLocalDateRange must be an instance of LocalDate or LocalDateRange but is ${localDateOrLocalDateRange.constructor.name}`);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
//-----------------------------------------------------------------------
|
|
611
|
+
/**
|
|
612
|
+
* Checks if this range is after the specified date.
|
|
613
|
+
*
|
|
614
|
+
* The result is true if every date in this range is after the specified date.
|
|
615
|
+
* An empty range behaves as though it is a date for comparison purposes.
|
|
616
|
+
*
|
|
617
|
+
* @param {LocalDate} date - the other date to compare to, not null
|
|
618
|
+
* @return {boolean} true if the start of this range is after the specified date
|
|
619
|
+
* @protected
|
|
620
|
+
*/
|
|
621
|
+
_isAfterLocalDate(date) {
|
|
622
|
+
return this._start.compareTo(date) > 0;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Checks if this range is before the specified date.
|
|
627
|
+
*
|
|
628
|
+
* The result is true if every date in this range is before the specified date.
|
|
629
|
+
* An empty range behaves as though it is a date for comparison purposes.
|
|
630
|
+
*
|
|
631
|
+
* @param {LocalDate} date - the other date to compare to, not null
|
|
632
|
+
* @return {boolean} true if the start of this range is before the specified date
|
|
633
|
+
* @protected
|
|
634
|
+
*/
|
|
635
|
+
_isBeforeLocalDate(date) {
|
|
636
|
+
return this._end.compareTo(date) <= 0 && this._start.compareTo(date) < 0;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
//-----------------------------------------------------------------------
|
|
640
|
+
/**
|
|
641
|
+
* Checks if this range is after the specified range.
|
|
642
|
+
*
|
|
643
|
+
* The result is true if every date in this range is after every date in the specified range.
|
|
644
|
+
* An empty range behaves as though it is a date for comparison purposes.
|
|
645
|
+
*
|
|
646
|
+
* @param {LocalDateRange} other - the other range to compare to, not null
|
|
647
|
+
* @return {boolean} true if every date in this range is after every date in the other range
|
|
648
|
+
* @protected
|
|
649
|
+
*/
|
|
650
|
+
_isAfterLocalDateRange(other) {
|
|
651
|
+
return this._start.compareTo(other._end) >= 0 && !other.equals(this);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Checks if this range is before the specified range.
|
|
656
|
+
*
|
|
657
|
+
* The result is true if every date in this range is before every date in the specified range.
|
|
658
|
+
* An empty range behaves as though it is a date for comparison purposes.
|
|
659
|
+
*
|
|
660
|
+
* @param {LocalDateRange} range - the other range to compare to, not null
|
|
661
|
+
* @return {boolean} true if every date in this range is before every date in the other range
|
|
662
|
+
* @protected
|
|
663
|
+
*/
|
|
664
|
+
_isBeforeLocalDateRange(range) {
|
|
665
|
+
return this._end.compareTo(range._start) <= 0 && !range.equals(this);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
//-----------------------------------------------------------------------
|
|
669
|
+
/**
|
|
670
|
+
* Obtains the length of this range in days.
|
|
671
|
+
*
|
|
672
|
+
* This returns the number of days between the start and end dates.
|
|
673
|
+
* Unbounded ranges return `Number.NaN`.
|
|
674
|
+
*
|
|
675
|
+
* @return {number} the length in days, Number.NaN if unbounded
|
|
676
|
+
*/
|
|
677
|
+
lengthInDays() {
|
|
678
|
+
if (this.isUnboundedStart() || this.isUnboundedEnd()) {
|
|
679
|
+
return Number.NaN;
|
|
680
|
+
}
|
|
681
|
+
return this._end.toEpochDay() - this._start.toEpochDay();
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Obtains the length of this range as a period.
|
|
686
|
+
*
|
|
687
|
+
* This returns the {@link Period} between the start and end dates.
|
|
688
|
+
* Unbounded ranges throw {@link ArithmeticException}.
|
|
689
|
+
*
|
|
690
|
+
* @return {Period} the period of the range
|
|
691
|
+
* @throws {ArithmeticException} if the calculation exceeds the capacity of `Period`,
|
|
692
|
+
* or the range is unbounded
|
|
693
|
+
*/
|
|
694
|
+
toPeriod() {
|
|
695
|
+
if (this.isUnboundedStart() || this.isUnboundedEnd()) {
|
|
696
|
+
throw new ArithmeticException('Unbounded range cannot be converted to a Period');
|
|
697
|
+
}
|
|
698
|
+
return Period.between(this._start, this._end);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
//-----------------------------------------------------------------------
|
|
702
|
+
/**
|
|
703
|
+
* Checks if this range is equal to another range.
|
|
704
|
+
*
|
|
705
|
+
* Compares this `LocalDateRange` with another ensuring that the two dates are the same.
|
|
706
|
+
* Only objects of type `LocalDateRange` are compared, other types return false.
|
|
707
|
+
*
|
|
708
|
+
* @param {*} obj - the object to check, null returns false
|
|
709
|
+
* @return {boolean} true if this is equal to the other range
|
|
710
|
+
*/
|
|
711
|
+
equals(obj) {
|
|
712
|
+
if (this === obj) {
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
if (obj instanceof LocalDateRange) {
|
|
716
|
+
const other = obj;
|
|
717
|
+
return this._start.equals(other._start) && this._end.equals(other._end);
|
|
718
|
+
}
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* A hash code for this range.
|
|
724
|
+
*
|
|
725
|
+
* @return {number} a suitable hash code
|
|
726
|
+
*/
|
|
727
|
+
hashCode() {
|
|
728
|
+
return this._start.hashCode() ^ this._end.hashCode();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
//-----------------------------------------------------------------------
|
|
732
|
+
/**
|
|
733
|
+
* Outputs this range as a `String`, such as `2007-12-03/2007-12-04`.
|
|
734
|
+
*
|
|
735
|
+
* The output will be the ISO-8601 format formed by combining the
|
|
736
|
+
* `toString()` methods of the two dates, separated by a forward slash.
|
|
737
|
+
*
|
|
738
|
+
* @return {string} a string representation of this date, not null
|
|
739
|
+
*/
|
|
740
|
+
toString() {
|
|
741
|
+
return `${this._start.toString()}/${this._end.toString()}`;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export function _init() {
|
|
746
|
+
/**
|
|
747
|
+
* A range over the whole time-line.
|
|
748
|
+
*/
|
|
749
|
+
LocalDateRange.ALL = new LocalDateRange(LocalDate.MIN, LocalDate.MAX);
|
|
750
|
+
}
|