@js-joda/core 6.2.0 → 6.3.1
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.cjs.js +5360 -5089
- package/dist/js-joda.cjs.js.map +1 -1
- package/dist/js-joda.esm.js +5360 -5089
- package/dist/js-joda.esm.js.map +1 -1
- package/dist/js-joda.js +5360 -5089
- package/dist/js-joda.js.map +1 -1
- package/dist/js-joda.min.js +2 -2
- package/package.json +2 -2
- package/src/Duration.js +128 -6
- package/src/Instant.js +6 -0
- package/src/LocalDate.js +2 -1
- package/src/MonthDay.js +1 -1
- package/src/ZonedDateTime.js +1 -1
- package/src/format/DateTimeBuilder.js +184 -39
- package/src/format/EnumMap.js +94 -24
- package/src/temporal/TemporalField.js +1 -1
- package/src/zone/SystemDefaultZoneRules.js +27 -4
- package/src/zone/ZoneOffsetTransition.js +2 -2
- package/typings/js-joda.d.ts +15 -0
- package/typings/js-joda.flow.js +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@js-joda/core",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "a date and time library for javascript",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"sourceMap": false,
|
|
48
48
|
"instrument": false
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "109fb8287af88f6cbc00cf7982201e1b999aa04e"
|
|
51
51
|
}
|
package/src/Duration.js
CHANGED
|
@@ -879,8 +879,11 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
879
879
|
/**
|
|
880
880
|
* Returns a copy of this duration divided by the specified value.
|
|
881
881
|
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
882
|
+
* The result is truncated toward zero to the nanosecond, as in threeten.
|
|
883
|
+
* For an integer divisor up to `MAX_SAFE_INTEGER` (positive or negative) the division is exact.
|
|
884
|
+
* Other divisors, with a fraction or beyond `MAX_SAFE_INTEGER`, are not exactly representable
|
|
885
|
+
* or not supported by threeten; they are divided by floating point arithmetic, expect floating
|
|
886
|
+
* point rounding errors.
|
|
884
887
|
*
|
|
885
888
|
* @param {Number} divisor - the value to divide the duration by, positive or negative, not zero
|
|
886
889
|
* @return {Duration} based on this duration divided by the specified divisor, not null
|
|
@@ -902,10 +905,30 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
902
905
|
seconds += 1;
|
|
903
906
|
nanos -= LocalTime.NANOS_PER_SECOND;
|
|
904
907
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
908
|
+
if (!Number.isInteger(divisor) || Math.abs(divisor) > MAX_SAFE_INTEGER) {
|
|
909
|
+
const secs = MathUtil.intDiv(seconds, divisor);
|
|
910
|
+
const remainder = MathUtil.intMod(seconds, divisor);
|
|
911
|
+
const nos = MathUtil.intDiv(remainder * LocalTime.NANOS_PER_SECOND + nanos, divisor);
|
|
912
|
+
return Duration.ofSeconds(secs, nos);
|
|
913
|
+
}
|
|
914
|
+
// the remainder operator is exact for integers, so are secs and the remainder
|
|
915
|
+
const remainder = seconds % divisor;
|
|
916
|
+
const secs = MathUtil.safeZero((seconds - remainder) / divisor);
|
|
917
|
+
const sign = ((seconds < 0 || nanos < 0) ? -1 : 1) * (divisor < 0 ? -1 : 1);
|
|
918
|
+
const absRemainder = Math.abs(remainder);
|
|
919
|
+
const absNanos = Math.abs(nanos);
|
|
920
|
+
const absDivisor = Math.abs(divisor);
|
|
921
|
+
// the largest remainder for which remainder * NANOS_PER_SECOND + nanos + divisor <= MAX_SAFE_INTEGER
|
|
922
|
+
const headroom = MAX_SAFE_INTEGER - absDivisor - absNanos;
|
|
923
|
+
let quotientNanos;
|
|
924
|
+
if (headroom >= 0 && absRemainder <= MathUtil.intDiv(headroom, LocalTime.NANOS_PER_SECOND)) {
|
|
925
|
+
// the usual case, e.g. dividedBy(60): remainder * NANOS_PER_SECOND + nanos is exact
|
|
926
|
+
quotientNanos = exactFloorDiv(absRemainder * LocalTime.NANOS_PER_SECOND + absNanos, absDivisor);
|
|
927
|
+
} else {
|
|
928
|
+
// remainder * NANOS_PER_SECOND exceeds MAX_SAFE_INTEGER, divide it without computing it
|
|
929
|
+
quotientNanos = divideScaledByNanos(absRemainder, absNanos, absDivisor);
|
|
930
|
+
}
|
|
931
|
+
return Duration.ofSeconds(secs, MathUtil.safeZero(sign * quotientNanos));
|
|
909
932
|
}
|
|
910
933
|
|
|
911
934
|
//-----------------------------------------------------------------------
|
|
@@ -1089,6 +1112,21 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
1089
1112
|
}
|
|
1090
1113
|
|
|
1091
1114
|
//-----------------------------------------------------------------------
|
|
1115
|
+
/**
|
|
1116
|
+
* Extracts the number of days in the duration.
|
|
1117
|
+
*
|
|
1118
|
+
* This returns the total number of days in the duration by dividing the
|
|
1119
|
+
* number of seconds by 86400.
|
|
1120
|
+
* This is based on the standard definition of a day as 24 hours.
|
|
1121
|
+
*
|
|
1122
|
+
* This instance is immutable and unaffected by this method call.
|
|
1123
|
+
*
|
|
1124
|
+
* @return {number} the number of days in the duration, may be negative
|
|
1125
|
+
*/
|
|
1126
|
+
toDaysPart() {
|
|
1127
|
+
return MathUtil.intDiv(this._seconds, LocalTime.SECONDS_PER_DAY);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1092
1130
|
/**
|
|
1093
1131
|
* Extracts the number of hours part in the duration.
|
|
1094
1132
|
*
|
|
@@ -1151,6 +1189,22 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
1151
1189
|
return MathUtil.intDiv(this._nanos, 1000000);
|
|
1152
1190
|
}
|
|
1153
1191
|
|
|
1192
|
+
/**
|
|
1193
|
+
* Get the nanoseconds part within seconds of the duration.
|
|
1194
|
+
*
|
|
1195
|
+
* The length of the duration is stored using two fields - seconds and nanoseconds.
|
|
1196
|
+
* The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to
|
|
1197
|
+
* the length in seconds.
|
|
1198
|
+
* The total duration is defined by calling {@link Duration.nano} and {@link Duration.seconds}.
|
|
1199
|
+
*
|
|
1200
|
+
* This instance is immutable and unaffected by this method call.
|
|
1201
|
+
*
|
|
1202
|
+
* @return {number} the nanoseconds within the second part of the length of the duration, from 0 to 999,999,999
|
|
1203
|
+
*/
|
|
1204
|
+
toNanosPart() {
|
|
1205
|
+
return this._nanos;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1154
1208
|
//-----------------------------------------------------------------------
|
|
1155
1209
|
/**
|
|
1156
1210
|
* Compares this duration to the specified {@link Duration}.
|
|
@@ -1277,6 +1331,74 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
1277
1331
|
|
|
1278
1332
|
}
|
|
1279
1333
|
|
|
1334
|
+
/**
|
|
1335
|
+
* `floor(value / divisor)` by floating point division, exact if `value + divisor <= MAX_SAFE_INTEGER`:
|
|
1336
|
+
* with `value = q * divisor + r` and `0 <= r < divisor`, the quotient `value / divisor` is at least
|
|
1337
|
+
* `1 / divisor` below `q + 1`. Rounding it up to `q + 1` would need that distance to be at most half
|
|
1338
|
+
* an ulp of `q + 1`, at most `(q + 1) / 2^53`, so `divisor * (q + 1) >= 2^53`; but
|
|
1339
|
+
* `divisor * (q + 1) <= value + divisor < 2^53`.
|
|
1340
|
+
*
|
|
1341
|
+
* @param {number} value - integer, `0 <= value`
|
|
1342
|
+
* @param {number} divisor - positive integer, `value + divisor <= MAX_SAFE_INTEGER`
|
|
1343
|
+
* @return {number}
|
|
1344
|
+
* @private
|
|
1345
|
+
*/
|
|
1346
|
+
function exactFloorDiv(value, divisor) {
|
|
1347
|
+
return Math.floor(value / divisor);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* Computes `floor((remainder * NANOS_PER_SECOND + nanos) / divisor)` exactly, by long division over
|
|
1352
|
+
* the nine decimal digits of `nanos`, without the intermediate values exceeding `MAX_SAFE_INTEGER`.
|
|
1353
|
+
* Each step divides `rem * 10 + digit`, with `rem < divisor`, by `divisor`:
|
|
1354
|
+
*
|
|
1355
|
+
* * up to `MAX_SAFE_INTEGER / 11`, `rem * 10 + digit + divisor < 11 * divisor` is small enough
|
|
1356
|
+
* for {@link exactFloorDiv}
|
|
1357
|
+
* * above, `rem * 10` itself may exceed `MAX_SAFE_INTEGER`; it is computed as ten additions of `rem`
|
|
1358
|
+
* modulo `divisor`, counting the wrap-arounds, so that all values stay below `divisor`
|
|
1359
|
+
*
|
|
1360
|
+
* @param {number} remainder - integer, `0 <= remainder < divisor`
|
|
1361
|
+
* @param {number} nanos - integer, `0 <= nanos < NANOS_PER_SECOND`
|
|
1362
|
+
* @param {number} divisor - positive integer, at most `MAX_SAFE_INTEGER`
|
|
1363
|
+
* @return {number} the quotient, `0 <= quotient < NANOS_PER_SECOND`
|
|
1364
|
+
* @private
|
|
1365
|
+
*/
|
|
1366
|
+
function divideScaledByNanos(remainder, nanos, divisor) {
|
|
1367
|
+
const directLimit = MathUtil.intDiv(MAX_SAFE_INTEGER, 11);
|
|
1368
|
+
let rem = remainder;
|
|
1369
|
+
let quotient = 0;
|
|
1370
|
+
for (let scale = 100000000; scale >= 1; scale /= 10) {
|
|
1371
|
+
const digit = MathUtil.intDiv(nanos, scale) % 10;
|
|
1372
|
+
let digitQuotient;
|
|
1373
|
+
if (divisor <= directLimit) {
|
|
1374
|
+
const value = rem * 10 + digit;
|
|
1375
|
+
digitQuotient = exactFloorDiv(value, divisor);
|
|
1376
|
+
rem = value - digitQuotient * divisor;
|
|
1377
|
+
} else {
|
|
1378
|
+
// digit < 10 < divisor, so divisor - digit and divisor - rem are positive and exact
|
|
1379
|
+
let acc = 0;
|
|
1380
|
+
digitQuotient = 0;
|
|
1381
|
+
for (let i = 0; i < 10; i++) {
|
|
1382
|
+
if (acc >= divisor - rem) {
|
|
1383
|
+
acc -= divisor - rem;
|
|
1384
|
+
digitQuotient++;
|
|
1385
|
+
} else {
|
|
1386
|
+
acc += rem;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
if (acc >= divisor - digit) {
|
|
1390
|
+
acc -= divisor - digit;
|
|
1391
|
+
digitQuotient++;
|
|
1392
|
+
} else {
|
|
1393
|
+
acc += digit;
|
|
1394
|
+
}
|
|
1395
|
+
rem = acc;
|
|
1396
|
+
}
|
|
1397
|
+
quotient = quotient * 10 + digitQuotient;
|
|
1398
|
+
}
|
|
1399
|
+
return quotient;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1280
1402
|
export function _init() {
|
|
1281
1403
|
/**
|
|
1282
1404
|
* Constant for a duration of zero.
|
package/src/Instant.js
CHANGED
|
@@ -916,6 +916,12 @@ export class Instant extends Temporal {
|
|
|
916
916
|
* @throws ArithmeticException if numeric overflow occurs
|
|
917
917
|
*/
|
|
918
918
|
toEpochMilli() {
|
|
919
|
+
if (this._seconds < 0 && this._nanos > 0) {
|
|
920
|
+
// avoid an overflow of seconds * 1000 for instants close to the minimum epoch milli
|
|
921
|
+
const millis = MathUtil.safeMultiply(this._seconds + 1, 1000);
|
|
922
|
+
const adjustment = MathUtil.intDiv(this._nanos, NANOS_PER_MILLI) - 1000;
|
|
923
|
+
return MathUtil.safeAdd(millis, adjustment);
|
|
924
|
+
}
|
|
919
925
|
const millis = MathUtil.safeMultiply(this._seconds, 1000);
|
|
920
926
|
return millis + MathUtil.intDiv(this._nanos, NANOS_PER_MILLI);
|
|
921
927
|
}
|
package/src/LocalDate.js
CHANGED
|
@@ -178,9 +178,10 @@ export class LocalDate extends ChronoLocalDate{
|
|
|
178
178
|
*
|
|
179
179
|
* @param {number} [epochDay=0] - the Epoch Day to convert, based on the epoch 1970-01-01
|
|
180
180
|
* @return {LocalDate} the local date, not null
|
|
181
|
-
* @throws {
|
|
181
|
+
* @throws {DateTimeException} if the epoch days exceeds the supported date range
|
|
182
182
|
*/
|
|
183
183
|
static ofEpochDay(epochDay=0) {
|
|
184
|
+
ChronoField.EPOCH_DAY.checkValidValue(epochDay);
|
|
184
185
|
let adjust, adjustCycles, doyEst, yearEst, zeroDay;
|
|
185
186
|
zeroDay = epochDay + DAYS_0000_TO_1970;
|
|
186
187
|
zeroDay -= 60;
|
package/src/MonthDay.js
CHANGED
|
@@ -25,7 +25,7 @@ import { ZoneId } from './ZoneId';
|
|
|
25
25
|
* A month-day in the ISO-8601 calendar system, such as `--12-03`.
|
|
26
26
|
*
|
|
27
27
|
* {@link MonthDay} is an immutable date-time object that represents the combination
|
|
28
|
-
* of a
|
|
28
|
+
* of a month and day. Any field that can be derived from a month and day, such as
|
|
29
29
|
* quarter-of-year, can be obtained.
|
|
30
30
|
*
|
|
31
31
|
* This class does not store or represent a year, time or time-zone.
|
package/src/ZonedDateTime.js
CHANGED
|
@@ -319,7 +319,7 @@ export class ZonedDateTime extends ChronoZonedDateTime {
|
|
|
319
319
|
*
|
|
320
320
|
* Converting an instant to a zoned date-time is simple as there is only one valid
|
|
321
321
|
* offset for each instant. If the valid offset is different to the offset specified,
|
|
322
|
-
* the
|
|
322
|
+
* the date-time and offset of the zoned date-time will differ from those specified.
|
|
323
323
|
*
|
|
324
324
|
* If the {@link ZoneId} to be used is a {@link ZoneOffset}, this method is equivalent
|
|
325
325
|
* to {@link of}.
|
|
@@ -13,15 +13,17 @@ import { ResolverStyle } from './ResolverStyle';
|
|
|
13
13
|
|
|
14
14
|
import { IsoChronology } from '../chrono/IsoChronology';
|
|
15
15
|
import { ChronoLocalDate } from '../chrono/ChronoLocalDate';
|
|
16
|
+
import { ChronoLocalDateTime } from '../chrono/ChronoLocalDateTime';
|
|
16
17
|
import { ChronoField } from '../temporal/ChronoField';
|
|
17
|
-
import { IsoFields } from '../temporal/IsoFields';
|
|
18
18
|
import { TemporalAccessor } from '../temporal/TemporalAccessor';
|
|
19
19
|
import { TemporalQueries } from '../temporal/TemporalQueries';
|
|
20
20
|
|
|
21
|
+
import { Instant } from '../Instant';
|
|
21
22
|
import { LocalTime } from '../LocalTime';
|
|
22
23
|
import { LocalDate } from '../LocalDate';
|
|
23
24
|
import { Period } from '../Period';
|
|
24
25
|
|
|
26
|
+
import { ZonedDateTime } from '../ZonedDateTime';
|
|
25
27
|
import { ZoneOffset } from '../ZoneOffset';
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -146,46 +148,92 @@ export class DateTimeBuilder extends TemporalAccessor {
|
|
|
146
148
|
this.fieldValues.retainAll(resolverFields);
|
|
147
149
|
}
|
|
148
150
|
// handle standard fields
|
|
149
|
-
|
|
150
|
-
if (this._resolveFields(resolverStyle)) {
|
|
151
|
-
// this._mergeInstantFields();
|
|
152
|
-
}
|
|
151
|
+
this._mergeInstantFields();
|
|
153
152
|
this._mergeDate(resolverStyle);
|
|
154
153
|
this._mergeTime(resolverStyle);
|
|
154
|
+
// the other fields may need fields produced by merging, e.g. YEAR from YEAR_OF_ERA
|
|
155
|
+
if (this._resolveFields(resolverStyle)) {
|
|
156
|
+
this._mergeInstantFields();
|
|
157
|
+
this._mergeDate(resolverStyle);
|
|
158
|
+
this._mergeTime(resolverStyle);
|
|
159
|
+
}
|
|
155
160
|
this._resolveTimeInferZeroes(resolverStyle);
|
|
156
|
-
|
|
161
|
+
this._crossCheck();
|
|
157
162
|
if (this.excessDays != null && this.excessDays.isZero() === false && this.date != null && this.time != null) {
|
|
158
163
|
this.date = this.date.plus(this.excessDays);
|
|
159
164
|
this.excessDays = Period.ZERO;
|
|
160
165
|
}
|
|
161
|
-
|
|
166
|
+
this._resolveFractional();
|
|
162
167
|
this._resolveInstant();
|
|
163
168
|
return this;
|
|
164
169
|
}
|
|
165
170
|
|
|
166
171
|
/**
|
|
167
|
-
* Resolves the
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
+
* Resolves the fields that are not {@link ChronoField}s, such as {@link IsoFields} or the
|
|
173
|
+
* fields of `@js-joda/locale` WeekFields, by delegating to their own `resolve()`, as
|
|
174
|
+
* java.time does. A field may resolve to a date, a time or a date-time, or replace itself
|
|
175
|
+
* by other fields (for example a localized day-of-week by {@link ChronoField.DAY_OF_WEEK}),
|
|
176
|
+
* so the fields are visited again after each change. Returns whether anything changed.
|
|
172
177
|
*
|
|
173
178
|
* @param {ResolverStyle} resolverStyle
|
|
174
179
|
* @return {boolean}
|
|
175
180
|
* @private
|
|
176
181
|
*/
|
|
177
182
|
_resolveFields(resolverStyle) {
|
|
178
|
-
let
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
let changes = 0;
|
|
184
|
+
let changed = true;
|
|
185
|
+
while (changed && changes < 100) {
|
|
186
|
+
changed = false;
|
|
187
|
+
const fields = this.fieldValues.keys();
|
|
188
|
+
for (let i = 0; i < fields.length; i++) {
|
|
189
|
+
const field = fields[i];
|
|
190
|
+
if (field instanceof ChronoField || typeof field.resolve !== 'function') {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const resolved = field.resolve(this.fieldValues, this, resolverStyle);
|
|
194
|
+
if (resolved != null) {
|
|
195
|
+
if (resolved instanceof ChronoLocalDateTime) {
|
|
196
|
+
this._resolveMakeChanges(field, resolved.toLocalDate());
|
|
197
|
+
this._resolveMakeChanges(field, resolved.toLocalTime());
|
|
198
|
+
} else if (resolved instanceof ChronoLocalDate || resolved instanceof LocalTime) {
|
|
199
|
+
this._resolveMakeChanges(field, resolved);
|
|
200
|
+
} else {
|
|
201
|
+
throw new DateTimeException(`Unknown type: ${resolved}`);
|
|
202
|
+
}
|
|
203
|
+
changed = true;
|
|
204
|
+
} else if (this.fieldValues.containsKey(field) === false) {
|
|
205
|
+
changed = true;
|
|
206
|
+
}
|
|
207
|
+
if (changed) {
|
|
208
|
+
changes++;
|
|
209
|
+
break; // start again, the fields have changed
|
|
185
210
|
}
|
|
186
211
|
}
|
|
187
212
|
}
|
|
188
|
-
|
|
213
|
+
if (changes === 100) {
|
|
214
|
+
throw new DateTimeException('Badly written field');
|
|
215
|
+
}
|
|
216
|
+
return changes > 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Adds a date or time resolved by a field as {@link ChronoField.EPOCH_DAY} or
|
|
221
|
+
* {@link ChronoField.NANO_OF_DAY}, to be merged with the other fields.
|
|
222
|
+
*
|
|
223
|
+
* @param {TemporalField} targetField
|
|
224
|
+
* @param {ChronoLocalDate|LocalTime} dateOrTime
|
|
225
|
+
* @private
|
|
226
|
+
*/
|
|
227
|
+
_resolveMakeChanges(targetField, dateOrTime) {
|
|
228
|
+
const isDate = dateOrTime instanceof ChronoLocalDate;
|
|
229
|
+
const field = isDate ? ChronoField.EPOCH_DAY : ChronoField.NANO_OF_DAY;
|
|
230
|
+
const value = isDate ? dateOrTime.toEpochDay() : dateOrTime.toNanoOfDay();
|
|
231
|
+
const old = this.fieldValues.get(field);
|
|
232
|
+
if (old != null && old !== value) {
|
|
233
|
+
const oldDateOrTime = isDate ? LocalDate.ofEpochDay(old) : LocalTime.ofNanoOfDay(old);
|
|
234
|
+
throw new DateTimeException(`Conflict found: ${oldDateOrTime} differs from ${dateOrTime} while resolving ${targetField}`);
|
|
235
|
+
}
|
|
236
|
+
this.fieldValues.put(field, value);
|
|
189
237
|
}
|
|
190
238
|
|
|
191
239
|
/**
|
|
@@ -211,28 +259,26 @@ export class DateTimeBuilder extends TemporalAccessor {
|
|
|
211
259
|
*/
|
|
212
260
|
_checkDate(date) {
|
|
213
261
|
if (date != null) {
|
|
262
|
+
if (this.date != null && this.date.equals(date) === false) {
|
|
263
|
+
throw new DateTimeException(`Conflict found: Fields resolved to two different dates: ${this.date} ${date}`);
|
|
264
|
+
}
|
|
214
265
|
this._addObject(date);
|
|
215
|
-
for (const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
continue;
|
|
226
|
-
} else {
|
|
227
|
-
throw ex;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
const val2 = this.fieldValues.get(field);
|
|
231
|
-
if (val1 !== val2) {
|
|
232
|
-
throw new DateTimeException(`Conflict found: Field ${field} ${val1} differs from ${field} ${val2} derived from ${date}`);
|
|
233
|
-
}
|
|
266
|
+
for (const field of this.fieldValues.keys()) {
|
|
267
|
+
if (field instanceof ChronoField && field.isDateBased()) {
|
|
268
|
+
let val1;
|
|
269
|
+
try {
|
|
270
|
+
val1 = date.getLong(field);
|
|
271
|
+
} catch (ex) {
|
|
272
|
+
if (ex instanceof DateTimeException) {
|
|
273
|
+
continue;
|
|
274
|
+
} else {
|
|
275
|
+
throw ex;
|
|
234
276
|
}
|
|
235
277
|
}
|
|
278
|
+
const val2 = this.fieldValues.get(field);
|
|
279
|
+
if (val1 !== val2) {
|
|
280
|
+
throw new DateTimeException(`Conflict found: Field ${field} ${val1} differs from ${field} ${val2} derived from ${date}`);
|
|
281
|
+
}
|
|
236
282
|
}
|
|
237
283
|
}
|
|
238
284
|
}
|
|
@@ -470,6 +516,105 @@ export class DateTimeBuilder extends TemporalAccessor {
|
|
|
470
516
|
}
|
|
471
517
|
}
|
|
472
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Converts a parsed instant-seconds into a date and a second-of-day, using the parsed zone
|
|
521
|
+
* or, if there is none, the parsed offset. Without a zone or offset, the instant-seconds
|
|
522
|
+
* are left unchanged.
|
|
523
|
+
*
|
|
524
|
+
* @private
|
|
525
|
+
*/
|
|
526
|
+
_mergeInstantFields() {
|
|
527
|
+
if (this.fieldValues.containsKey(ChronoField.INSTANT_SECONDS)) {
|
|
528
|
+
if (this.zone != null) {
|
|
529
|
+
this._mergeInstantFields0(this.zone);
|
|
530
|
+
} else {
|
|
531
|
+
const offsetSecs = this.fieldValues.get(ChronoField.OFFSET_SECONDS);
|
|
532
|
+
if (offsetSecs != null) {
|
|
533
|
+
this._mergeInstantFields0(ZoneOffset.ofTotalSeconds(offsetSecs));
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* @param {ZoneId} selectedZone
|
|
541
|
+
* @private
|
|
542
|
+
*/
|
|
543
|
+
_mergeInstantFields0(selectedZone) {
|
|
544
|
+
const instant = Instant.ofEpochSecond(this.fieldValues.remove(ChronoField.INSTANT_SECONDS));
|
|
545
|
+
const zdt = ZonedDateTime.ofInstant(instant, selectedZone);
|
|
546
|
+
if (this.date == null) {
|
|
547
|
+
this._addObject(zdt.toLocalDate());
|
|
548
|
+
} else {
|
|
549
|
+
this._resolveMakeChanges(ChronoField.INSTANT_SECONDS, zdt.toLocalDate());
|
|
550
|
+
}
|
|
551
|
+
this._addFieldValue(ChronoField.SECOND_OF_DAY, zdt.toLocalTime().toSecondOfDay());
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Checks the remaining field values against the resolved date and time, and removes the
|
|
556
|
+
* ones that match.
|
|
557
|
+
*
|
|
558
|
+
* @private
|
|
559
|
+
*/
|
|
560
|
+
_crossCheck() {
|
|
561
|
+
if (this.date != null && this.time != null) {
|
|
562
|
+
this._crossCheck0(this.date.atTime(this.time));
|
|
563
|
+
} else if (this.date != null) {
|
|
564
|
+
this._crossCheck0(this.date);
|
|
565
|
+
} else if (this.time != null) {
|
|
566
|
+
this._crossCheck0(this.time);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* @param {TemporalAccessor} temporal
|
|
572
|
+
* @private
|
|
573
|
+
*/
|
|
574
|
+
_crossCheck0(temporal) {
|
|
575
|
+
for (const field of this.fieldValues.keys()) {
|
|
576
|
+
if (temporal.isSupported(field)) {
|
|
577
|
+
let temporalValue;
|
|
578
|
+
try {
|
|
579
|
+
temporalValue = temporal.getLong(field);
|
|
580
|
+
} catch (ex) {
|
|
581
|
+
if (ex instanceof DateTimeException) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
throw ex;
|
|
585
|
+
}
|
|
586
|
+
const value = this.fieldValues.get(field);
|
|
587
|
+
if (temporalValue !== value) {
|
|
588
|
+
throw new DateTimeException(`Cross check failed: ${field} ${temporalValue} vs ${field} ${value}`);
|
|
589
|
+
}
|
|
590
|
+
this.fieldValues.remove(field);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Without a time, adds the fraction-of-second fields to a parsed instant-seconds,
|
|
597
|
+
* second-of-day or second-of-minute, with zero if no nano-of-second was parsed.
|
|
598
|
+
*
|
|
599
|
+
* @private
|
|
600
|
+
*/
|
|
601
|
+
_resolveFractional() {
|
|
602
|
+
if (this.time == null &&
|
|
603
|
+
(this.fieldValues.containsKey(ChronoField.INSTANT_SECONDS) ||
|
|
604
|
+
this.fieldValues.containsKey(ChronoField.SECOND_OF_DAY) ||
|
|
605
|
+
this.fieldValues.containsKey(ChronoField.SECOND_OF_MINUTE))) {
|
|
606
|
+
if (this.fieldValues.containsKey(ChronoField.NANO_OF_SECOND)) {
|
|
607
|
+
const nos = this.fieldValues.get(ChronoField.NANO_OF_SECOND);
|
|
608
|
+
this.fieldValues.put(ChronoField.MICRO_OF_SECOND, MathUtil.intDiv(nos, 1000));
|
|
609
|
+
this.fieldValues.put(ChronoField.MILLI_OF_SECOND, MathUtil.intDiv(nos, 1000000));
|
|
610
|
+
} else {
|
|
611
|
+
this.fieldValues.put(ChronoField.NANO_OF_SECOND, 0);
|
|
612
|
+
this.fieldValues.put(ChronoField.MICRO_OF_SECOND, 0);
|
|
613
|
+
this.fieldValues.put(ChronoField.MILLI_OF_SECOND, 0);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
473
618
|
_resolveInstant() {
|
|
474
619
|
if (this.date != null && this.time != null) {
|
|
475
620
|
const offsetSecs = this.fieldValues.get(ChronoField.OFFSET_SECONDS);
|
package/src/format/EnumMap.js
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
+
* A map from {@link TemporalField} to value.
|
|
8
|
+
*
|
|
9
|
+
* Fields are matched by identity, like the `HashMap` used by java.time. Entries are bucketed
|
|
10
|
+
* by `field.name()`, but fields with the same name are distinct keys: for example the
|
|
11
|
+
* `WeekBasedYear` field of `@js-joda/locale` WeekFields is not `IsoFields.WEEK_BASED_YEAR`.
|
|
12
|
+
*
|
|
7
13
|
* @private
|
|
8
14
|
*/
|
|
9
15
|
export class EnumMap {
|
|
@@ -11,20 +17,49 @@ export class EnumMap {
|
|
|
11
17
|
this._map = {};
|
|
12
18
|
}
|
|
13
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @param {TemporalField} key
|
|
22
|
+
* @return {?Array<{key: TemporalField, value: *}>} the entries with the name of key, or undefined
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
_bucket(key){
|
|
26
|
+
const name = key.name();
|
|
27
|
+
return Object.prototype.hasOwnProperty.call(this._map, name) ? this._map[name] : undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {TemporalField} key
|
|
32
|
+
* @return {?{key: TemporalField, value: *}} the entry of key, or undefined
|
|
33
|
+
* @private
|
|
34
|
+
*/
|
|
35
|
+
_entry(key){
|
|
36
|
+
const bucket = this._bucket(key);
|
|
37
|
+
if (bucket != null) {
|
|
38
|
+
for (let i = 0; i < bucket.length; i++) {
|
|
39
|
+
if (bucket[i].key === key) {
|
|
40
|
+
return bucket[i];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
14
47
|
putAll(otherMap){
|
|
15
|
-
|
|
16
|
-
|
|
48
|
+
const keys = otherMap.keys();
|
|
49
|
+
for (let i = 0; i < keys.length; i++) {
|
|
50
|
+
this.set(keys[i], otherMap.get(keys[i]));
|
|
17
51
|
}
|
|
18
52
|
return this;
|
|
19
53
|
}
|
|
20
54
|
|
|
21
55
|
containsKey(key){
|
|
22
|
-
|
|
23
|
-
return
|
|
56
|
+
const entry = this._entry(key);
|
|
57
|
+
return entry != null && entry.value !== undefined;
|
|
24
58
|
}
|
|
25
59
|
|
|
26
60
|
get(key) {
|
|
27
|
-
|
|
61
|
+
const entry = this._entry(key);
|
|
62
|
+
return entry != null ? entry.value : undefined;
|
|
28
63
|
}
|
|
29
64
|
|
|
30
65
|
put(key, val) {
|
|
@@ -32,39 +67,74 @@ export class EnumMap {
|
|
|
32
67
|
}
|
|
33
68
|
|
|
34
69
|
set(key, val) {
|
|
35
|
-
this.
|
|
70
|
+
const entry = this._entry(key);
|
|
71
|
+
if (entry != null) {
|
|
72
|
+
entry.value = val;
|
|
73
|
+
} else {
|
|
74
|
+
const name = key.name();
|
|
75
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
76
|
+
if (this._map.hasOwnProperty(name) === false) {
|
|
77
|
+
this._map[name] = [];
|
|
78
|
+
}
|
|
79
|
+
this._map[name].push({ key, value: val });
|
|
80
|
+
}
|
|
36
81
|
return this;
|
|
37
82
|
}
|
|
38
83
|
|
|
39
84
|
retainAll(keyList){
|
|
40
|
-
const
|
|
41
|
-
for(let i=0; i<keyList.length; i++){
|
|
42
|
-
|
|
43
|
-
|
|
85
|
+
const retained = new EnumMap();
|
|
86
|
+
for (let i = 0; i < keyList.length; i++) {
|
|
87
|
+
if (this.containsKey(keyList[i])) {
|
|
88
|
+
retained.set(keyList[i], this.get(keyList[i]));
|
|
89
|
+
}
|
|
44
90
|
}
|
|
45
|
-
this._map =
|
|
91
|
+
this._map = retained._map;
|
|
46
92
|
return this;
|
|
47
93
|
}
|
|
48
94
|
|
|
49
|
-
/**
|
|
50
|
-
* due to the bad performance of delete we just set the key entry to undefined.
|
|
51
|
-
*
|
|
52
|
-
* this might lead to issues with "null" entries. Calling clear in the end might solve the issue
|
|
53
|
-
* @param key
|
|
54
|
-
* @returns {*}
|
|
55
|
-
*/
|
|
56
95
|
remove(key){
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
96
|
+
const bucket = this._bucket(key);
|
|
97
|
+
if (bucket != null) {
|
|
98
|
+
for (let i = 0; i < bucket.length; i++) {
|
|
99
|
+
if (bucket[i].key === key) {
|
|
100
|
+
const val = bucket[i].value;
|
|
101
|
+
bucket.splice(i, 1);
|
|
102
|
+
if (bucket.length === 0) {
|
|
103
|
+
delete this._map[key.name()];
|
|
104
|
+
}
|
|
105
|
+
return val;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
61
110
|
}
|
|
62
111
|
|
|
63
|
-
|
|
64
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Returns the fields that have a value, a snapshot that is not changed by later updates.
|
|
114
|
+
*
|
|
115
|
+
* @return {TemporalField[]}
|
|
116
|
+
*/
|
|
117
|
+
keys(){
|
|
118
|
+
const keys = [];
|
|
119
|
+
for (const name in this._map) {
|
|
120
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
121
|
+
if (this._map.hasOwnProperty(name)) {
|
|
122
|
+
const bucket = this._map[name];
|
|
123
|
+
for (let i = 0; i < bucket.length; i++) {
|
|
124
|
+
if (bucket[i].value !== undefined) {
|
|
125
|
+
keys.push(bucket[i].key);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return keys;
|
|
65
131
|
}
|
|
66
132
|
|
|
67
133
|
clear(){
|
|
68
134
|
this._map = {};
|
|
69
135
|
}
|
|
136
|
+
|
|
137
|
+
toString(){
|
|
138
|
+
return `{${this.keys().map((key) => `${key}=${this.get(key)}`).join(', ')}}`;
|
|
139
|
+
}
|
|
70
140
|
}
|