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/chrono/AbstractChronology.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) 2012, 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.chrono;
63
64
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
65
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
66
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
67
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
68
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
69
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
70
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
71
import static java.time.temporal.ChronoField.EPOCH_DAY;
72
import static java.time.temporal.ChronoField.ERA;
73
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
74
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
75
import static java.time.temporal.ChronoField.YEAR;
76
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
77
import static java.time.temporal.ChronoUnit.DAYS;
78
import static java.time.temporal.ChronoUnit.MONTHS;
79
import static java.time.temporal.ChronoUnit.WEEKS;
80
import static java.time.temporal.TemporalAdjusters.nextOrSame;
81
82
import java.io.DataInput;
83
import java.io.DataOutput;
84
import java.io.IOException;
85
import java.io.InvalidObjectException;
86
import java.io.ObjectInputStream;
87
import java.io.ObjectStreamException;
88
import java.io.Serializable;
89
import java.time.DateTimeException;
90
import java.time.DayOfWeek;
91
import java.time.format.ResolverStyle;
92
import java.time.temporal.ChronoField;
93
import java.time.temporal.TemporalAdjusters;
94
import java.time.temporal.TemporalField;
95
import java.time.temporal.ValueRange;
96
import java.util.Comparator;
97
import java.util.HashSet;
98
import java.util.List;
99
import java.util.Locale;
100
import java.util.Map;
101
import java.util.Objects;
102
import java.util.ServiceLoader;
103
import java.util.Set;
104
import java.util.concurrent.ConcurrentHashMap;
105
106
import sun.util.logging.PlatformLogger;
107
108
/**
109
* An abstract implementation of a calendar system, used to organize and identify dates.
110
* <p>
111
* The main date and time API is built on the ISO calendar system.
112
* The chronology operates behind the scenes to represent the general concept of a calendar system.
113
* <p>
114
* See {@link Chronology} for more details.
115
*
116
* @implSpec
117
* This class is separated from the {@code Chronology} interface so that the static methods
118
* are not inherited. While {@code Chronology} can be implemented directly, it is strongly
119
* recommended to extend this abstract class instead.
120
* <p>
121
* This class must be implemented with care to ensure other classes operate correctly.
122
* All implementations that can be instantiated must be final, immutable and thread-safe.
123
* Subclasses should be Serializable wherever possible.
124
*
125
* @since 1.8
126
*/
127
public abstract class AbstractChronology implements Chronology {
128
129
/**
130
* ChronoLocalDate order constant.
131
*/
132
static final Comparator<ChronoLocalDate> DATE_ORDER =
133
(Comparator<ChronoLocalDate> & Serializable) (date1, date2) -> {
134
return Long.compare(date1.toEpochDay(), date2.toEpochDay());
135
};
136
/**
137
* ChronoLocalDateTime order constant.
138
*/
139
static final Comparator<ChronoLocalDateTime<? extends ChronoLocalDate>> DATE_TIME_ORDER =
140
(Comparator<ChronoLocalDateTime<? extends ChronoLocalDate>> & Serializable) (dateTime1, dateTime2) -> {
141
int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());
142
if (cmp == 0) {
143
cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());
144
}
145
return cmp;
146
};
147
/**
148
* ChronoZonedDateTime order constant.
149
*/
150
static final Comparator<ChronoZonedDateTime<?>> INSTANT_ORDER =
151
(Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
152
int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
153
if (cmp == 0) {
154
cmp = Long.compare(dateTime1.toLocalTime().getNano(), dateTime2.toLocalTime().getNano());
155
}
156
return cmp;
157
};
158
159
/**
160
* Map of available calendars by ID.
161
*/
162
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_ID = new ConcurrentHashMap<>();
163
/**
164
* Map of available calendars by calendar type.
165
*/
166
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_TYPE = new ConcurrentHashMap<>();
167
168
/**
169
* Register a Chronology by its ID and type for lookup by {@link #of(String)}.
170
* Chronologies must not be registered until they are completely constructed.
171
* Specifically, not in the constructor of Chronology.
172
*
173
* @param chrono the chronology to register; not null
174
* @return the already registered Chronology if any, may be null
175
*/
176
static Chronology registerChrono(Chronology chrono) {
177
return registerChrono(chrono, chrono.getId());
178
}
179
180
/**
181
* Register a Chronology by ID and type for lookup by {@link #of(String)}.
182
* Chronos must not be registered until they are completely constructed.
183
* Specifically, not in the constructor of Chronology.
184
*
185
* @param chrono the chronology to register; not null
186
* @param id the ID to register the chronology; not null
187
* @return the already registered Chronology if any, may be null
188
*/
189
static Chronology registerChrono(Chronology chrono, String id) {
190
Chronology prev = CHRONOS_BY_ID.putIfAbsent(id, chrono);
191
if (prev == null) {
192
String type = chrono.getCalendarType();
193
if (type != null) {
194
CHRONOS_BY_TYPE.putIfAbsent(type, chrono);
195
}
196
}
197
return prev;
198
}
199
200
/**
201
* Initialization of the maps from id and type to Chronology.
202
* The ServiceLoader is used to find and register any implementations
203
* of {@link java.time.chrono.AbstractChronology} found in the bootclass loader.
204
* The built-in chronologies are registered explicitly.
205
* Calendars configured via the Thread's context classloader are local
206
* to that thread and are ignored.
207
* <p>
208
* The initialization is done only once using the registration
209
* of the IsoChronology as the test and the final step.
210
* Multiple threads may perform the initialization concurrently.
211
* Only the first registration of each Chronology is retained by the
212
* ConcurrentHashMap.
213
* @return true if the cache was initialized
214
*/
215
private static boolean initCache() {
216
if (CHRONOS_BY_ID.get("ISO") == null) {
217
// Initialization is incomplete
218
219
// Register built-in Chronologies
220
registerChrono(HijrahChronology.INSTANCE);
221
registerChrono(JapaneseChronology.INSTANCE);
222
registerChrono(MinguoChronology.INSTANCE);
223
registerChrono(ThaiBuddhistChronology.INSTANCE);
224
225
// Register Chronologies from the ServiceLoader
226
@SuppressWarnings("rawtypes")
227
ServiceLoader<AbstractChronology> loader = ServiceLoader.load(AbstractChronology.class, null);
228
for (AbstractChronology chrono : loader) {
229
String id = chrono.getId();
230
if (id.equals("ISO") || registerChrono(chrono) != null) {
231
// Log the attempt to replace an existing Chronology
232
PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
233
logger.warning("Ignoring duplicate Chronology, from ServiceLoader configuration " + id);
234
}
235
}
236
237
// finally, register IsoChronology to mark initialization is complete
238
registerChrono(IsoChronology.INSTANCE);
239
return true;
240
}
241
return false;
242
}
243
244
//-----------------------------------------------------------------------
245
/**
246
* Obtains an instance of {@code Chronology} from a locale.
247
* <p>
248
* See {@link Chronology#ofLocale(Locale)}.
249
*
250
* @param locale the locale to use to obtain the calendar system, not null
251
* @return the calendar system associated with the locale, not null
252
* @throws java.time.DateTimeException if the locale-specified calendar cannot be found
253
*/
254
static Chronology ofLocale(Locale locale) {
255
Objects.requireNonNull(locale, "locale");
256
String type = locale.getUnicodeLocaleType("ca");
257
if (type == null || "iso".equals(type) || "iso8601".equals(type)) {
258
return IsoChronology.INSTANCE;
259
}
260
// Not pre-defined; lookup by the type
261
do {
262
Chronology chrono = CHRONOS_BY_TYPE.get(type);
263
if (chrono != null) {
264
return chrono;
265
}
266
// If not found, do the initialization (once) and repeat the lookup
267
} while (initCache());
268
269
// Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
270
// Application provided Chronologies must not be cached
271
@SuppressWarnings("rawtypes")
272
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
273
for (Chronology chrono : loader) {
274
if (type.equals(chrono.getCalendarType())) {
275
return chrono;
276
}
277
}
278
throw new DateTimeException("Unknown calendar system: " + type);
279
}
280
281
//-----------------------------------------------------------------------
282
/**
283
* Obtains an instance of {@code Chronology} from a chronology ID or
284
* calendar system type.
285
* <p>
286
* See {@link Chronology#of(String)}.
287
*
288
* @param id the chronology ID or calendar system type, not null
289
* @return the chronology with the identifier requested, not null
290
* @throws java.time.DateTimeException if the chronology cannot be found
291
*/
292
static Chronology of(String id) {
293
Objects.requireNonNull(id, "id");
294
do {
295
Chronology chrono = of0(id);
296
if (chrono != null) {
297
return chrono;
298
}
299
// If not found, do the initialization (once) and repeat the lookup
300
} while (initCache());
301
302
// Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
303
// Application provided Chronologies must not be cached
304
@SuppressWarnings("rawtypes")
305
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
306
for (Chronology chrono : loader) {
307
if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) {
308
return chrono;
309
}
310
}
311
throw new DateTimeException("Unknown chronology: " + id);
312
}
313
314
/**
315
* Obtains an instance of {@code Chronology} from a chronology ID or
316
* calendar system type.
317
*
318
* @param id the chronology ID or calendar system type, not null
319
* @return the chronology with the identifier requested, or {@code null} if not found
320
*/
321
private static Chronology of0(String id) {
322
Chronology chrono = CHRONOS_BY_ID.get(id);
323
if (chrono == null) {
324
chrono = CHRONOS_BY_TYPE.get(id);
325
}
326
return chrono;
327
}
328
329
/**
330
* Returns the available chronologies.
331
* <p>
332
* Each returned {@code Chronology} is available for use in the system.
333
* The set of chronologies includes the system chronologies and
334
* any chronologies provided by the application via ServiceLoader
335
* configuration.
336
*
337
* @return the independent, modifiable set of the available chronology IDs, not null
338
*/
339
static Set<Chronology> getAvailableChronologies() {
340
initCache(); // force initialization
341
HashSet<Chronology> chronos = new HashSet<>(CHRONOS_BY_ID.values());
342
343
/// Add in Chronologies from the ServiceLoader configuration
344
@SuppressWarnings("rawtypes")
345
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
346
for (Chronology chrono : loader) {
347
chronos.add(chrono);
348
}
349
return chronos;
350
}
351
352
//-----------------------------------------------------------------------
353
/**
354
* Creates an instance.
355
*/
356
protected AbstractChronology() {
357
}
358
359
//-----------------------------------------------------------------------
360
/**
361
* Resolves parsed {@code ChronoField} values into a date during parsing.
362
* <p>
363
* Most {@code TemporalField} implementations are resolved using the
364
* resolve method on the field. By contrast, the {@code ChronoField} class
365
* defines fields that only have meaning relative to the chronology.
366
* As such, {@code ChronoField} date fields are resolved here in the
367
* context of a specific chronology.
368
* <p>
369
* {@code ChronoField} instances are resolved by this method, which may
370
* be overridden in subclasses.
371
* <ul>
372
* <li>{@code EPOCH_DAY} - If present, this is converted to a date and
373
* all other date fields are then cross-checked against the date.
374
* <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
375
* {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
376
* then the field is validated.
377
* <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
378
* are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
379
* range is not validated, in smart and strict mode it is. The {@code ERA} is
380
* validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
381
* present, and the mode is smart or lenient, then the last available era
382
* is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
383
* left untouched. If only the {@code ERA} is present, then it is left untouched.
384
* <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
385
* If all three are present, then they are combined to form a date.
386
* In all three modes, the {@code YEAR} is validated.
387
* If the mode is smart or strict, then the month and day are validated.
388
* If the mode is lenient, then the date is combined in a manner equivalent to
389
* creating a date on the first day of the first month in the requested year,
390
* then adding the difference in months, then the difference in days.
391
* If the mode is smart, and the day-of-month is greater than the maximum for
392
* the year-month, then the day-of-month is adjusted to the last day-of-month.
393
* If the mode is strict, then the three fields must form a valid date.
394
* <li>{@code YEAR} and {@code DAY_OF_YEAR} -
395
* If both are present, then they are combined to form a date.
396
* In all three modes, the {@code YEAR} is validated.
397
* If the mode is lenient, then the date is combined in a manner equivalent to
398
* creating a date on the first day of the requested year, then adding
399
* the difference in days.
400
* If the mode is smart or strict, then the two fields must form a valid date.
401
* <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
402
* {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
403
* If all four are present, then they are combined to form a date.
404
* In all three modes, the {@code YEAR} is validated.
405
* If the mode is lenient, then the date is combined in a manner equivalent to
406
* creating a date on the first day of the first month in the requested year, then adding
407
* the difference in months, then the difference in weeks, then in days.
408
* If the mode is smart or strict, then the all four fields are validated to
409
* their outer ranges. The date is then combined in a manner equivalent to
410
* creating a date on the first day of the requested year and month, then adding
411
* the amount in weeks and days to reach their values. If the mode is strict,
412
* the date is additionally validated to check that the day and week adjustment
413
* did not change the month.
414
* <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
415
* {@code DAY_OF_WEEK} - If all four are present, then they are combined to
416
* form a date. The approach is the same as described above for
417
* years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
418
* The day-of-week is adjusted as the next or same matching day-of-week once
419
* the years, months and weeks have been handled.
420
* <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
421
* If all three are present, then they are combined to form a date.
422
* In all three modes, the {@code YEAR} is validated.
423
* If the mode is lenient, then the date is combined in a manner equivalent to
424
* creating a date on the first day of the requested year, then adding
425
* the difference in weeks, then in days.
426
* If the mode is smart or strict, then the all three fields are validated to
427
* their outer ranges. The date is then combined in a manner equivalent to
428
* creating a date on the first day of the requested year, then adding
429
* the amount in weeks and days to reach their values. If the mode is strict,
430
* the date is additionally validated to check that the day and week adjustment
431
* did not change the year.
432
* <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
433
* If all three are present, then they are combined to form a date.
434
* The approach is the same as described above for years and weeks in
435
* {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
436
* next or same matching day-of-week once the years and weeks have been handled.
437
* </ul>
438
* <p>
439
* The default implementation is suitable for most calendar systems.
440
* If {@link java.time.temporal.ChronoField#YEAR_OF_ERA} is found without an {@link java.time.temporal.ChronoField#ERA}
441
* then the last era in {@link #eras()} is used.
442
* The implementation assumes a 7 day week, that the first day-of-month
443
* has the value 1, that first day-of-year has the value 1, and that the
444
* first of the month and year always exists.
445
*
446
* @param fieldValues the map of fields to values, which can be updated, not null
447
* @param resolverStyle the requested type of resolve, not null
448
* @return the resolved date, null if insufficient information to create a date
449
* @throws java.time.DateTimeException if the date cannot be resolved, typically
450
* because of a conflict in the input data
451
*/
452
@Override
453
public ChronoLocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
454
// check epoch-day before inventing era
455
if (fieldValues.containsKey(EPOCH_DAY)) {
456
return dateEpochDay(fieldValues.remove(EPOCH_DAY));
457
}
458
459
// fix proleptic month before inventing era
460
resolveProlepticMonth(fieldValues, resolverStyle);
461
462
// invent era if necessary to resolve year-of-era
463
ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle);
464
if (resolved != null) {
465
return resolved;
466
}
467
468
// build date
469
if (fieldValues.containsKey(YEAR)) {
470
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
471
if (fieldValues.containsKey(DAY_OF_MONTH)) {
472
return resolveYMD(fieldValues, resolverStyle);
473
}
474
if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
475
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
476
return resolveYMAA(fieldValues, resolverStyle);
477
}
478
if (fieldValues.containsKey(DAY_OF_WEEK)) {
479
return resolveYMAD(fieldValues, resolverStyle);
480
}
481
}
482
}
483
if (fieldValues.containsKey(DAY_OF_YEAR)) {
484
return resolveYD(fieldValues, resolverStyle);
485
}
486
if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
487
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
488
return resolveYAA(fieldValues, resolverStyle);
489
}
490
if (fieldValues.containsKey(DAY_OF_WEEK)) {
491
return resolveYAD(fieldValues, resolverStyle);
492
}
493
}
494
}
495
return null;
496
}
497
498
void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
499
Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
500
if (pMonth != null) {
501
if (resolverStyle != ResolverStyle.LENIENT) {
502
PROLEPTIC_MONTH.checkValidValue(pMonth);
503
}
504
// first day-of-month is likely to be safest for setting proleptic-month
505
// cannot add to year zero, as not all chronologies have a year zero
506
ChronoLocalDate chronoDate = dateNow()
507
.with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
508
addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
509
addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
510
}
511
}
512
513
ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
514
Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
515
if (yoeLong != null) {
516
Long eraLong = fieldValues.remove(ERA);
517
int yoe;
518
if (resolverStyle != ResolverStyle.LENIENT) {
519
yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
520
} else {
521
yoe = Math.toIntExact(yoeLong);
522
}
523
if (eraLong != null) {
524
Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));
525
addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
526
} else {
527
if (fieldValues.containsKey(YEAR)) {
528
int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
529
ChronoLocalDate chronoDate = dateYearDay(year, 1);
530
addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
531
} else if (resolverStyle == ResolverStyle.STRICT) {
532
// do not invent era if strict
533
// reinstate the field removed earlier, no cross-check issues
534
fieldValues.put(YEAR_OF_ERA, yoeLong);
535
} else {
536
List<Era> eras = eras();
537
if (eras.isEmpty()) {
538
addFieldValue(fieldValues, YEAR, yoe);
539
} else {
540
Era eraObj = eras.get(eras.size() - 1);
541
addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
542
}
543
}
544
}
545
} else if (fieldValues.containsKey(ERA)) {
546
range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated
547
}
548
return null;
549
}
550
551
ChronoLocalDate resolveYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
552
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
553
if (resolverStyle == ResolverStyle.LENIENT) {
554
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
555
long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
556
return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
557
}
558
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
559
ValueRange domRange = range(DAY_OF_MONTH);
560
int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
561
if (resolverStyle == ResolverStyle.SMART) { // previous valid
562
try {
563
return date(y, moy, dom);
564
} catch (DateTimeException ex) {
565
return date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
566
}
567
}
568
return date(y, moy, dom);
569
}
570
571
ChronoLocalDate resolveYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
572
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
573
if (resolverStyle == ResolverStyle.LENIENT) {
574
long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
575
return dateYearDay(y, 1).plus(days, DAYS);
576
}
577
int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
578
return dateYearDay(y, doy); // smart is same as strict
579
}
580
581
ChronoLocalDate resolveYMAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
582
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
583
if (resolverStyle == ResolverStyle.LENIENT) {
584
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
585
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
586
long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
587
return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS);
588
}
589
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
590
int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
591
int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
592
ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
593
if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
594
throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
595
}
596
return date;
597
}
598
599
ChronoLocalDate resolveYMAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
600
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
601
if (resolverStyle == ResolverStyle.LENIENT) {
602
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
603
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
604
long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
605
return resolveAligned(date(y, 1, 1), months, weeks, dow);
606
}
607
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
608
int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
609
int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
610
ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
611
if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
612
throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
613
}
614
return date;
615
}
616
617
ChronoLocalDate resolveYAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
618
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
619
if (resolverStyle == ResolverStyle.LENIENT) {
620
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
621
long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
622
return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS);
623
}
624
int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
625
int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
626
ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
627
if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
628
throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
629
}
630
return date;
631
}
632
633
ChronoLocalDate resolveYAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
634
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
635
if (resolverStyle == ResolverStyle.LENIENT) {
636
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
637
long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
638
return resolveAligned(dateYearDay(y, 1), 0, weeks, dow);
639
}
640
int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
641
int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
642
ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
643
if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
644
throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
645
}
646
return date;
647
}
648
649
ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) {
650
ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS);
651
if (dow > 7) {
652
date = date.plus((dow - 1) / 7, WEEKS);
653
dow = ((dow - 1) % 7) + 1;
654
} else if (dow < 1) {
655
date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS);
656
dow = ((dow + 6) % 7) + 1;
657
}
658
return date.with(nextOrSame(DayOfWeek.of((int) dow)));
659
}
660
661
/**
662
* Adds a field-value pair to the map, checking for conflicts.
663
* <p>
664
* If the field is not already present, then the field-value pair is added to the map.
665
* If the field is already present and it has the same value as that specified, no action occurs.
666
* If the field is already present and it has a different value to that specified, then
667
* an exception is thrown.
668
*
669
* @param field the field to add, not null
670
* @param value the value to add, not null
671
* @throws java.time.DateTimeException if the field is already present with a different value
672
*/
673
void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
674
Long old = fieldValues.get(field); // check first for better error message
675
if (old != null && old.longValue() != value) {
676
throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
677
}
678
fieldValues.put(field, value);
679
}
680
681
//-----------------------------------------------------------------------
682
/**
683
* Compares this chronology to another chronology.
684
* <p>
685
* The comparison order first by the chronology ID string, then by any
686
* additional information specific to the subclass.
687
* It is "consistent with equals", as defined by {@link Comparable}.
688
*
689
* @implSpec
690
* This implementation compares the chronology ID.
691
* Subclasses must compare any additional state that they store.
692
*
693
* @param other the other chronology to compare to, not null
694
* @return the comparator value, negative if less, positive if greater
695
*/
696
@Override
697
public int compareTo(Chronology other) {
698
return getId().compareTo(other.getId());
699
}
700
701
/**
702
* Checks if this chronology is equal to another chronology.
703
* <p>
704
* The comparison is based on the entire state of the object.
705
*
706
* @implSpec
707
* This implementation checks the type and calls
708
* {@link #compareTo(java.time.chrono.Chronology)}.
709
*
710
* @param obj the object to check, null returns false
711
* @return true if this is equal to the other chronology
712
*/
713
@Override
714
public boolean equals(Object obj) {
715
if (this == obj) {
716
return true;
717
}
718
if (obj instanceof AbstractChronology) {
719
return compareTo((AbstractChronology) obj) == 0;
720
}
721
return false;
722
}
723
724
/**
725
* A hash code for this chronology.
726
* <p>
727
* The hash code should be based on the entire state of the object.
728
*
729
* @implSpec
730
* This implementation is based on the chronology ID and class.
731
* Subclasses should add any additional state that they store.
732
*
733
* @return a suitable hash code
734
*/
735
@Override
736
public int hashCode() {
737
return getClass().hashCode() ^ getId().hashCode();
738
}
739
740
//-----------------------------------------------------------------------
741
/**
742
* Outputs this chronology as a {@code String}, using the chronology ID.
743
*
744
* @return a string representation of this chronology, not null
745
*/
746
@Override
747
public String toString() {
748
return getId();
749
}
750
751
//-----------------------------------------------------------------------
752
/**
753
* Writes the Chronology using a
754
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
755
* <pre>
756
* out.writeByte(1); // identifies this as a Chronology
757
* out.writeUTF(getId());
758
* </pre>
759
*
760
* @return the instance of {@code Ser}, not null
761
*/
762
Object writeReplace() {
763
return new Ser(Ser.CHRONO_TYPE, this);
764
}
765
766
/**
767
* Defend against malicious streams.
768
*
769
* @param s the stream to read
770
* @throws java.io.InvalidObjectException always
771
*/
772
private void readObject(ObjectInputStream s) throws ObjectStreamException {
773
throw new InvalidObjectException("Deserialization via serialization delegate");
774
}
775
776
void writeExternal(DataOutput out) throws IOException {
777
out.writeUTF(getId());
778
}
779
780
static Chronology readExternal(DataInput in) throws IOException {
781
String id = in.readUTF();
782
return Chronology.of(id);
783
}
784
785
}
786
787