1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.joda.time.chrono;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.Locale;
21  import java.util.Map;
22  
23  import org.joda.time.Chronology;
24  import org.joda.time.DateTimeField;
25  import org.joda.time.DateTimeUtils;
26  import org.joda.time.DateTimeZone;
27  import org.joda.time.DurationField;
28  import org.joda.time.IllegalFieldValueException;
29  import org.joda.time.Instant;
30  import org.joda.time.ReadableInstant;
31  import org.joda.time.ReadablePartial;
32  import org.joda.time.field.BaseDateTimeField;
33  import org.joda.time.field.DecoratedDurationField;
34  import org.joda.time.format.DateTimeFormatter;
35  import org.joda.time.format.ISODateTimeFormat;
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  public final class GJChronology extends AssembledChronology {
75  
76      
77      private static final long serialVersionUID = -2545574827706931671L;
78  
79      
80  
81  
82      private static long convertByYear(long instant, Chronology from, Chronology to) {
83          return to.getDateTimeMillis
84              (from.year().get(instant),
85               from.monthOfYear().get(instant),
86               from.dayOfMonth().get(instant),
87               from.millisOfDay().get(instant));
88      }
89  
90      
91  
92  
93      private static long convertByWeekyear(final long instant, Chronology from, Chronology to) {
94          long newInstant;
95          newInstant = to.weekyear().set(0, from.weekyear().get(instant));
96          newInstant = to.weekOfWeekyear().set(newInstant, from.weekOfWeekyear().get(instant));
97          newInstant = to.dayOfWeek().set(newInstant, from.dayOfWeek().get(instant));
98          newInstant = to.millisOfDay().set(newInstant, from.millisOfDay().get(instant));
99          return newInstant;
100     }
101 
102     
103 
104 
105     static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L);
106 
107     
108     private static final Map<DateTimeZone, ArrayList<GJChronology>> cCache = new HashMap<DateTimeZone, ArrayList<GJChronology>>();
109 
110     
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122     public static GJChronology getInstanceUTC() {
123         return getInstance(DateTimeZone.UTC, DEFAULT_CUTOVER, 4);
124     }
125 
126     
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138     public static GJChronology getInstance() {
139         return getInstance(DateTimeZone.getDefault(), DEFAULT_CUTOVER, 4);
140     }
141 
142     
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154     public static GJChronology getInstance(DateTimeZone zone) {
155         return getInstance(zone, DEFAULT_CUTOVER, 4);
156     }
157 
158     
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169     public static GJChronology getInstance(
170             DateTimeZone zone,
171             ReadableInstant gregorianCutover) {
172         
173         return getInstance(zone, gregorianCutover, 4);
174     }
175     
176     
177 
178 
179 
180 
181 
182 
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<GJChronology> chronos = cCache.get(zone);
200         if (chronos == null) {
201             chronos = new ArrayList<GJChronology>(2);
202             cCache.put(zone, chronos);
203         } else {
204             for (int i=chronos.size(); --i>=0; ) {
205                 chrono = 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 
235 
236 
237 
238 
239 
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 
265 
266 
267 
268     private GJChronology(JulianChronology julian,
269                          GregorianChronology gregorian,
270                          Instant cutoverInstant) {
271         super(null, new Object[] {julian, gregorian, cutoverInstant});
272     }
273 
274     
275 
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 
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     
300     
301     
302 
303 
304 
305 
306     public Chronology withUTC() {
307         return withZone(DateTimeZone.UTC);
308     }
309 
310     
311 
312 
313 
314 
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         
336         long instant = iGregorianChronology.getDateTimeMillis
337             (year, monthOfYear, dayOfMonth, millisOfDay);
338         if (instant < iCutoverMillis) {
339             
340             instant = iJulianChronology.getDateTimeMillis
341                 (year, monthOfYear, dayOfMonth, millisOfDay);
342             if (instant >= iCutoverMillis) {
343                 
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         
363         long instant;
364         try {
365             instant = iGregorianChronology.getDateTimeMillis
366                 (year, monthOfYear, dayOfMonth,
367                  hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
368         } catch (IllegalFieldValueException ex) {
369             if (monthOfYear != 2 || dayOfMonth != 29) {
370                 throw ex;
371             }
372             instant = iGregorianChronology.getDateTimeMillis
373                 (year, monthOfYear, 28,
374                  hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
375             if (instant >= iCutoverMillis) {
376                 throw ex;
377             }
378         }
379         if (instant < iCutoverMillis) {
380             
381             instant = iJulianChronology.getDateTimeMillis
382                 (year, monthOfYear, dayOfMonth,
383                  hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
384             if (instant >= iCutoverMillis) {
385                 
386                 throw new IllegalArgumentException("Specified date does not exist");
387             }
388         }
389         return instant;
390     }
391 
392     
393 
394 
395 
396     public Instant getGregorianCutover() {
397         return iCutoverInstant;
398     }
399 
400     
401 
402 
403 
404 
405     public int getMinimumDaysInFirstWeek() {
406         return iGregorianChronology.getMinimumDaysInFirstWeek();
407     }
408 
409     
410 
411 
412 
413 
414 
415 
416     public boolean equals(Object obj) {
417         return super.equals(obj);
418     }
419 
420     
421 
422 
423 
424 
425 
426     public int hashCode() {
427         return "GJ".hashCode() * 11 + iJulianChronology.hashCode() +
428             iGregorianChronology.hashCode() + iCutoverInstant.hashCode();
429     }
430 
431     
432     
433     
434 
435 
436 
437 
438     public String toString() {
439         StringBuffer sb = new StringBuffer(60);
440         sb.append("GJChronology");
441         sb.append('[');
442         sb.append(getZone().getID());
443         
444         if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) {
445             sb.append(",cutover=");
446             DateTimeFormatter printer;
447             if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) {
448                 printer = ISODateTimeFormat.date();
449             } else {
450                 printer = ISODateTimeFormat.dateTime();
451             }
452             printer.withChronology(withUTC()).printTo(sb, iCutoverMillis);
453         }
454         
455         if (getMinimumDaysInFirstWeek() != 4) {
456             sb.append(",mdfw=");
457             sb.append(getMinimumDaysInFirstWeek());
458         }
459         sb.append(']');
460         
461         return sb.toString();
462     }
463 
464     protected void assemble(Fields fields) {
465         Object[] params = (Object[])getParam();
466 
467         JulianChronology julian = (JulianChronology)params[0];
468         GregorianChronology gregorian = (GregorianChronology)params[1];
469         Instant cutoverInstant = (Instant)params[2];
470         iCutoverMillis = cutoverInstant.getMillis();
471 
472         iJulianChronology = julian;
473         iGregorianChronology = gregorian;
474         iCutoverInstant = cutoverInstant;
475 
476         if (getBase() != null) {
477             return;
478         }
479 
480         if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) {
481             throw new IllegalArgumentException();
482         }
483 
484         
485         iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis);
486 
487         
488 
489         
490         
491         fields.copyFieldsFrom(gregorian);
492         
493         
494         
495 
496         
497         if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
498             
499             
500 
501             fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis);
502             fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis);
503             fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis);
504             fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis);
505             fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis);
506             fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis);
507             fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis);
508             fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis);
509             fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis);
510             fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(),
511                                                          fields.clockhourOfHalfday, iCutoverMillis);
512             fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis);
513         }
514 
515         
516         {
517             fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis);
518         }
519 
520         
521         
522         
523         
524 
525         {
526             long cutover = gregorian.year().roundCeiling(iCutoverMillis);
527             fields.dayOfYear = new CutoverField(
528                 julian.dayOfYear(), fields.dayOfYear, cutover);
529         }
530 
531         {
532             long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis);
533             fields.weekOfWeekyear = new CutoverField(
534                 julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true);
535         }
536 
537         
538         
539         
540         {
541             fields.year = new ImpreciseCutoverField(
542                 julian.year(), fields.year, iCutoverMillis);
543             fields.years = fields.year.getDurationField();
544             fields.yearOfEra = new ImpreciseCutoverField(
545                 julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis);
546             fields.yearOfCentury = new ImpreciseCutoverField(
547                 julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis);
548             
549             fields.centuryOfEra = new ImpreciseCutoverField(
550                 julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis);
551             fields.centuries = fields.centuryOfEra.getDurationField();
552             
553             fields.monthOfYear = new ImpreciseCutoverField(
554                 julian.monthOfYear(), fields.monthOfYear, iCutoverMillis);
555             fields.months = fields.monthOfYear.getDurationField();
556             
557             fields.weekyear = new ImpreciseCutoverField(
558                 julian.weekyear(), fields.weekyear, null, iCutoverMillis, true);
559             fields.weekyearOfCentury = new ImpreciseCutoverField(
560                 julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis);
561             fields.weekyears = fields.weekyear.getDurationField();
562         }
563 
564         
565         
566         {
567             CutoverField cf = new CutoverField
568                 (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis);
569             cf.iRangeDurationField = fields.months;
570             fields.dayOfMonth = cf;
571         }
572     }
573 
574     long julianToGregorianByYear(long instant) {
575         return convertByYear(instant, iJulianChronology, iGregorianChronology);
576     }
577 
578     long gregorianToJulianByYear(long instant) {
579         return convertByYear(instant, iGregorianChronology, iJulianChronology);
580     }
581 
582     long julianToGregorianByWeekyear(long instant) {
583         return convertByWeekyear(instant, iJulianChronology, iGregorianChronology);
584     }
585 
586     long gregorianToJulianByWeekyear(long instant) {
587         return convertByWeekyear(instant, iGregorianChronology, iJulianChronology);
588     }
589 
590     
591     
592 
593 
594 
595     private class CutoverField extends BaseDateTimeField {
596         private static final long serialVersionUID = 3528501219481026402L;
597 
598         final DateTimeField iJulianField;
599         final DateTimeField iGregorianField;
600         final long iCutover;
601         final boolean iConvertByWeekyear;
602 
603         protected DurationField iDurationField;
604         protected DurationField iRangeDurationField;
605 
606         
607 
608 
609 
610 
611         CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
612             this(julianField, gregorianField, cutoverMillis, false);
613         }
614 
615         
616 
617 
618 
619 
620 
621         CutoverField(DateTimeField julianField, DateTimeField gregorianField,
622                      long cutoverMillis, boolean convertByWeekyear) {
623             super(gregorianField.getType());
624             iJulianField = julianField;
625             iGregorianField = gregorianField;
626             iCutover = cutoverMillis;
627             iConvertByWeekyear = convertByWeekyear;
628             
629             
630             iDurationField = gregorianField.getDurationField();
631 
632             DurationField rangeField = gregorianField.getRangeDurationField();
633             if (rangeField == null) {
634                 rangeField = julianField.getRangeDurationField();
635             }
636             iRangeDurationField = rangeField;
637         }
638 
639         public boolean isLenient() {
640             return false;
641         }
642 
643         public int get(long instant) {
644             if (instant >= iCutover) {
645                 return iGregorianField.get(instant);
646             } else {
647                 return iJulianField.get(instant);
648             }
649         }
650 
651         public String getAsText(long instant, Locale locale) {
652             if (instant >= iCutover) {
653                 return iGregorianField.getAsText(instant, locale);
654             } else {
655                 return iJulianField.getAsText(instant, locale);
656             }
657         }
658 
659         public String getAsText(int fieldValue, Locale locale) {
660             return iGregorianField.getAsText(fieldValue, locale);
661         }
662 
663         public String getAsShortText(long instant, Locale locale) {
664             if (instant >= iCutover) {
665                 return iGregorianField.getAsShortText(instant, locale);
666             } else {
667                 return iJulianField.getAsShortText(instant, locale);
668             }
669         }
670 
671         public String getAsShortText(int fieldValue, Locale locale) {
672             return iGregorianField.getAsShortText(fieldValue, locale);
673         }
674 
675         public long add(long instant, int value) {
676             return iGregorianField.add(instant, value);
677         }
678 
679         public long add(long instant, long value) {
680             return iGregorianField.add(instant, value);
681         }
682 
683         public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) {
684             
685             
686             if (valueToAdd == 0) {
687                 return values;
688             }
689             if (DateTimeUtils.isContiguous(partial)) {
690                 long instant = 0L;
691                 for (int i = 0, isize = partial.size(); i < isize; i++) {
692                     instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]);
693                 }
694                 instant = add(instant, valueToAdd);
695                 return GJChronology.this.get(partial, instant);
696             } else {
697                 return super.add(partial, fieldIndex, values, valueToAdd);
698             }
699         }
700 
701         public int getDifference(long minuendInstant, long subtrahendInstant) {
702             return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
703         }
704 
705         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
706             return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
707         }
708 
709         public long set(long instant, int value) {
710             if (instant >= iCutover) {
711                 instant = iGregorianField.set(instant, value);
712                 if (instant < iCutover) {
713                     
714                     if (instant + iGapDuration < iCutover) {
715                         instant = gregorianToJulian(instant);
716                     }
717                     
718                     if (get(instant) != value) {
719                         throw new IllegalFieldValueException
720                             (iGregorianField.getType(), Integer.valueOf(value), null, null);
721                     }
722                 }
723             } else {
724                 instant = iJulianField.set(instant, value);
725                 if (instant >= iCutover) {
726                     
727                     if (instant - iGapDuration >= iCutover) {
728                         instant = julianToGregorian(instant);
729                     }
730                     
731                     if (get(instant) != value) {
732                        throw new IllegalFieldValueException
733                             (iJulianField.getType(), Integer.valueOf(value), null, null);
734                     }
735                 }
736             }
737             return instant;
738         }
739 
740         public long set(long instant, String text, Locale locale) {
741             if (instant >= iCutover) {
742                 instant = iGregorianField.set(instant, text, locale);
743                 if (instant < iCutover) {
744                     
745                     if (instant + iGapDuration < iCutover) {
746                         instant = gregorianToJulian(instant);
747                     }
748                     
749                 }
750             } else {
751                 instant = iJulianField.set(instant, text, locale);
752                 if (instant >= iCutover) {
753                     
754                     if (instant - iGapDuration >= iCutover) {
755                         instant = julianToGregorian(instant);
756                     }
757                     
758                 }
759             }
760             return instant;
761         }
762 
763         public DurationField getDurationField() {
764             return iDurationField;
765         }
766 
767         public DurationField getRangeDurationField() {
768             return iRangeDurationField;
769         }
770 
771         public boolean isLeap(long instant) {
772             if (instant >= iCutover) {
773                 return iGregorianField.isLeap(instant);
774             } else {
775                 return iJulianField.isLeap(instant);
776             }
777         }
778 
779         public int getLeapAmount(long instant) {
780             if (instant >= iCutover) {
781                 return iGregorianField.getLeapAmount(instant);
782             } else {
783                 return iJulianField.getLeapAmount(instant);
784             }
785         }
786 
787         public DurationField getLeapDurationField() {
788             return iGregorianField.getLeapDurationField();
789         }
790 
791 
792         public int getMinimumValue() {
793             
794             
795             return iJulianField.getMinimumValue();
796         }
797 
798         public int getMinimumValue(ReadablePartial partial) {
799             return iJulianField.getMinimumValue(partial);
800         }
801 
802         public int getMinimumValue(ReadablePartial partial, int[] values) {
803             return iJulianField.getMinimumValue(partial, values);
804         }
805 
806         public int getMinimumValue(long instant) {
807             if (instant < iCutover) {
808                 return iJulianField.getMinimumValue(instant);
809             }
810 
811             int min = iGregorianField.getMinimumValue(instant);
812 
813             
814             
815             instant = iGregorianField.set(instant, min);
816             if (instant < iCutover) {
817                 min = iGregorianField.get(iCutover);
818             }
819 
820             return min;
821         }
822 
823         public int getMaximumValue() {
824             
825             
826             return iGregorianField.getMaximumValue();
827         }
828 
829         public int getMaximumValue(long instant) {
830             if (instant >= iCutover) {
831                 return iGregorianField.getMaximumValue(instant);
832             }
833 
834             int max = iJulianField.getMaximumValue(instant);
835 
836             
837             
838             instant = iJulianField.set(instant, max);
839             if (instant >= iCutover) {
840                 max = iJulianField.get(iJulianField.add(iCutover, -1));
841             }
842 
843             return max;
844         }
845 
846         public int getMaximumValue(ReadablePartial partial) {
847             long instant = GJChronology.getInstanceUTC().set(partial, 0L);
848             return getMaximumValue(instant);
849         }
850 
851         public int getMaximumValue(ReadablePartial partial, int[] values) {
852             Chronology chrono = GJChronology.getInstanceUTC();
853             long instant = 0L;
854             for (int i = 0, isize = partial.size(); i < isize; i++) {
855                 DateTimeField field = partial.getFieldType(i).getField(chrono);
856                 if (values[i] <= field.getMaximumValue(instant)) {
857                     instant = field.set(instant, values[i]);
858                 }
859             }
860             return getMaximumValue(instant);
861         }
862 
863         public long roundFloor(long instant) {
864             if (instant >= iCutover) {
865                 instant = iGregorianField.roundFloor(instant);
866                 if (instant < iCutover) {
867                     
868                     if (instant + iGapDuration < iCutover) {
869                         instant = gregorianToJulian(instant);
870                     }
871                 }
872             } else {
873                 instant = iJulianField.roundFloor(instant);
874             }
875             return instant;
876         }
877 
878         public long roundCeiling(long instant) {
879             if (instant >= iCutover) {
880                 instant = iGregorianField.roundCeiling(instant);
881             } else {
882                 instant = iJulianField.roundCeiling(instant);
883                 if (instant >= iCutover) {
884                     
885                     if (instant - iGapDuration >= iCutover) {
886                         instant = julianToGregorian(instant);
887                     }
888                 }
889             }
890             return instant;
891         }
892 
893         public int getMaximumTextLength(Locale locale) {
894             return Math.max(iJulianField.getMaximumTextLength(locale),
895                             iGregorianField.getMaximumTextLength(locale));
896         }
897 
898         public int getMaximumShortTextLength(Locale locale) {
899             return Math.max(iJulianField.getMaximumShortTextLength(locale),
900                             iGregorianField.getMaximumShortTextLength(locale));
901         }
902 
903         protected long julianToGregorian(long instant) {
904             if (iConvertByWeekyear) {
905                 return julianToGregorianByWeekyear(instant);
906             } else {
907                 return julianToGregorianByYear(instant);
908             }
909         }
910 
911         protected long gregorianToJulian(long instant) {
912             if (iConvertByWeekyear) {
913                 return gregorianToJulianByWeekyear(instant);
914             } else {
915                 return gregorianToJulianByYear(instant);
916             }
917         }
918     }
919 
920     
921     
922 
923 
924 
925 
926 
927     private final class ImpreciseCutoverField extends CutoverField {
928         private static final long serialVersionUID = 3410248757173576441L;
929 
930         
931 
932 
933         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
934             this(julianField, gregorianField, null, cutoverMillis, false);
935         }
936 
937         
938 
939 
940 
941 
942         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
943                               DurationField durationField, long cutoverMillis)
944         {
945             this(julianField, gregorianField, durationField, cutoverMillis, false);
946         }
947 
948         
949 
950 
951 
952 
953         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
954                               DurationField durationField,
955                               long cutoverMillis, boolean convertByWeekyear)
956         {
957             super(julianField, gregorianField, cutoverMillis, convertByWeekyear);
958             if (durationField == null) {
959                 durationField = new LinkedDurationField(iDurationField, this);
960             }
961             iDurationField = durationField;
962         }
963 
964         public long add(long instant, int value) {
965             if (instant >= iCutover) {
966                 instant = iGregorianField.add(instant, value);
967                 if (instant < iCutover) {
968                     
969                     if (instant + iGapDuration < iCutover) {
970                         instant = gregorianToJulian(instant);
971                     }
972                 }
973             } else {
974                 instant = iJulianField.add(instant, value);
975                 if (instant >= iCutover) {
976                     
977                     if (instant - iGapDuration >= iCutover) {
978                         instant = julianToGregorian(instant);
979                     }
980                 }
981             }
982             return instant;
983         }
984         
985         public long add(long instant, long value) {
986             if (instant >= iCutover) {
987                 instant = iGregorianField.add(instant, value);
988                 if (instant < iCutover) {
989                     
990                     if (instant + iGapDuration < iCutover) {
991                         instant = gregorianToJulian(instant);
992                     }
993                 }
994             } else {
995                 instant = iJulianField.add(instant, value);
996                 if (instant >= iCutover) {
997                     
998                     if (instant - iGapDuration >= iCutover) {
999                         instant = julianToGregorian(instant);
1000                     }
1001                 }
1002             }
1003             return instant;
1004         }
1005 
1006         public int getDifference(long minuendInstant, long subtrahendInstant) {
1007             if (minuendInstant >= iCutover) {
1008                 if (subtrahendInstant >= iCutover) {
1009                     return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
1010                 }
1011                 
1012                 
1013                 minuendInstant = gregorianToJulian(minuendInstant);
1014                 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1015             } else {
1016                 if (subtrahendInstant < iCutover) {
1017                     return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1018                 }
1019                 
1020                 
1021                 minuendInstant = julianToGregorian(minuendInstant);
1022                 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
1023             }
1024         }
1025 
1026         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1027             if (minuendInstant >= iCutover) {
1028                 if (subtrahendInstant >= iCutover) {
1029                     return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1030                 }
1031                 
1032                 
1033                 minuendInstant = gregorianToJulian(minuendInstant);
1034                 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1035             } else {
1036                 if (subtrahendInstant < iCutover) {
1037                     return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1038                 }
1039                 
1040                 
1041                 minuendInstant = julianToGregorian(minuendInstant);
1042                 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1043             }
1044         }
1045 
1046         
1047         
1048         
1049         
1050         
1051         
1052         
1053         
1054         
1055 
1056         public int getMinimumValue(long instant) {
1057             if (instant >= iCutover) {
1058                 return iGregorianField.getMinimumValue(instant);
1059             } else {
1060                 return iJulianField.getMinimumValue(instant);
1061             }
1062         }
1063 
1064         public int getMaximumValue(long instant) {
1065             if (instant >= iCutover) {
1066                 return iGregorianField.getMaximumValue(instant);
1067             } else {
1068                 return iJulianField.getMaximumValue(instant);
1069             }
1070         }
1071     }
1072 
1073     
1074     
1075 
1076 
1077     private static class LinkedDurationField extends DecoratedDurationField {
1078         private static final long serialVersionUID = 4097975388007713084L;
1079 
1080         private final ImpreciseCutoverField iField;
1081 
1082         LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) {
1083             super(durationField, durationField.getType());
1084             iField = dateTimeField;
1085         }
1086 
1087         public long add(long instant, int value) {
1088             return iField.add(instant, value);
1089         }
1090 
1091         public long add(long instant, long value) {
1092             return iField.add(instant, value);
1093         }
1094 
1095         public int getDifference(long minuendInstant, long subtrahendInstant) {
1096             return iField.getDifference(minuendInstant, subtrahendInstant);
1097         }
1098 
1099         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1100             return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1101         }
1102     }
1103 
1104 }