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     */
016    package org.joda.time.chrono;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.Locale;
021    import java.util.Map;
022    
023    import org.joda.time.Chronology;
024    import org.joda.time.DateTimeField;
025    import org.joda.time.DateTimeUtils;
026    import org.joda.time.DateTimeZone;
027    import org.joda.time.DurationField;
028    import org.joda.time.IllegalFieldValueException;
029    import org.joda.time.Instant;
030    import org.joda.time.ReadableInstant;
031    import org.joda.time.ReadablePartial;
032    import org.joda.time.field.BaseDateTimeField;
033    import org.joda.time.field.DecoratedDurationField;
034    import org.joda.time.format.DateTimeFormatter;
035    import org.joda.time.format.ISODateTimeFormat;
036    
037    /**
038     * Implements the Gregorian/Julian calendar system which is the calendar system
039     * used in most of the world. Wherever possible, it is recommended to use the
040     * {@link ISOChronology} instead.
041     * <p>
042     * The Gregorian calendar replaced the Julian calendar, and the point in time
043     * when this chronology switches can be controlled using the second parameter
044     * of the getInstance method. By default this cutover is set to the date the
045     * Gregorian calendar was first instituted, October 15, 1582.
046     * <p>
047     * Before this date, this chronology uses the proleptic Julian calendar
048     * (proleptic means extending indefinitely). The Julian calendar has leap years
049     * every four years, whereas the Gregorian has special rules for 100 and 400
050     * years. A meaningful result will thus be obtained for all input values.
051     * However before 8 CE, Julian leap years were irregular, and before 45 BCE
052     * there was no Julian calendar.
053     * <p>
054     * This chronology differs from
055     * {@link java.util.GregorianCalendar GregorianCalendar} in that years
056     * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1.
057     * The yearOfEra field produces results compatible with GregorianCalendar.
058     * <p>
059     * The Julian calendar does not have a year zero, and so year -1 is followed by
060     * year 1. If the Gregorian cutover date is specified at or before year -1
061     * (Julian), year zero is defined. In other words, the proleptic Gregorian
062     * chronology used by this class has a year zero.
063     * <p>
064     * To create a pure proleptic Julian chronology, use {@link JulianChronology},
065     * and to create a pure proleptic Gregorian chronology, use
066     * {@link GregorianChronology}.
067     * <p>
068     * GJChronology is thread-safe and immutable.
069     * 
070     * @author Brian S O'Neill
071     * @author Stephen Colebourne
072     * @since 1.0
073     */
074    public final class GJChronology extends AssembledChronology {
075    
076        /** Serialization lock */
077        private static final long serialVersionUID = -2545574827706931671L;
078    
079        /**
080         * Convert a datetime from one chronology to another.
081         */
082        private static long convertByYear(long instant, Chronology from, Chronology to) {
083            return to.getDateTimeMillis
084                (from.year().get(instant),
085                 from.monthOfYear().get(instant),
086                 from.dayOfMonth().get(instant),
087                 from.millisOfDay().get(instant));
088        }
089    
090        /**
091         * Convert a datetime from one chronology to another.
092         */
093        private static long convertByWeekyear(final long instant, Chronology from, Chronology to) {
094            long newInstant;
095            newInstant = to.weekyear().set(0, from.weekyear().get(instant));
096            newInstant = to.weekOfWeekyear().set(newInstant, from.weekOfWeekyear().get(instant));
097            newInstant = to.dayOfWeek().set(newInstant, from.dayOfWeek().get(instant));
098            newInstant = to.millisOfDay().set(newInstant, from.millisOfDay().get(instant));
099            return newInstant;
100        }
101    
102        /**
103         * The default GregorianJulian cutover point.
104         */
105        static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L);
106    
107        /** Cache of zone to chronology list */
108        private static final Map<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    }