001/*
002 *  Copyright 2001-2009 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 */
016package org.joda.time.chrono;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.Locale;
021import java.util.Map;
022
023import org.joda.time.Chronology;
024import org.joda.time.DateTimeField;
025import org.joda.time.DateTimeUtils;
026import org.joda.time.DateTimeZone;
027import org.joda.time.DurationField;
028import org.joda.time.IllegalFieldValueException;
029import org.joda.time.Instant;
030import org.joda.time.ReadableInstant;
031import org.joda.time.ReadablePartial;
032import org.joda.time.field.BaseDateTimeField;
033import org.joda.time.field.DecoratedDurationField;
034import org.joda.time.format.DateTimeFormatter;
035import 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 */
074public 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<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}