Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/time/format/Parsed.java
38918 views
1
/*
2
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
/*
27
* This file is available under and governed by the GNU General Public
28
* License version 2 only, as published by the Free Software Foundation.
29
* However, the following notice accompanied the original version of this
30
* file:
31
*
32
* Copyright (c) 2008-2013, Stephen Colebourne & Michael Nascimento Santos
33
*
34
* All rights reserved.
35
*
36
* Redistribution and use in source and binary forms, with or without
37
* modification, are permitted provided that the following conditions are met:
38
*
39
* * Redistributions of source code must retain the above copyright notice,
40
* this list of conditions and the following disclaimer.
41
*
42
* * Redistributions in binary form must reproduce the above copyright notice,
43
* this list of conditions and the following disclaimer in the documentation
44
* and/or other materials provided with the distribution.
45
*
46
* * Neither the name of JSR-310 nor the names of its contributors
47
* may be used to endorse or promote products derived from this software
48
* without specific prior written permission.
49
*
50
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
54
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
55
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
56
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
57
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61
*/
62
package java.time.format;
63
64
import static java.time.temporal.ChronoField.AMPM_OF_DAY;
65
import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM;
66
import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY;
67
import static java.time.temporal.ChronoField.HOUR_OF_AMPM;
68
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
69
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
70
import static java.time.temporal.ChronoField.MICRO_OF_DAY;
71
import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
72
import static java.time.temporal.ChronoField.MILLI_OF_DAY;
73
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
74
import static java.time.temporal.ChronoField.MINUTE_OF_DAY;
75
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
76
import static java.time.temporal.ChronoField.NANO_OF_DAY;
77
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
78
import static java.time.temporal.ChronoField.OFFSET_SECONDS;
79
import static java.time.temporal.ChronoField.SECOND_OF_DAY;
80
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
81
82
import java.time.DateTimeException;
83
import java.time.Instant;
84
import java.time.LocalDate;
85
import java.time.LocalTime;
86
import java.time.Period;
87
import java.time.ZoneId;
88
import java.time.ZoneOffset;
89
import java.time.chrono.ChronoLocalDate;
90
import java.time.chrono.ChronoLocalDateTime;
91
import java.time.chrono.ChronoZonedDateTime;
92
import java.time.chrono.Chronology;
93
import java.time.temporal.ChronoField;
94
import java.time.temporal.TemporalAccessor;
95
import java.time.temporal.TemporalField;
96
import java.time.temporal.TemporalQueries;
97
import java.time.temporal.TemporalQuery;
98
import java.time.temporal.UnsupportedTemporalTypeException;
99
import java.util.HashMap;
100
import java.util.Iterator;
101
import java.util.Map;
102
import java.util.Map.Entry;
103
import java.util.Objects;
104
import java.util.Set;
105
106
/**
107
* A store of parsed data.
108
* <p>
109
* This class is used during parsing to collect the data. Part of the parsing process
110
* involves handling optional blocks and multiple copies of the data get created to
111
* support the necessary backtracking.
112
* <p>
113
* Once parsing is completed, this class can be used as the resultant {@code TemporalAccessor}.
114
* In most cases, it is only exposed once the fields have been resolved.
115
*
116
* @implSpec
117
* This class is a mutable context intended for use from a single thread.
118
* Usage of the class is thread-safe within standard parsing as a new instance of this class
119
* is automatically created for each parse and parsing is single-threaded
120
*
121
* @since 1.8
122
*/
123
final class Parsed implements TemporalAccessor {
124
// some fields are accessed using package scope from DateTimeParseContext
125
126
/**
127
* The parsed fields.
128
*/
129
final Map<TemporalField, Long> fieldValues = new HashMap<>();
130
/**
131
* The parsed zone.
132
*/
133
ZoneId zone;
134
/**
135
* The parsed chronology.
136
*/
137
Chronology chrono;
138
/**
139
* Whether a leap-second is parsed.
140
*/
141
boolean leapSecond;
142
/**
143
* The resolver style to use.
144
*/
145
private ResolverStyle resolverStyle;
146
/**
147
* The resolved date.
148
*/
149
private ChronoLocalDate date;
150
/**
151
* The resolved time.
152
*/
153
private LocalTime time;
154
/**
155
* The excess period from time-only parsing.
156
*/
157
Period excessDays = Period.ZERO;
158
159
/**
160
* Creates an instance.
161
*/
162
Parsed() {
163
}
164
165
/**
166
* Creates a copy.
167
*/
168
Parsed copy() {
169
// only copy fields used in parsing stage
170
Parsed cloned = new Parsed();
171
cloned.fieldValues.putAll(this.fieldValues);
172
cloned.zone = this.zone;
173
cloned.chrono = this.chrono;
174
cloned.leapSecond = this.leapSecond;
175
return cloned;
176
}
177
178
//-----------------------------------------------------------------------
179
@Override
180
public boolean isSupported(TemporalField field) {
181
if (fieldValues.containsKey(field) ||
182
(date != null && date.isSupported(field)) ||
183
(time != null && time.isSupported(field))) {
184
return true;
185
}
186
return field != null && (field instanceof ChronoField == false) && field.isSupportedBy(this);
187
}
188
189
@Override
190
public long getLong(TemporalField field) {
191
Objects.requireNonNull(field, "field");
192
Long value = fieldValues.get(field);
193
if (value != null) {
194
return value;
195
}
196
if (date != null && date.isSupported(field)) {
197
return date.getLong(field);
198
}
199
if (time != null && time.isSupported(field)) {
200
return time.getLong(field);
201
}
202
if (field instanceof ChronoField) {
203
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
204
}
205
return field.getFrom(this);
206
}
207
208
@SuppressWarnings("unchecked")
209
@Override
210
public <R> R query(TemporalQuery<R> query) {
211
if (query == TemporalQueries.zoneId()) {
212
return (R) zone;
213
} else if (query == TemporalQueries.chronology()) {
214
return (R) chrono;
215
} else if (query == TemporalQueries.localDate()) {
216
return (R) (date != null ? LocalDate.from(date) : null);
217
} else if (query == TemporalQueries.localTime()) {
218
return (R) time;
219
} else if (query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
220
return query.queryFrom(this);
221
} else if (query == TemporalQueries.precision()) {
222
return null; // not a complete date/time
223
}
224
// inline TemporalAccessor.super.query(query) as an optimization
225
// non-JDK classes are not permitted to make this optimization
226
return query.queryFrom(this);
227
}
228
229
//-----------------------------------------------------------------------
230
/**
231
* Resolves the fields in this context.
232
*
233
* @param resolverStyle the resolver style, not null
234
* @param resolverFields the fields to use for resolving, null for all fields
235
* @return this, for method chaining
236
* @throws DateTimeException if resolving one field results in a value for
237
* another field that is in conflict
238
*/
239
TemporalAccessor resolve(ResolverStyle resolverStyle, Set<TemporalField> resolverFields) {
240
if (resolverFields != null) {
241
fieldValues.keySet().retainAll(resolverFields);
242
}
243
this.resolverStyle = resolverStyle;
244
resolveFields();
245
resolveTimeLenient();
246
crossCheck();
247
resolvePeriod();
248
resolveFractional();
249
resolveInstant();
250
return this;
251
}
252
253
//-----------------------------------------------------------------------
254
private void resolveFields() {
255
// resolve ChronoField
256
resolveInstantFields();
257
resolveDateFields();
258
resolveTimeFields();
259
260
// if any other fields, handle them
261
// any lenient date resolution should return epoch-day
262
if (fieldValues.size() > 0) {
263
int changedCount = 0;
264
outer:
265
while (changedCount < 50) {
266
for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
267
TemporalField targetField = entry.getKey();
268
TemporalAccessor resolvedObject = targetField.resolve(fieldValues, this, resolverStyle);
269
if (resolvedObject != null) {
270
if (resolvedObject instanceof ChronoZonedDateTime) {
271
ChronoZonedDateTime<?> czdt = (ChronoZonedDateTime<?>) resolvedObject;
272
if (zone == null) {
273
zone = czdt.getZone();
274
} else if (zone.equals(czdt.getZone()) == false) {
275
throw new DateTimeException("ChronoZonedDateTime must use the effective parsed zone: " + zone);
276
}
277
resolvedObject = czdt.toLocalDateTime();
278
}
279
if (resolvedObject instanceof ChronoLocalDateTime) {
280
ChronoLocalDateTime<?> cldt = (ChronoLocalDateTime<?>) resolvedObject;
281
updateCheckConflict(cldt.toLocalTime(), Period.ZERO);
282
updateCheckConflict(cldt.toLocalDate());
283
changedCount++;
284
continue outer; // have to restart to avoid concurrent modification
285
}
286
if (resolvedObject instanceof ChronoLocalDate) {
287
updateCheckConflict((ChronoLocalDate) resolvedObject);
288
changedCount++;
289
continue outer; // have to restart to avoid concurrent modification
290
}
291
if (resolvedObject instanceof LocalTime) {
292
updateCheckConflict((LocalTime) resolvedObject, Period.ZERO);
293
changedCount++;
294
continue outer; // have to restart to avoid concurrent modification
295
}
296
throw new DateTimeException("Method resolve() can only return ChronoZonedDateTime, " +
297
"ChronoLocalDateTime, ChronoLocalDate or LocalTime");
298
} else if (fieldValues.containsKey(targetField) == false) {
299
changedCount++;
300
continue outer; // have to restart to avoid concurrent modification
301
}
302
}
303
break;
304
}
305
if (changedCount == 50) { // catch infinite loops
306
throw new DateTimeException("One of the parsed fields has an incorrectly implemented resolve method");
307
}
308
// if something changed then have to redo ChronoField resolve
309
if (changedCount > 0) {
310
resolveInstantFields();
311
resolveDateFields();
312
resolveTimeFields();
313
}
314
}
315
}
316
317
private void updateCheckConflict(TemporalField targetField, TemporalField changeField, Long changeValue) {
318
Long old = fieldValues.put(changeField, changeValue);
319
if (old != null && old.longValue() != changeValue.longValue()) {
320
throw new DateTimeException("Conflict found: " + changeField + " " + old +
321
" differs from " + changeField + " " + changeValue +
322
" while resolving " + targetField);
323
}
324
}
325
326
//-----------------------------------------------------------------------
327
private void resolveInstantFields() {
328
// resolve parsed instant seconds to date and time if zone available
329
if (fieldValues.containsKey(INSTANT_SECONDS)) {
330
if (zone != null) {
331
resolveInstantFields0(zone);
332
} else {
333
Long offsetSecs = fieldValues.get(OFFSET_SECONDS);
334
if (offsetSecs != null) {
335
ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue());
336
resolveInstantFields0(offset);
337
}
338
}
339
}
340
}
341
342
private void resolveInstantFields0(ZoneId selectedZone) {
343
Instant instant = Instant.ofEpochSecond(fieldValues.remove(INSTANT_SECONDS));
344
ChronoZonedDateTime<?> zdt = chrono.zonedDateTime(instant, selectedZone);
345
updateCheckConflict(zdt.toLocalDate());
346
updateCheckConflict(INSTANT_SECONDS, SECOND_OF_DAY, (long) zdt.toLocalTime().toSecondOfDay());
347
}
348
349
//-----------------------------------------------------------------------
350
private void resolveDateFields() {
351
updateCheckConflict(chrono.resolveDate(fieldValues, resolverStyle));
352
}
353
354
private void updateCheckConflict(ChronoLocalDate cld) {
355
if (date != null) {
356
if (cld != null && date.equals(cld) == false) {
357
throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld);
358
}
359
} else if (cld != null) {
360
if (chrono.equals(cld.getChronology()) == false) {
361
throw new DateTimeException("ChronoLocalDate must use the effective parsed chronology: " + chrono);
362
}
363
date = cld;
364
}
365
}
366
367
//-----------------------------------------------------------------------
368
private void resolveTimeFields() {
369
// simplify fields
370
if (fieldValues.containsKey(CLOCK_HOUR_OF_DAY)) {
371
// lenient allows anything, smart allows 0-24, strict allows 1-24
372
long ch = fieldValues.remove(CLOCK_HOUR_OF_DAY);
373
if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) {
374
CLOCK_HOUR_OF_DAY.checkValidValue(ch);
375
}
376
updateCheckConflict(CLOCK_HOUR_OF_DAY, HOUR_OF_DAY, ch == 24 ? 0 : ch);
377
}
378
if (fieldValues.containsKey(CLOCK_HOUR_OF_AMPM)) {
379
// lenient allows anything, smart allows 0-12, strict allows 1-12
380
long ch = fieldValues.remove(CLOCK_HOUR_OF_AMPM);
381
if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) {
382
CLOCK_HOUR_OF_AMPM.checkValidValue(ch);
383
}
384
updateCheckConflict(CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM, ch == 12 ? 0 : ch);
385
}
386
if (fieldValues.containsKey(AMPM_OF_DAY) && fieldValues.containsKey(HOUR_OF_AMPM)) {
387
long ap = fieldValues.remove(AMPM_OF_DAY);
388
long hap = fieldValues.remove(HOUR_OF_AMPM);
389
if (resolverStyle == ResolverStyle.LENIENT) {
390
updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, Math.addExact(Math.multiplyExact(ap, 12), hap));
391
} else { // STRICT or SMART
392
AMPM_OF_DAY.checkValidValue(ap);
393
HOUR_OF_AMPM.checkValidValue(ap);
394
updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap);
395
}
396
}
397
if (fieldValues.containsKey(NANO_OF_DAY)) {
398
long nod = fieldValues.remove(NANO_OF_DAY);
399
if (resolverStyle != ResolverStyle.LENIENT) {
400
NANO_OF_DAY.checkValidValue(nod);
401
}
402
updateCheckConflict(NANO_OF_DAY, HOUR_OF_DAY, nod / 3600_000_000_000L);
403
updateCheckConflict(NANO_OF_DAY, MINUTE_OF_HOUR, (nod / 60_000_000_000L) % 60);
404
updateCheckConflict(NANO_OF_DAY, SECOND_OF_MINUTE, (nod / 1_000_000_000L) % 60);
405
updateCheckConflict(NANO_OF_DAY, NANO_OF_SECOND, nod % 1_000_000_000L);
406
}
407
if (fieldValues.containsKey(MICRO_OF_DAY)) {
408
long cod = fieldValues.remove(MICRO_OF_DAY);
409
if (resolverStyle != ResolverStyle.LENIENT) {
410
MICRO_OF_DAY.checkValidValue(cod);
411
}
412
updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1_000_000L);
413
updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1_000_000L);
414
}
415
if (fieldValues.containsKey(MILLI_OF_DAY)) {
416
long lod = fieldValues.remove(MILLI_OF_DAY);
417
if (resolverStyle != ResolverStyle.LENIENT) {
418
MILLI_OF_DAY.checkValidValue(lod);
419
}
420
updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1_000);
421
updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1_000);
422
}
423
if (fieldValues.containsKey(SECOND_OF_DAY)) {
424
long sod = fieldValues.remove(SECOND_OF_DAY);
425
if (resolverStyle != ResolverStyle.LENIENT) {
426
SECOND_OF_DAY.checkValidValue(sod);
427
}
428
updateCheckConflict(SECOND_OF_DAY, HOUR_OF_DAY, sod / 3600);
429
updateCheckConflict(SECOND_OF_DAY, MINUTE_OF_HOUR, (sod / 60) % 60);
430
updateCheckConflict(SECOND_OF_DAY, SECOND_OF_MINUTE, sod % 60);
431
}
432
if (fieldValues.containsKey(MINUTE_OF_DAY)) {
433
long mod = fieldValues.remove(MINUTE_OF_DAY);
434
if (resolverStyle != ResolverStyle.LENIENT) {
435
MINUTE_OF_DAY.checkValidValue(mod);
436
}
437
updateCheckConflict(MINUTE_OF_DAY, HOUR_OF_DAY, mod / 60);
438
updateCheckConflict(MINUTE_OF_DAY, MINUTE_OF_HOUR, mod % 60);
439
}
440
441
// combine partial second fields strictly, leaving lenient expansion to later
442
if (fieldValues.containsKey(NANO_OF_SECOND)) {
443
long nos = fieldValues.get(NANO_OF_SECOND);
444
if (resolverStyle != ResolverStyle.LENIENT) {
445
NANO_OF_SECOND.checkValidValue(nos);
446
}
447
if (fieldValues.containsKey(MICRO_OF_SECOND)) {
448
long cos = fieldValues.remove(MICRO_OF_SECOND);
449
if (resolverStyle != ResolverStyle.LENIENT) {
450
MICRO_OF_SECOND.checkValidValue(cos);
451
}
452
nos = cos * 1000 + (nos % 1000);
453
updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, nos);
454
}
455
if (fieldValues.containsKey(MILLI_OF_SECOND)) {
456
long los = fieldValues.remove(MILLI_OF_SECOND);
457
if (resolverStyle != ResolverStyle.LENIENT) {
458
MILLI_OF_SECOND.checkValidValue(los);
459
}
460
updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, los * 1_000_000L + (nos % 1_000_000L));
461
}
462
}
463
464
// convert to time if all four fields available (optimization)
465
if (fieldValues.containsKey(HOUR_OF_DAY) && fieldValues.containsKey(MINUTE_OF_HOUR) &&
466
fieldValues.containsKey(SECOND_OF_MINUTE) && fieldValues.containsKey(NANO_OF_SECOND)) {
467
long hod = fieldValues.remove(HOUR_OF_DAY);
468
long moh = fieldValues.remove(MINUTE_OF_HOUR);
469
long som = fieldValues.remove(SECOND_OF_MINUTE);
470
long nos = fieldValues.remove(NANO_OF_SECOND);
471
resolveTime(hod, moh, som, nos);
472
}
473
}
474
475
private void resolveTimeLenient() {
476
// leniently create a time from incomplete information
477
// done after everything else as it creates information from nothing
478
// which would break updateCheckConflict(field)
479
480
if (time == null) {
481
// NANO_OF_SECOND merged with MILLI/MICRO above
482
if (fieldValues.containsKey(MILLI_OF_SECOND)) {
483
long los = fieldValues.remove(MILLI_OF_SECOND);
484
if (fieldValues.containsKey(MICRO_OF_SECOND)) {
485
// merge milli-of-second and micro-of-second for better error message
486
long cos = los * 1_000 + (fieldValues.get(MICRO_OF_SECOND) % 1_000);
487
updateCheckConflict(MILLI_OF_SECOND, MICRO_OF_SECOND, cos);
488
fieldValues.remove(MICRO_OF_SECOND);
489
fieldValues.put(NANO_OF_SECOND, cos * 1_000L);
490
} else {
491
// convert milli-of-second to nano-of-second
492
fieldValues.put(NANO_OF_SECOND, los * 1_000_000L);
493
}
494
} else if (fieldValues.containsKey(MICRO_OF_SECOND)) {
495
// convert micro-of-second to nano-of-second
496
long cos = fieldValues.remove(MICRO_OF_SECOND);
497
fieldValues.put(NANO_OF_SECOND, cos * 1_000L);
498
}
499
500
// merge hour/minute/second/nano leniently
501
Long hod = fieldValues.get(HOUR_OF_DAY);
502
if (hod != null) {
503
Long moh = fieldValues.get(MINUTE_OF_HOUR);
504
Long som = fieldValues.get(SECOND_OF_MINUTE);
505
Long nos = fieldValues.get(NANO_OF_SECOND);
506
507
// check for invalid combinations that cannot be defaulted
508
if ((moh == null && (som != null || nos != null)) ||
509
(moh != null && som == null && nos != null)) {
510
return;
511
}
512
513
// default as necessary and build time
514
long mohVal = (moh != null ? moh : 0);
515
long somVal = (som != null ? som : 0);
516
long nosVal = (nos != null ? nos : 0);
517
resolveTime(hod, mohVal, somVal, nosVal);
518
fieldValues.remove(HOUR_OF_DAY);
519
fieldValues.remove(MINUTE_OF_HOUR);
520
fieldValues.remove(SECOND_OF_MINUTE);
521
fieldValues.remove(NANO_OF_SECOND);
522
}
523
}
524
525
// validate remaining
526
if (resolverStyle != ResolverStyle.LENIENT && fieldValues.size() > 0) {
527
for (Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
528
TemporalField field = entry.getKey();
529
if (field instanceof ChronoField && field.isTimeBased()) {
530
((ChronoField) field).checkValidValue(entry.getValue());
531
}
532
}
533
}
534
}
535
536
private void resolveTime(long hod, long moh, long som, long nos) {
537
if (resolverStyle == ResolverStyle.LENIENT) {
538
long totalNanos = Math.multiplyExact(hod, 3600_000_000_000L);
539
totalNanos = Math.addExact(totalNanos, Math.multiplyExact(moh, 60_000_000_000L));
540
totalNanos = Math.addExact(totalNanos, Math.multiplyExact(som, 1_000_000_000L));
541
totalNanos = Math.addExact(totalNanos, nos);
542
int excessDays = (int) Math.floorDiv(totalNanos, 86400_000_000_000L); // safe int cast
543
long nod = Math.floorMod(totalNanos, 86400_000_000_000L);
544
updateCheckConflict(LocalTime.ofNanoOfDay(nod), Period.ofDays(excessDays));
545
} else { // STRICT or SMART
546
int mohVal = MINUTE_OF_HOUR.checkValidIntValue(moh);
547
int nosVal = NANO_OF_SECOND.checkValidIntValue(nos);
548
// handle 24:00 end of day
549
if (resolverStyle == ResolverStyle.SMART && hod == 24 && mohVal == 0 && som == 0 && nosVal == 0) {
550
updateCheckConflict(LocalTime.MIDNIGHT, Period.ofDays(1));
551
} else {
552
int hodVal = HOUR_OF_DAY.checkValidIntValue(hod);
553
int somVal = SECOND_OF_MINUTE.checkValidIntValue(som);
554
updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal), Period.ZERO);
555
}
556
}
557
}
558
559
private void resolvePeriod() {
560
// add whole days if we have both date and time
561
if (date != null && time != null && excessDays.isZero() == false) {
562
date = date.plus(excessDays);
563
excessDays = Period.ZERO;
564
}
565
}
566
567
private void resolveFractional() {
568
// ensure fractional seconds available as ChronoField requires
569
// resolveTimeLenient() will have merged MICRO_OF_SECOND/MILLI_OF_SECOND to NANO_OF_SECOND
570
if (time == null &&
571
(fieldValues.containsKey(INSTANT_SECONDS) ||
572
fieldValues.containsKey(SECOND_OF_DAY) ||
573
fieldValues.containsKey(SECOND_OF_MINUTE))) {
574
if (fieldValues.containsKey(NANO_OF_SECOND)) {
575
long nos = fieldValues.get(NANO_OF_SECOND);
576
fieldValues.put(MICRO_OF_SECOND, nos / 1000);
577
fieldValues.put(MILLI_OF_SECOND, nos / 1000000);
578
} else {
579
fieldValues.put(NANO_OF_SECOND, 0L);
580
fieldValues.put(MICRO_OF_SECOND, 0L);
581
fieldValues.put(MILLI_OF_SECOND, 0L);
582
}
583
}
584
}
585
586
private void resolveInstant() {
587
// add instant seconds if we have date, time and zone
588
if (date != null && time != null) {
589
if (zone != null) {
590
long instant = date.atTime(time).atZone(zone).getLong(ChronoField.INSTANT_SECONDS);
591
fieldValues.put(INSTANT_SECONDS, instant);
592
} else {
593
Long offsetSecs = fieldValues.get(OFFSET_SECONDS);
594
if (offsetSecs != null) {
595
ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue());
596
long instant = date.atTime(time).atZone(offset).getLong(ChronoField.INSTANT_SECONDS);
597
fieldValues.put(INSTANT_SECONDS, instant);
598
}
599
}
600
}
601
}
602
603
private void updateCheckConflict(LocalTime timeToSet, Period periodToSet) {
604
if (time != null) {
605
if (time.equals(timeToSet) == false) {
606
throw new DateTimeException("Conflict found: Fields resolved to different times: " + time + " " + timeToSet);
607
}
608
if (excessDays.isZero() == false && periodToSet.isZero() == false && excessDays.equals(periodToSet) == false) {
609
throw new DateTimeException("Conflict found: Fields resolved to different excess periods: " + excessDays + " " + periodToSet);
610
} else {
611
excessDays = periodToSet;
612
}
613
} else {
614
time = timeToSet;
615
excessDays = periodToSet;
616
}
617
}
618
619
//-----------------------------------------------------------------------
620
private void crossCheck() {
621
// only cross-check date, time and date-time
622
// avoid object creation if possible
623
if (date != null) {
624
crossCheck(date);
625
}
626
if (time != null) {
627
crossCheck(time);
628
if (date != null && fieldValues.size() > 0) {
629
crossCheck(date.atTime(time));
630
}
631
}
632
}
633
634
private void crossCheck(TemporalAccessor target) {
635
for (Iterator<Entry<TemporalField, Long>> it = fieldValues.entrySet().iterator(); it.hasNext(); ) {
636
Entry<TemporalField, Long> entry = it.next();
637
TemporalField field = entry.getKey();
638
if (target.isSupported(field)) {
639
long val1;
640
try {
641
val1 = target.getLong(field);
642
} catch (RuntimeException ex) {
643
continue;
644
}
645
long val2 = entry.getValue();
646
if (val1 != val2) {
647
throw new DateTimeException("Conflict found: Field " + field + " " + val1 +
648
" differs from " + field + " " + val2 + " derived from " + target);
649
}
650
it.remove();
651
}
652
}
653
}
654
655
//-----------------------------------------------------------------------
656
@Override
657
public String toString() {
658
StringBuilder buf = new StringBuilder(64);
659
buf.append(fieldValues).append(',').append(chrono);
660
if (zone != null) {
661
buf.append(',').append(zone);
662
}
663
if (date != null || time != null) {
664
buf.append(" resolved to ");
665
if (date != null) {
666
buf.append(date);
667
if (time != null) {
668
buf.append('T').append(time);
669
}
670
} else {
671
buf.append(time);
672
}
673
}
674
return buf.toString();
675
}
676
677
}
678
679