001 /*
002 * Copyright 2001-2005 Stephen Colebourne
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.joda.time.chrono;
017
018 import java.util.ArrayList;
019 import java.util.HashMap;
020 import java.util.Locale;
021 import java.util.Map;
022
023 import org.joda.time.Chronology;
024 import org.joda.time.DateTimeField;
025 import org.joda.time.DateTimeUtils;
026 import org.joda.time.DateTimeZone;
027 import org.joda.time.DurationField;
028 import org.joda.time.IllegalFieldValueException;
029 import org.joda.time.Instant;
030 import org.joda.time.ReadableInstant;
031 import org.joda.time.ReadablePartial;
032 import org.joda.time.field.BaseDateTimeField;
033 import org.joda.time.field.DecoratedDurationField;
034 import org.joda.time.format.DateTimeFormatter;
035 import org.joda.time.format.ISODateTimeFormat;
036
037 /**
038 * Implements the Gregorian/Julian calendar system which is the calendar system
039 * used in most of the world. Wherever possible, it is recommended to use the
040 * {@link ISOChronology} instead.
041 * <p>
042 * The Gregorian calendar replaced the Julian calendar, and the point in time
043 * when this chronology switches can be controlled using the second parameter
044 * of the getInstance method. By default this cutover is set to the date the
045 * Gregorian calendar was first instituted, October 15, 1582.
046 * <p>
047 * Before this date, this chronology uses the proleptic Julian calendar
048 * (proleptic means extending indefinitely). The Julian calendar has leap years
049 * every four years, whereas the Gregorian has special rules for 100 and 400
050 * years. A meaningful result will thus be obtained for all input values.
051 * However before 8 CE, Julian leap years were irregular, and before 45 BCE
052 * there was no Julian calendar.
053 * <p>
054 * This chronology differs from
055 * {@link java.util.GregorianCalendar GregorianCalendar} in that years
056 * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1.
057 * The yearOfEra field produces results compatible with GregorianCalendar.
058 * <p>
059 * The Julian calendar does not have a year zero, and so year -1 is followed by
060 * year 1. If the Gregorian cutover date is specified at or before year -1
061 * (Julian), year zero is defined. In other words, the proleptic Gregorian
062 * chronology used by this class has a year zero.
063 * <p>
064 * To create a pure proleptic Julian chronology, use {@link JulianChronology},
065 * and to create a pure proleptic Gregorian chronology, use
066 * {@link GregorianChronology}.
067 * <p>
068 * GJChronology is thread-safe and immutable.
069 *
070 * @author Brian S O'Neill
071 * @author Stephen Colebourne
072 * @since 1.0
073 */
074 public final class GJChronology extends AssembledChronology {
075
076 /** Serialization lock */
077 private static final long serialVersionUID = -2545574827706931671L;
078
079 /**
080 * Convert a datetime from one chronology to another.
081 */
082 private static long convertByYear(long instant, Chronology from, Chronology to) {
083 return to.getDateTimeMillis
084 (from.year().get(instant),
085 from.monthOfYear().get(instant),
086 from.dayOfMonth().get(instant),
087 from.millisOfDay().get(instant));
088 }
089
090 /**
091 * Convert a datetime from one chronology to another.
092 */
093 private static long convertByWeekyear(final long instant, Chronology from, Chronology to) {
094 long newInstant;
095 newInstant = to.weekyear().set(0, from.weekyear().get(instant));
096 newInstant = to.weekOfWeekyear().set(newInstant, from.weekOfWeekyear().get(instant));
097 newInstant = to.dayOfWeek().set(newInstant, from.dayOfWeek().get(instant));
098 newInstant = to.millisOfDay().set(newInstant, from.millisOfDay().get(instant));
099 return newInstant;
100 }
101
102 /**
103 * The default GregorianJulian cutover point.
104 */
105 static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L);
106
107 /** Cache of zone to chronology list */
108 private static final Map cCache = new HashMap();
109
110 /**
111 * Factory method returns instances of the default GJ cutover
112 * chronology. This uses a cutover date of October 15, 1582 (Gregorian)
113 * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by
114 * October 15, 1582 (Gregorian).
115 *
116 * <p>The first day of the week is designated to be
117 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
118 * and the minimum days in the first week of the year is 4.
119 *
120 * <p>The time zone of the returned instance is UTC.
121 */
122 public static GJChronology getInstanceUTC() {
123 return getInstance(DateTimeZone.UTC, DEFAULT_CUTOVER, 4);
124 }
125
126 /**
127 * Factory method returns instances of the default GJ cutover
128 * chronology. This uses a cutover date of October 15, 1582 (Gregorian)
129 * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by
130 * October 15, 1582 (Gregorian).
131 *
132 * <p>The first day of the week is designated to be
133 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
134 * and the minimum days in the first week of the year is 4.
135 *
136 * <p>The returned chronology is in the default time zone.
137 */
138 public static GJChronology getInstance() {
139 return getInstance(DateTimeZone.getDefault(), DEFAULT_CUTOVER, 4);
140 }
141
142 /**
143 * Factory method returns instances of the GJ cutover chronology. This uses
144 * a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this
145 * value, October 4, 1582 (Julian) is followed by October 15, 1582
146 * (Gregorian).
147 *
148 * <p>The first day of the week is designated to be
149 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
150 * and the minimum days in the first week of the year is 4.
151 *
152 * @param zone the time zone to use, null is default
153 */
154 public static GJChronology getInstance(DateTimeZone zone) {
155 return getInstance(zone, DEFAULT_CUTOVER, 4);
156 }
157
158 /**
159 * Factory method returns instances of the GJ cutover chronology. Any
160 * cutover date may be specified.
161 *
162 * <p>The first day of the week is designated to be
163 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
164 * and the minimum days in the first week of the year is 4.
165 *
166 * @param zone the time zone to use, null is default
167 * @param gregorianCutover the cutover to use, null means default
168 */
169 public static GJChronology getInstance(
170 DateTimeZone zone,
171 ReadableInstant gregorianCutover) {
172
173 return getInstance(zone, gregorianCutover, 4);
174 }
175
176 /**
177 * Factory method returns instances of the GJ cutover chronology. Any
178 * cutover date may be specified.
179 *
180 * @param zone the time zone to use, null is default
181 * @param gregorianCutover the cutover to use, null means default
182 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4
183 */
184 public static synchronized GJChronology getInstance(
185 DateTimeZone zone,
186 ReadableInstant gregorianCutover,
187 int minDaysInFirstWeek) {
188
189 zone = DateTimeUtils.getZone(zone);
190 Instant cutoverInstant;
191 if (gregorianCutover == null) {
192 cutoverInstant = DEFAULT_CUTOVER;
193 } else {
194 cutoverInstant = gregorianCutover.toInstant();
195 }
196
197 GJChronology chrono;
198
199 ArrayList chronos = (ArrayList)cCache.get(zone);
200 if (chronos == null) {
201 chronos = new ArrayList(2);
202 cCache.put(zone, chronos);
203 } else {
204 for (int i=chronos.size(); --i>=0; ) {
205 chrono = (GJChronology)chronos.get(i);
206 if (minDaysInFirstWeek == chrono.getMinimumDaysInFirstWeek() &&
207 cutoverInstant.equals(chrono.getGregorianCutover())) {
208
209 return chrono;
210 }
211 }
212 }
213
214 if (zone == DateTimeZone.UTC) {
215 chrono = new GJChronology
216 (JulianChronology.getInstance(zone, minDaysInFirstWeek),
217 GregorianChronology.getInstance(zone, minDaysInFirstWeek),
218 cutoverInstant);
219 } else {
220 chrono = getInstance(DateTimeZone.UTC, cutoverInstant, minDaysInFirstWeek);
221 chrono = new GJChronology
222 (ZonedChronology.getInstance(chrono, zone),
223 chrono.iJulianChronology,
224 chrono.iGregorianChronology,
225 chrono.iCutoverInstant);
226 }
227
228 chronos.add(chrono);
229
230 return chrono;
231 }
232
233 /**
234 * Factory method returns instances of the GJ cutover chronology. Any
235 * cutover date may be specified.
236 *
237 * @param zone the time zone to use, null is default
238 * @param gregorianCutover the cutover to use
239 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4
240 */
241 public static GJChronology getInstance(
242 DateTimeZone zone,
243 long gregorianCutover,
244 int minDaysInFirstWeek) {
245
246 Instant cutoverInstant;
247 if (gregorianCutover == DEFAULT_CUTOVER.getMillis()) {
248 cutoverInstant = null;
249 } else {
250 cutoverInstant = new Instant(gregorianCutover);
251 }
252 return getInstance(zone, cutoverInstant, minDaysInFirstWeek);
253 }
254
255 //-----------------------------------------------------------------------
256 private JulianChronology iJulianChronology;
257 private GregorianChronology iGregorianChronology;
258 private Instant iCutoverInstant;
259
260 private long iCutoverMillis;
261 private long iGapDuration;
262
263 /**
264 * @param julian chronology used before the cutover instant
265 * @param gregorian chronology used at and after the cutover instant
266 * @param cutoverInstant instant when the gregorian chronology began
267 */
268 private GJChronology(JulianChronology julian,
269 GregorianChronology gregorian,
270 Instant cutoverInstant) {
271 super(null, new Object[] {julian, gregorian, cutoverInstant});
272 }
273
274 /**
275 * Called when applying a time zone.
276 */
277 private GJChronology(Chronology base,
278 JulianChronology julian,
279 GregorianChronology gregorian,
280 Instant cutoverInstant) {
281 super(base, new Object[] {julian, gregorian, cutoverInstant});
282 }
283
284 /**
285 * Serialization singleton
286 */
287 private Object readResolve() {
288 return getInstance(getZone(), iCutoverInstant, getMinimumDaysInFirstWeek());
289 }
290
291 public DateTimeZone getZone() {
292 Chronology base;
293 if ((base = getBase()) != null) {
294 return base.getZone();
295 }
296 return DateTimeZone.UTC;
297 }
298
299 // Conversion
300 //-----------------------------------------------------------------------
301 /**
302 * Gets the Chronology in the UTC time zone.
303 *
304 * @return the chronology in UTC
305 */
306 public Chronology withUTC() {
307 return withZone(DateTimeZone.UTC);
308 }
309
310 /**
311 * Gets the Chronology in a specific time zone.
312 *
313 * @param zone the zone to get the chronology in, null is default
314 * @return the chronology
315 */
316 public Chronology withZone(DateTimeZone zone) {
317 if (zone == null) {
318 zone = DateTimeZone.getDefault();
319 }
320 if (zone == getZone()) {
321 return this;
322 }
323 return getInstance(zone, iCutoverInstant, getMinimumDaysInFirstWeek());
324 }
325
326 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
327 int millisOfDay)
328 throws IllegalArgumentException
329 {
330 Chronology base;
331 if ((base = getBase()) != null) {
332 return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay);
333 }
334
335 // Assume date is Gregorian.
336 long instant = iGregorianChronology.getDateTimeMillis
337 (year, monthOfYear, dayOfMonth, millisOfDay);
338 if (instant < iCutoverMillis) {
339 // Maybe it's Julian.
340 instant = iJulianChronology.getDateTimeMillis
341 (year, monthOfYear, dayOfMonth, millisOfDay);
342 if (instant >= iCutoverMillis) {
343 // Okay, it's in the illegal cutover gap.
344 throw new IllegalArgumentException("Specified date does not exist");
345 }
346 }
347 return instant;
348 }
349
350 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
351 int hourOfDay, int minuteOfHour,
352 int secondOfMinute, int millisOfSecond)
353 throws IllegalArgumentException
354 {
355 Chronology base;
356 if ((base = getBase()) != null) {
357 return base.getDateTimeMillis
358 (year, monthOfYear, dayOfMonth,
359 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
360 }
361
362 // Assume date is Gregorian.
363 long instant = iGregorianChronology.getDateTimeMillis
364 (year, monthOfYear, dayOfMonth,
365 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
366 if (instant < iCutoverMillis) {
367 // Maybe it's Julian.
368 instant = iJulianChronology.getDateTimeMillis
369 (year, monthOfYear, dayOfMonth,
370 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
371 if (instant >= iCutoverMillis) {
372 // Okay, it's in the illegal cutover gap.
373 throw new IllegalArgumentException("Specified date does not exist");
374 }
375 }
376 return instant;
377 }
378
379 /**
380 * Gets the cutover instant between Gregorian and Julian chronologies.
381 * @return the cutover instant
382 */
383 public Instant getGregorianCutover() {
384 return iCutoverInstant;
385 }
386
387 /**
388 * Gets the minimum days needed for a week to be the first week in a year.
389 *
390 * @return the minimum days
391 */
392 public int getMinimumDaysInFirstWeek() {
393 return iGregorianChronology.getMinimumDaysInFirstWeek();
394 }
395
396 /**
397 * Checks if this chronology instance equals another.
398 *
399 * @param obj the object to compare to
400 * @return true if equal
401 * @since 1.6
402 */
403 public boolean equals(Object obj) {
404 return super.equals(obj);
405 }
406
407 /**
408 * A suitable hash code for the chronology.
409 *
410 * @return the hash code
411 * @since 1.6
412 */
413 public int hashCode() {
414 return "GJ".hashCode() * 11 + iJulianChronology.hashCode() +
415 iGregorianChronology.hashCode() + iCutoverInstant.hashCode();
416 }
417
418 // Output
419 //-----------------------------------------------------------------------
420 /**
421 * Gets a debugging toString.
422 *
423 * @return a debugging string
424 */
425 public String toString() {
426 StringBuffer sb = new StringBuffer(60);
427 sb.append("GJChronology");
428 sb.append('[');
429 sb.append(getZone().getID());
430
431 if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) {
432 sb.append(",cutover=");
433 DateTimeFormatter printer;
434 if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) {
435 printer = ISODateTimeFormat.date();
436 } else {
437 printer = ISODateTimeFormat.dateTime();
438 }
439 printer.withChronology(withUTC()).printTo(sb, iCutoverMillis);
440 }
441
442 if (getMinimumDaysInFirstWeek() != 4) {
443 sb.append(",mdfw=");
444 sb.append(getMinimumDaysInFirstWeek());
445 }
446 sb.append(']');
447
448 return sb.toString();
449 }
450
451 protected void assemble(Fields fields) {
452 Object[] params = (Object[])getParam();
453
454 JulianChronology julian = (JulianChronology)params[0];
455 GregorianChronology gregorian = (GregorianChronology)params[1];
456 Instant cutoverInstant = (Instant)params[2];
457 iCutoverMillis = cutoverInstant.getMillis();
458
459 iJulianChronology = julian;
460 iGregorianChronology = gregorian;
461 iCutoverInstant = cutoverInstant;
462
463 if (getBase() != null) {
464 return;
465 }
466
467 if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) {
468 throw new IllegalArgumentException();
469 }
470
471 // Compute difference between the chronologies at the cutover instant
472 iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis);
473
474 // Begin field definitions.
475
476 // First just copy all the Gregorian fields and then override those
477 // that need special attention.
478 fields.copyFieldsFrom(gregorian);
479
480 // Assuming cutover is at midnight, all time of day fields can be
481 // gregorian since they are unaffected by cutover.
482
483 // Verify assumption.
484 if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
485 // Cutover is sometime in the day, so cutover fields are required
486 // for time of day.
487
488 fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis);
489 fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis);
490 fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis);
491 fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis);
492 fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis);
493 fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis);
494 fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis);
495 fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis);
496 fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis);
497 fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(),
498 fields.clockhourOfHalfday, iCutoverMillis);
499 fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis);
500 }
501
502 // These fields just require basic cutover support.
503 {
504 fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis);
505 }
506
507 // DayOfYear and weekOfWeekyear require special handling since cutover
508 // year has fewer days and weeks. Extend the cutover to the start of
509 // the next year or weekyear. This keeps the sequence unbroken during
510 // the cutover year.
511
512 {
513 long cutover = gregorian.year().roundCeiling(iCutoverMillis);
514 fields.dayOfYear = new CutoverField(
515 julian.dayOfYear(), fields.dayOfYear, cutover);
516 }
517
518 {
519 long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis);
520 fields.weekOfWeekyear = new CutoverField(
521 julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true);
522 }
523
524 // These fields are special because they have imprecise durations. The
525 // family of addition methods need special attention. Override affected
526 // duration fields as well.
527 {
528 fields.year = new ImpreciseCutoverField(
529 julian.year(), fields.year, iCutoverMillis);
530 fields.years = fields.year.getDurationField();
531 fields.yearOfEra = new ImpreciseCutoverField(
532 julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis);
533 fields.yearOfCentury = new ImpreciseCutoverField(
534 julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis);
535
536 fields.centuryOfEra = new ImpreciseCutoverField(
537 julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis);
538 fields.centuries = fields.centuryOfEra.getDurationField();
539
540 fields.monthOfYear = new ImpreciseCutoverField(
541 julian.monthOfYear(), fields.monthOfYear, iCutoverMillis);
542 fields.months = fields.monthOfYear.getDurationField();
543
544 fields.weekyear = new ImpreciseCutoverField(
545 julian.weekyear(), fields.weekyear, null, iCutoverMillis, true);
546 fields.weekyearOfCentury = new ImpreciseCutoverField(
547 julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis);
548 fields.weekyears = fields.weekyear.getDurationField();
549 }
550
551 // These fields require basic cutover support, except they must link to
552 // imprecise durations.
553 {
554 CutoverField cf = new CutoverField
555 (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis);
556 cf.iRangeDurationField = fields.months;
557 fields.dayOfMonth = cf;
558 }
559 }
560
561 long julianToGregorianByYear(long instant) {
562 return convertByYear(instant, iJulianChronology, iGregorianChronology);
563 }
564
565 long gregorianToJulianByYear(long instant) {
566 return convertByYear(instant, iGregorianChronology, iJulianChronology);
567 }
568
569 long julianToGregorianByWeekyear(long instant) {
570 return convertByWeekyear(instant, iJulianChronology, iGregorianChronology);
571 }
572
573 long gregorianToJulianByWeekyear(long instant) {
574 return convertByWeekyear(instant, iGregorianChronology, iJulianChronology);
575 }
576
577 //-----------------------------------------------------------------------
578 /**
579 * This basic cutover field adjusts calls to 'get' and 'set' methods, and
580 * assumes that calls to add and addWrapField are unaffected by the cutover.
581 */
582 private class CutoverField extends BaseDateTimeField {
583 private static final long serialVersionUID = 3528501219481026402L;
584
585 final DateTimeField iJulianField;
586 final DateTimeField iGregorianField;
587 final long iCutover;
588 final boolean iConvertByWeekyear;
589
590 protected DurationField iDurationField;
591 protected DurationField iRangeDurationField;
592
593 /**
594 * @param julianField field from the chronology used before the cutover instant
595 * @param gregorianField field from the chronology used at and after the cutover
596 * @param cutoverMillis the millis of the cutover
597 */
598 CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
599 this(julianField, gregorianField, cutoverMillis, false);
600 }
601
602 /**
603 * @param julianField field from the chronology used before the cutover instant
604 * @param gregorianField field from the chronology used at and after the cutover
605 * @param cutoverMillis the millis of the cutover
606 * @param convertByWeekyear
607 */
608 CutoverField(DateTimeField julianField, DateTimeField gregorianField,
609 long cutoverMillis, boolean convertByWeekyear) {
610 super(gregorianField.getType());
611 iJulianField = julianField;
612 iGregorianField = gregorianField;
613 iCutover = cutoverMillis;
614 iConvertByWeekyear = convertByWeekyear;
615 // Although average length of Julian and Gregorian years differ,
616 // use the Gregorian duration field because it is more accurate.
617 iDurationField = gregorianField.getDurationField();
618
619 DurationField rangeField = gregorianField.getRangeDurationField();
620 if (rangeField == null) {
621 rangeField = julianField.getRangeDurationField();
622 }
623 iRangeDurationField = rangeField;
624 }
625
626 public boolean isLenient() {
627 return false;
628 }
629
630 public int get(long instant) {
631 if (instant >= iCutover) {
632 return iGregorianField.get(instant);
633 } else {
634 return iJulianField.get(instant);
635 }
636 }
637
638 public String getAsText(long instant, Locale locale) {
639 if (instant >= iCutover) {
640 return iGregorianField.getAsText(instant, locale);
641 } else {
642 return iJulianField.getAsText(instant, locale);
643 }
644 }
645
646 public String getAsText(int fieldValue, Locale locale) {
647 return iGregorianField.getAsText(fieldValue, locale);
648 }
649
650 public String getAsShortText(long instant, Locale locale) {
651 if (instant >= iCutover) {
652 return iGregorianField.getAsShortText(instant, locale);
653 } else {
654 return iJulianField.getAsShortText(instant, locale);
655 }
656 }
657
658 public String getAsShortText(int fieldValue, Locale locale) {
659 return iGregorianField.getAsShortText(fieldValue, locale);
660 }
661
662 public long add(long instant, int value) {
663 return iGregorianField.add(instant, value);
664 }
665
666 public long add(long instant, long value) {
667 return iGregorianField.add(instant, value);
668 }
669
670 public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) {
671 // overridden as superclass algorithm can't handle
672 // 2004-02-29 + 48 months -> 2008-02-29 type dates
673 if (valueToAdd == 0) {
674 return values;
675 }
676 if (DateTimeUtils.isContiguous(partial)) {
677 long instant = 0L;
678 for (int i = 0, isize = partial.size(); i < isize; i++) {
679 instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]);
680 }
681 instant = add(instant, valueToAdd);
682 return GJChronology.this.get(partial, instant);
683 } else {
684 return super.add(partial, fieldIndex, values, valueToAdd);
685 }
686 }
687
688 public int getDifference(long minuendInstant, long subtrahendInstant) {
689 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
690 }
691
692 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
693 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
694 }
695
696 public long set(long instant, int value) {
697 if (instant >= iCutover) {
698 instant = iGregorianField.set(instant, value);
699 if (instant < iCutover) {
700 // Only adjust if gap fully crossed.
701 if (instant + iGapDuration < iCutover) {
702 instant = gregorianToJulian(instant);
703 }
704 // Verify that new value stuck.
705 if (get(instant) != value) {
706 throw new IllegalFieldValueException
707 (iGregorianField.getType(), new Integer(value), null, null);
708 }
709 }
710 } else {
711 instant = iJulianField.set(instant, value);
712 if (instant >= iCutover) {
713 // Only adjust if gap fully crossed.
714 if (instant - iGapDuration >= iCutover) {
715 instant = julianToGregorian(instant);
716 }
717 // Verify that new value stuck.
718 if (get(instant) != value) {
719 throw new IllegalFieldValueException
720 (iJulianField.getType(), new Integer(value), null, null);
721 }
722 }
723 }
724 return instant;
725 }
726
727 public long set(long instant, String text, Locale locale) {
728 if (instant >= iCutover) {
729 instant = iGregorianField.set(instant, text, locale);
730 if (instant < iCutover) {
731 // Only adjust if gap fully crossed.
732 if (instant + iGapDuration < iCutover) {
733 instant = gregorianToJulian(instant);
734 }
735 // Cannot verify that new value stuck because set may be lenient.
736 }
737 } else {
738 instant = iJulianField.set(instant, text, locale);
739 if (instant >= iCutover) {
740 // Only adjust if gap fully crossed.
741 if (instant - iGapDuration >= iCutover) {
742 instant = julianToGregorian(instant);
743 }
744 // Cannot verify that new value stuck because set may be lenient.
745 }
746 }
747 return instant;
748 }
749
750 public DurationField getDurationField() {
751 return iDurationField;
752 }
753
754 public DurationField getRangeDurationField() {
755 return iRangeDurationField;
756 }
757
758 public boolean isLeap(long instant) {
759 if (instant >= iCutover) {
760 return iGregorianField.isLeap(instant);
761 } else {
762 return iJulianField.isLeap(instant);
763 }
764 }
765
766 public int getLeapAmount(long instant) {
767 if (instant >= iCutover) {
768 return iGregorianField.getLeapAmount(instant);
769 } else {
770 return iJulianField.getLeapAmount(instant);
771 }
772 }
773
774 public DurationField getLeapDurationField() {
775 return iGregorianField.getLeapDurationField();
776 }
777
778
779 public int getMinimumValue() {
780 // For all precise fields, the Julian and Gregorian limits are
781 // identical. Choose Julian to tighten up the year limits.
782 return iJulianField.getMinimumValue();
783 }
784
785 public int getMinimumValue(ReadablePartial partial) {
786 return iJulianField.getMinimumValue(partial);
787 }
788
789 public int getMinimumValue(ReadablePartial partial, int[] values) {
790 return iJulianField.getMinimumValue(partial, values);
791 }
792
793 public int getMinimumValue(long instant) {
794 if (instant < iCutover) {
795 return iJulianField.getMinimumValue(instant);
796 }
797
798 int min = iGregorianField.getMinimumValue(instant);
799
800 // Because the cutover may reduce the length of this field, verify
801 // the minimum by setting it.
802 instant = iGregorianField.set(instant, min);
803 if (instant < iCutover) {
804 min = iGregorianField.get(iCutover);
805 }
806
807 return min;
808 }
809
810 public int getMaximumValue() {
811 // For all precise fields, the Julian and Gregorian limits are
812 // identical.
813 return iGregorianField.getMaximumValue();
814 }
815
816 public int getMaximumValue(long instant) {
817 if (instant >= iCutover) {
818 return iGregorianField.getMaximumValue(instant);
819 }
820
821 int max = iJulianField.getMaximumValue(instant);
822
823 // Because the cutover may reduce the length of this field, verify
824 // the maximum by setting it.
825 instant = iJulianField.set(instant, max);
826 if (instant >= iCutover) {
827 max = iJulianField.get(iJulianField.add(iCutover, -1));
828 }
829
830 return max;
831 }
832
833 public int getMaximumValue(ReadablePartial partial) {
834 long instant = GJChronology.getInstanceUTC().set(partial, 0L);
835 return getMaximumValue(instant);
836 }
837
838 public int getMaximumValue(ReadablePartial partial, int[] values) {
839 Chronology chrono = GJChronology.getInstanceUTC();
840 long instant = 0L;
841 for (int i = 0, isize = partial.size(); i < isize; i++) {
842 DateTimeField field = partial.getFieldType(i).getField(chrono);
843 if (values[i] <= field.getMaximumValue(instant)) {
844 instant = field.set(instant, values[i]);
845 }
846 }
847 return getMaximumValue(instant);
848 }
849
850 public long roundFloor(long instant) {
851 if (instant >= iCutover) {
852 instant = iGregorianField.roundFloor(instant);
853 if (instant < iCutover) {
854 // Only adjust if gap fully crossed.
855 if (instant + iGapDuration < iCutover) {
856 instant = gregorianToJulian(instant);
857 }
858 }
859 } else {
860 instant = iJulianField.roundFloor(instant);
861 }
862 return instant;
863 }
864
865 public long roundCeiling(long instant) {
866 if (instant >= iCutover) {
867 instant = iGregorianField.roundCeiling(instant);
868 } else {
869 instant = iJulianField.roundCeiling(instant);
870 if (instant >= iCutover) {
871 // Only adjust if gap fully crossed.
872 if (instant - iGapDuration >= iCutover) {
873 instant = julianToGregorian(instant);
874 }
875 }
876 }
877 return instant;
878 }
879
880 public int getMaximumTextLength(Locale locale) {
881 return Math.max(iJulianField.getMaximumTextLength(locale),
882 iGregorianField.getMaximumTextLength(locale));
883 }
884
885 public int getMaximumShortTextLength(Locale locale) {
886 return Math.max(iJulianField.getMaximumShortTextLength(locale),
887 iGregorianField.getMaximumShortTextLength(locale));
888 }
889
890 protected long julianToGregorian(long instant) {
891 if (iConvertByWeekyear) {
892 return julianToGregorianByWeekyear(instant);
893 } else {
894 return julianToGregorianByYear(instant);
895 }
896 }
897
898 protected long gregorianToJulian(long instant) {
899 if (iConvertByWeekyear) {
900 return gregorianToJulianByWeekyear(instant);
901 } else {
902 return gregorianToJulianByYear(instant);
903 }
904 }
905 }
906
907 //-----------------------------------------------------------------------
908 /**
909 * Cutover field for variable length fields. These fields internally call
910 * set whenever add is called. As a result, the same correction applied to
911 * set must be applied to add and addWrapField. Knowing when to use this
912 * field requires specific knowledge of how the GJ fields are implemented.
913 */
914 private final class ImpreciseCutoverField extends CutoverField {
915 private static final long serialVersionUID = 3410248757173576441L;
916
917 /**
918 * Creates a duration field that links back to this.
919 */
920 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
921 this(julianField, gregorianField, null, cutoverMillis, false);
922 }
923
924 /**
925 * Uses a shared duration field rather than creating a new one.
926 *
927 * @param durationField shared duration field
928 */
929 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
930 DurationField durationField, long cutoverMillis)
931 {
932 this(julianField, gregorianField, durationField, cutoverMillis, false);
933 }
934
935 /**
936 * Uses a shared duration field rather than creating a new one.
937 *
938 * @param durationField shared duration field
939 */
940 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
941 DurationField durationField,
942 long cutoverMillis, boolean convertByWeekyear)
943 {
944 super(julianField, gregorianField, cutoverMillis, convertByWeekyear);
945 if (durationField == null) {
946 durationField = new LinkedDurationField(iDurationField, this);
947 }
948 iDurationField = durationField;
949 }
950
951 public long add(long instant, int value) {
952 if (instant >= iCutover) {
953 instant = iGregorianField.add(instant, value);
954 if (instant < iCutover) {
955 // Only adjust if gap fully crossed.
956 if (instant + iGapDuration < iCutover) {
957 instant = gregorianToJulian(instant);
958 }
959 }
960 } else {
961 instant = iJulianField.add(instant, value);
962 if (instant >= iCutover) {
963 // Only adjust if gap fully crossed.
964 if (instant - iGapDuration >= iCutover) {
965 instant = julianToGregorian(instant);
966 }
967 }
968 }
969 return instant;
970 }
971
972 public long add(long instant, long value) {
973 if (instant >= iCutover) {
974 instant = iGregorianField.add(instant, value);
975 if (instant < iCutover) {
976 // Only adjust if gap fully crossed.
977 if (instant + iGapDuration < iCutover) {
978 instant = gregorianToJulian(instant);
979 }
980 }
981 } else {
982 instant = iJulianField.add(instant, value);
983 if (instant >= iCutover) {
984 // Only adjust if gap fully crossed.
985 if (instant - iGapDuration >= iCutover) {
986 instant = julianToGregorian(instant);
987 }
988 }
989 }
990 return instant;
991 }
992
993 public int getDifference(long minuendInstant, long subtrahendInstant) {
994 if (minuendInstant >= iCutover) {
995 if (subtrahendInstant >= iCutover) {
996 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
997 }
998 // Remember, the add is being reversed. Since subtrahend is
999 // Julian, convert minuend to Julian to match.
1000 minuendInstant = gregorianToJulian(minuendInstant);
1001 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1002 } else {
1003 if (subtrahendInstant < iCutover) {
1004 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1005 }
1006 // Remember, the add is being reversed. Since subtrahend is
1007 // Gregorian, convert minuend to Gregorian to match.
1008 minuendInstant = julianToGregorian(minuendInstant);
1009 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
1010 }
1011 }
1012
1013 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1014 if (minuendInstant >= iCutover) {
1015 if (subtrahendInstant >= iCutover) {
1016 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1017 }
1018 // Remember, the add is being reversed. Since subtrahend is
1019 // Julian, convert minuend to Julian to match.
1020 minuendInstant = gregorianToJulian(minuendInstant);
1021 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1022 } else {
1023 if (subtrahendInstant < iCutover) {
1024 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1025 }
1026 // Remember, the add is being reversed. Since subtrahend is
1027 // Gregorian, convert minuend to Gregorian to match.
1028 minuendInstant = julianToGregorian(minuendInstant);
1029 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1030 }
1031 }
1032
1033 // Since the imprecise fields have durations longer than the gap
1034 // duration, keep these methods simple. The inherited implementations
1035 // produce incorrect results.
1036 //
1037 // Degenerate case: If this field is a month, and the cutover is set
1038 // far into the future, then the gap duration may be so large as to
1039 // reduce the number of months in a year. If the missing month(s) are
1040 // at the beginning or end of the year, then the minimum and maximum
1041 // values are not 1 and 12. I don't expect this case to ever occur.
1042
1043 public int getMinimumValue(long instant) {
1044 if (instant >= iCutover) {
1045 return iGregorianField.getMinimumValue(instant);
1046 } else {
1047 return iJulianField.getMinimumValue(instant);
1048 }
1049 }
1050
1051 public int getMaximumValue(long instant) {
1052 if (instant >= iCutover) {
1053 return iGregorianField.getMaximumValue(instant);
1054 } else {
1055 return iJulianField.getMaximumValue(instant);
1056 }
1057 }
1058 }
1059
1060 //-----------------------------------------------------------------------
1061 /**
1062 * Links the duration back to a ImpreciseCutoverField.
1063 */
1064 private static class LinkedDurationField extends DecoratedDurationField {
1065 private static final long serialVersionUID = 4097975388007713084L;
1066
1067 private final ImpreciseCutoverField iField;
1068
1069 LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) {
1070 super(durationField, durationField.getType());
1071 iField = dateTimeField;
1072 }
1073
1074 public long add(long instant, int value) {
1075 return iField.add(instant, value);
1076 }
1077
1078 public long add(long instant, long value) {
1079 return iField.add(instant, value);
1080 }
1081
1082 public int getDifference(long minuendInstant, long subtrahendInstant) {
1083 return iField.getDifference(minuendInstant, subtrahendInstant);
1084 }
1085
1086 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1087 return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1088 }
1089 }
1090
1091 }