View Javadoc

1   /*
2    *  Copyright 2001-2009 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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   * Implements the Gregorian/Julian calendar system which is the calendar system
39   * used in most of the world. Wherever possible, it is recommended to use the
40   * {@link ISOChronology} instead.
41   * <p>
42   * The Gregorian calendar replaced the Julian calendar, and the point in time
43   * when this chronology switches can be controlled using the second parameter
44   * of the getInstance method. By default this cutover is set to the date the
45   * Gregorian calendar was first instituted, October 15, 1582.
46   * <p>
47   * Before this date, this chronology uses the proleptic Julian calendar
48   * (proleptic means extending indefinitely). The Julian calendar has leap years
49   * every four years, whereas the Gregorian has special rules for 100 and 400
50   * years. A meaningful result will thus be obtained for all input values.
51   * However before 8 CE, Julian leap years were irregular, and before 45 BCE
52   * there was no Julian calendar.
53   * <p>
54   * This chronology differs from
55   * {@link java.util.GregorianCalendar GregorianCalendar} in that years
56   * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1.
57   * The yearOfEra field produces results compatible with GregorianCalendar.
58   * <p>
59   * The Julian calendar does not have a year zero, and so year -1 is followed by
60   * year 1. If the Gregorian cutover date is specified at or before year -1
61   * (Julian), year zero is defined. In other words, the proleptic Gregorian
62   * chronology used by this class has a year zero.
63   * <p>
64   * To create a pure proleptic Julian chronology, use {@link JulianChronology},
65   * and to create a pure proleptic Gregorian chronology, use
66   * {@link GregorianChronology}.
67   * <p>
68   * GJChronology is thread-safe and immutable.
69   * 
70   * @author Brian S O'Neill
71   * @author Stephen Colebourne
72   * @since 1.0
73   */
74  public final class GJChronology extends AssembledChronology {
75  
76      /** Serialization lock */
77      private static final long serialVersionUID = -2545574827706931671L;
78  
79      /**
80       * Convert a datetime from one chronology to another.
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       * Convert a datetime from one chronology to another.
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      * 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<DateTimeZone, ArrayList<GJChronology>> cCache = new HashMap<DateTimeZone, ArrayList<GJChronology>>();
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<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      * 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;
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             // Maybe it's Julian.
381             instant = iJulianChronology.getDateTimeMillis
382                 (year, monthOfYear, dayOfMonth,
383                  hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
384             if (instant >= iCutoverMillis) {
385                 // Okay, it's in the illegal cutover gap.
386                 throw new IllegalArgumentException("Specified date does not exist");
387             }
388         }
389         return instant;
390     }
391 
392     /**
393      * Gets the cutover instant between Gregorian and Julian chronologies.
394      * @return the cutover instant
395      */
396     public Instant getGregorianCutover() {
397         return iCutoverInstant;
398     }
399 
400     /**
401      * Gets the minimum days needed for a week to be the first week in a year.
402      * 
403      * @return the minimum days
404      */
405     public int getMinimumDaysInFirstWeek() {
406         return iGregorianChronology.getMinimumDaysInFirstWeek();
407     }
408 
409     /**
410      * Checks if this chronology instance equals another.
411      * 
412      * @param obj  the object to compare to
413      * @return true if equal
414      * @since 1.6
415      */
416     public boolean equals(Object obj) {
417         return super.equals(obj);
418     }
419 
420     /**
421      * A suitable hash code for the chronology.
422      * 
423      * @return the hash code
424      * @since 1.6
425      */
426     public int hashCode() {
427         return "GJ".hashCode() * 11 + iJulianChronology.hashCode() +
428             iGregorianChronology.hashCode() + iCutoverInstant.hashCode();
429     }
430 
431     // Output
432     //-----------------------------------------------------------------------
433     /**
434      * Gets a debugging toString.
435      * 
436      * @return a debugging string
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         // Compute difference between the chronologies at the cutover instant
485         iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis);
486 
487         // Begin field definitions.
488 
489         // First just copy all the Gregorian fields and then override those
490         // that need special attention.
491         fields.copyFieldsFrom(gregorian);
492         
493         // Assuming cutover is at midnight, all time of day fields can be
494         // gregorian since they are unaffected by cutover.
495 
496         // Verify assumption.
497         if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
498             // Cutover is sometime in the day, so cutover fields are required
499             // for time of day.
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         // These fields just require basic cutover support.
516         {
517             fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis);
518         }
519 
520         // DayOfYear and weekOfWeekyear require special handling since cutover
521         // year has fewer days and weeks. Extend the cutover to the start of
522         // the next year or weekyear. This keeps the sequence unbroken during
523         // the cutover year.
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         // These fields are special because they have imprecise durations. The
538         // family of addition methods need special attention. Override affected
539         // duration fields as well.
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         // These fields require basic cutover support, except they must link to
565         // imprecise durations.
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      * This basic cutover field adjusts calls to 'get' and 'set' methods, and
593      * assumes that calls to add and addWrapField are unaffected by the cutover.
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          * @param julianField field from the chronology used before the cutover instant
608          * @param gregorianField field from the chronology used at and after the cutover
609          * @param cutoverMillis  the millis of the cutover
610          */
611         CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
612             this(julianField, gregorianField, cutoverMillis, false);
613         }
614 
615         /**
616          * @param julianField field from the chronology used before the cutover instant
617          * @param gregorianField field from the chronology used at and after the cutover
618          * @param cutoverMillis  the millis of the cutover
619          * @param convertByWeekyear
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             // Although average length of Julian and Gregorian years differ,
629             // use the Gregorian duration field because it is more accurate.
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             // overridden as superclass algorithm can't handle
685             // 2004-02-29 + 48 months -> 2008-02-29 type dates
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                     // Only adjust if gap fully crossed.
714                     if (instant + iGapDuration < iCutover) {
715                         instant = gregorianToJulian(instant);
716                     }
717                     // Verify that new value stuck.
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                     // Only adjust if gap fully crossed.
727                     if (instant - iGapDuration >= iCutover) {
728                         instant = julianToGregorian(instant);
729                     }
730                     // Verify that new value stuck.
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                     // Only adjust if gap fully crossed.
745                     if (instant + iGapDuration < iCutover) {
746                         instant = gregorianToJulian(instant);
747                     }
748                     // Cannot verify that new value stuck because set may be lenient.
749                 }
750             } else {
751                 instant = iJulianField.set(instant, text, locale);
752                 if (instant >= iCutover) {
753                     // Only adjust if gap fully crossed.
754                     if (instant - iGapDuration >= iCutover) {
755                         instant = julianToGregorian(instant);
756                     }
757                     // Cannot verify that new value stuck because set may be lenient.
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             // For all precise fields, the Julian and Gregorian limits are
794             // identical. Choose Julian to tighten up the year limits.
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             // Because the cutover may reduce the length of this field, verify
814             // the minimum by setting it.
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             // For all precise fields, the Julian and Gregorian limits are
825             // identical.
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             // Because the cutover may reduce the length of this field, verify
837             // the maximum by setting it.
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                     // Only adjust if gap fully crossed.
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                     // Only adjust if gap fully crossed.
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      * Cutover field for variable length fields. These fields internally call
923      * set whenever add is called. As a result, the same correction applied to
924      * set must be applied to add and addWrapField. Knowing when to use this
925      * field requires specific knowledge of how the GJ fields are implemented.
926      */
927     private final class ImpreciseCutoverField extends CutoverField {
928         private static final long serialVersionUID = 3410248757173576441L;
929 
930         /**
931          * Creates a duration field that links back to this.
932          */
933         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
934             this(julianField, gregorianField, null, cutoverMillis, false);
935         }
936 
937         /**
938          * Uses a shared duration field rather than creating a new one.
939          *
940          * @param durationField shared duration field
941          */
942         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
943                               DurationField durationField, long cutoverMillis)
944         {
945             this(julianField, gregorianField, durationField, cutoverMillis, false);
946         }
947 
948         /**
949          * Uses a shared duration field rather than creating a new one.
950          *
951          * @param durationField shared duration field
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                     // Only adjust if gap fully crossed.
969                     if (instant + iGapDuration < iCutover) {
970                         instant = gregorianToJulian(instant);
971                     }
972                 }
973             } else {
974                 instant = iJulianField.add(instant, value);
975                 if (instant >= iCutover) {
976                     // Only adjust if gap fully crossed.
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                     // Only adjust if gap fully crossed.
990                     if (instant + iGapDuration < iCutover) {
991                         instant = gregorianToJulian(instant);
992                     }
993                 }
994             } else {
995                 instant = iJulianField.add(instant, value);
996                 if (instant >= iCutover) {
997                     // Only adjust if gap fully crossed.
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                 // Remember, the add is being reversed. Since subtrahend is
1012                 // Julian, convert minuend to Julian to match.
1013                 minuendInstant = gregorianToJulian(minuendInstant);
1014                 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1015             } else {
1016                 if (subtrahendInstant < iCutover) {
1017                     return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1018                 }
1019                 // Remember, the add is being reversed. Since subtrahend is
1020                 // Gregorian, convert minuend to Gregorian to match.
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                 // Remember, the add is being reversed. Since subtrahend is
1032                 // Julian, convert minuend to Julian to match.
1033                 minuendInstant = gregorianToJulian(minuendInstant);
1034                 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1035             } else {
1036                 if (subtrahendInstant < iCutover) {
1037                     return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1038                 }
1039                 // Remember, the add is being reversed. Since subtrahend is
1040                 // Gregorian, convert minuend to Gregorian to match.
1041                 minuendInstant = julianToGregorian(minuendInstant);
1042                 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1043             }
1044         }
1045 
1046         // Since the imprecise fields have durations longer than the gap
1047         // duration, keep these methods simple. The inherited implementations
1048         // produce incorrect results.
1049         //
1050         // Degenerate case: If this field is a month, and the cutover is set
1051         // far into the future, then the gap duration may be so large as to
1052         // reduce the number of months in a year. If the missing month(s) are
1053         // at the beginning or end of the year, then the minimum and maximum
1054         // values are not 1 and 12. I don't expect this case to ever occur.
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      * Links the duration back to a ImpreciseCutoverField.
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 }