UNPKG

9.11 kBJavaScriptView Raw
1/**
2 * @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
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
7import {assert} from '../assert';
8import {DateTimeException, IllegalArgumentException} from '../errors';
9import {MathUtil} from '../MathUtil';
10
11/**
12 * The range of valid values for a date-time field.
13 *
14 * All TemporalField instances have a valid range of values.
15 * For example, the ISO day-of-month runs from 1 to somewhere between 28 and 31.
16 * This class captures that valid range.
17 *
18 * It is important to be aware of the limitations of this class.
19 * Only the minimum and maximum values are provided.
20 * It is possible for there to be invalid values within the outer range.
21 * For example, a weird field may have valid values of 1, 2, 4, 6, 7, thus
22 * have a range of '1 - 7', despite that fact that values 3 and 5 are invalid.
23 *
24 * Instances of this class are not tied to a specific field.
25 */
26export class ValueRange {
27
28 /**
29 *
30 * @param {!number} minSmallest
31 * @param {!number} minLargest
32 * @param {!number} maxSmallest
33 * @param {!number} maxLargest
34 * @private
35 */
36 constructor(minSmallest, minLargest, maxSmallest, maxLargest) {
37 assert(!(minSmallest > minLargest), 'Smallest minimum value \'' + minSmallest +
38 '\' must be less than largest minimum value \'' + minLargest + '\'', IllegalArgumentException);
39 assert(!(maxSmallest > maxLargest), 'Smallest maximum value \'' + maxSmallest +
40 '\' must be less than largest maximum value \'' + maxLargest + '\'', IllegalArgumentException);
41 assert(!(minLargest > maxLargest), 'Minimum value \'' + minLargest +
42 '\' must be less than maximum value \'' + maxLargest + '\'', IllegalArgumentException);
43
44 this._minSmallest = minSmallest;
45 this._minLargest = minLargest;
46 this._maxLargest = maxLargest;
47 this._maxSmallest = maxSmallest;
48 }
49
50 /**
51 * Is the value range fixed and fully known.
52 *
53 * For example, the ISO day-of-month runs from 1 to between 28 and 31.
54 * Since there is uncertainty about the maximum value, the range is not fixed.
55 * However, for the month of January, the range is always 1 to 31, thus it is fixed.
56 *
57 * @return {boolean} true if the set of values is fixed
58 */
59 isFixed() {
60 return this._minSmallest === this._minLargest && this._maxSmallest === this._maxLargest;
61 }
62
63 /**
64 *
65 * @returns {number}
66 */
67 minimum(){
68 return this._minSmallest;
69 }
70
71 /**
72 *
73 * @returns {number}
74 */
75 largestMinimum(){
76 return this._minLargest;
77 }
78
79 /**
80 *
81 * @returns {number}
82 */
83 maximum(){
84 return this._maxLargest;
85 }
86
87 /**
88 *
89 * @returns {number}
90 */
91 smallestMaximum(){
92 return this._maxSmallest;
93 }
94
95 /**
96 *
97 * @returns {boolean}
98 */
99 isValidValue(value) {
100 return (this.minimum() <= value && value <= this.maximum());
101 }
102
103 /**
104 *
105 * @param {number} value
106 * @param {TemporalField} field
107 */
108 checkValidValue(value, field) {
109 let msg;
110 if (!this.isValidValue(value)) {
111 if (field != null) {
112 msg = ('Invalid value for ' + field + ' (valid values ' + (this.toString()) + '): ') + value;
113 } else {
114 msg = ('Invalid value (valid values ' + (this.toString()) + '): ') + value;
115 }
116 return assert(false, msg, DateTimeException);
117 }
118 return value;
119 }
120
121 /**
122 * Checks that the specified value is valid and fits in an `int`.
123 *
124 * This validates that the value is within the valid range of values and that
125 * all valid values are within the bounds of an `int`.
126 * The field is only used to improve the error message.
127 *
128 * @param {number} value - the value to check
129 * @param {TemporalField} field - the field being checked, may be null
130 * @return {number} the value that was passed in
131 * @see #isValidIntValue(long)
132 */
133 checkValidIntValue(value, field) {
134 if (this.isValidIntValue(value) === false) {
135 throw new DateTimeException('Invalid int value for ' + field + ': ' + value);
136 }
137 return value;
138 }
139
140 /**
141 * Checks if the value is within the valid range and that all values
142 * in the range fit in an `int`.
143 *
144 * This method combines {@link isIntValue} and {@link isValidValue}.
145 *
146 * @param {number} value - the value to check
147 * @return true if the value is valid and fits in an `int`
148 */
149 isValidIntValue(value) {
150 return this.isIntValue() && this.isValidValue(value);
151 }
152
153 /**
154 * Checks if all values in the range fit in an `int`.
155 *
156 * This checks that all valid values are within the bounds of an `int`.
157 *
158 * For example, the ISO month-of-year has values from 1 to 12, which fits in an `int`.
159 * By comparison, ISO nano-of-day runs from 1 to 86,400,000,000,000 which does not fit in an `int`.
160 *
161 * This implementation uses {@link getMinimum} and {@link getMaximum}.
162 *
163 * @return boolean if a valid value always fits in an `int`
164 */
165 isIntValue() { // should be isSafeIntegerValue
166 return this.minimum() >= MathUtil.MIN_SAFE_INTEGER && this.maximum() <= MathUtil.MAX_SAFE_INTEGER;
167 }
168
169 /**
170 * Checks if this range is equal to another range.
171 *
172 * The comparison is based on the four values, minimum, largest minimum,
173 * smallest maximum and maximum.
174 * Only objects of type {@link ValueRange} are compared, other types return false.
175 *
176 * @param {*} other - the object to check, null returns false
177 * @return {boolean} true if this is equal to the other range
178 */
179 equals(other) {
180 if (other === this) {
181 return true;
182 }
183 if (other instanceof ValueRange) {
184 return this._minSmallest === other._minSmallest && this._minLargest === other._minLargest &&
185 this._maxSmallest === other._maxSmallest && this._maxLargest === other._maxLargest;
186 }
187 return false;
188 }
189
190 /**
191 * A hash code for this range.
192 *
193 * @return {number} a suitable hash code
194 */
195 hashCode() {
196 return MathUtil.hashCode(this._minSmallest, this._minLargest, this._maxSmallest, this._maxLargest);
197 }
198
199 /*
200 * Outputs this range as a String.
201 *
202 * The format will be '{min}/{largestMin} - {smallestMax}/{max}',
203 * where the largestMin or smallestMax sections may be omitted, together
204 * with associated slash, if they are the same as the min or max.
205 *
206 * @return {string} a string representation of this range, not null
207 */
208 toString() {
209 let str = this.minimum() + (this.minimum() !== this.largestMinimum() ? '/' + (this.largestMinimum()) : '');
210 str += ' - ';
211 str += this.smallestMaximum() + (this.smallestMaximum() !== this.maximum() ? '/' + (this.maximum()) : '');
212 return str;
213 }
214
215 /*
216 * called with 2 params: Obtains a fixed value range.
217 *
218 * This factory obtains a range where the minimum and maximum values are fixed.
219 * For example, the ISO month-of-year always runs from 1 to 12.
220 *
221 * @param min the minimum value
222 * @param max the maximum value
223 * @return the ValueRange for min, max, not null
224
225 * called with 3 params: Obtains a variable value range.
226 *
227 * This factory obtains a range where the minimum value is fixed and the maximum value may vary.
228 * For example, the ISO day-of-month always starts at 1, but ends between 28 and 31.
229 *
230 * @param min the minimum value
231 * @param maxSmallest the smallest maximum value
232 * @param maxLargest the largest maximum value
233 * @return the ValueRange for min, smallest max, largest max, not null
234
235 * called with 4 params: Obtains a fully variable value range.
236 *
237 * This factory obtains a range where both the minimum and maximum value may vary.
238 *
239 * @param minSmallest the smallest minimum value
240 * @param minLargest the largest minimum value
241 * @param maxSmallest the smallest maximum value
242 * @param maxLargest the largest maximum value
243 *
244 * @return {ValueRange} the ValueRange for smallest min, largest min, smallest max, largest max, not null
245 */
246 static of() {
247 if (arguments.length === 2) {
248 return new ValueRange(arguments[0], arguments[0], arguments[1], arguments[1]);
249 } else if (arguments.length === 3) {
250 return new ValueRange(arguments[0], arguments[0], arguments[1], arguments[2]);
251 } else if (arguments.length === 4) {
252 return new ValueRange(arguments[0], arguments[1], arguments[2], arguments[3]);
253 } else {
254 return assert(false, 'Invalid number of arguments ' + arguments.length, IllegalArgumentException);
255 }
256 }
257}