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 = iGregorianChronology.getDateTimeMillis
364                (year, monthOfYear, dayOfMonth,
365                 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
366            if (instant < iCutoverMillis) {
367                // Maybe it's Julian.
368                instant = iJulianChronology.getDateTimeMillis
369                    (year, monthOfYear, dayOfMonth,
370                     hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
371                if (instant >= iCutoverMillis) {
372                    // Okay, it's in the illegal cutover gap.
373                    throw new IllegalArgumentException("Specified date does not exist");
374                }
375            }
376            return instant;
377        }
378    
379        /**
380         * Gets the cutover instant between Gregorian and Julian chronologies.
381         * @return the cutover instant
382         */
383        public Instant getGregorianCutover() {
384            return iCutoverInstant;
385        }
386    
387        /**
388         * Gets the minimum days needed for a week to be the first week in a year.
389         * 
390         * @return the minimum days
391         */
392        public int getMinimumDaysInFirstWeek() {
393            return iGregorianChronology.getMinimumDaysInFirstWeek();
394        }
395    
396        /**
397         * Checks if this chronology instance equals another.
398         * 
399         * @param obj  the object to compare to
400         * @return true if equal
401         * @since 1.6
402         */
403        public boolean equals(Object obj) {
404            return super.equals(obj);
405        }
406    
407        /**
408         * A suitable hash code for the chronology.
409         * 
410         * @return the hash code
411         * @since 1.6
412         */
413        public int hashCode() {
414            return "GJ".hashCode() * 11 + iJulianChronology.hashCode() +
415                iGregorianChronology.hashCode() + iCutoverInstant.hashCode();
416        }
417    
418        // Output
419        //-----------------------------------------------------------------------
420        /**
421         * Gets a debugging toString.
422         * 
423         * @return a debugging string
424         */
425        public String toString() {
426            StringBuffer sb = new StringBuffer(60);
427            sb.append("GJChronology");
428            sb.append('[');
429            sb.append(getZone().getID());
430            
431            if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) {
432                sb.append(",cutover=");
433                DateTimeFormatter printer;
434                if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) {
435                    printer = ISODateTimeFormat.date();
436                } else {
437                    printer = ISODateTimeFormat.dateTime();
438                }
439                printer.withChronology(withUTC()).printTo(sb, iCutoverMillis);
440            }
441            
442            if (getMinimumDaysInFirstWeek() != 4) {
443                sb.append(",mdfw=");
444                sb.append(getMinimumDaysInFirstWeek());
445            }
446            sb.append(']');
447            
448            return sb.toString();
449        }
450    
451        protected void assemble(Fields fields) {
452            Object[] params = (Object[])getParam();
453    
454            JulianChronology julian = (JulianChronology)params[0];
455            GregorianChronology gregorian = (GregorianChronology)params[1];
456            Instant cutoverInstant = (Instant)params[2];
457            iCutoverMillis = cutoverInstant.getMillis();
458    
459            iJulianChronology = julian;
460            iGregorianChronology = gregorian;
461            iCutoverInstant = cutoverInstant;
462    
463            if (getBase() != null) {
464                return;
465            }
466    
467            if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) {
468                throw new IllegalArgumentException();
469            }
470    
471            // Compute difference between the chronologies at the cutover instant
472            iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis);
473    
474            // Begin field definitions.
475    
476            // First just copy all the Gregorian fields and then override those
477            // that need special attention.
478            fields.copyFieldsFrom(gregorian);
479            
480            // Assuming cutover is at midnight, all time of day fields can be
481            // gregorian since they are unaffected by cutover.
482    
483            // Verify assumption.
484            if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
485                // Cutover is sometime in the day, so cutover fields are required
486                // for time of day.
487    
488                fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis);
489                fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis);
490                fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis);
491                fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis);
492                fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis);
493                fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis);
494                fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis);
495                fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis);
496                fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis);
497                fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(),
498                                                             fields.clockhourOfHalfday, iCutoverMillis);
499                fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis);
500            }
501    
502            // These fields just require basic cutover support.
503            {
504                fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis);
505            }
506    
507            // DayOfYear and weekOfWeekyear require special handling since cutover
508            // year has fewer days and weeks. Extend the cutover to the start of
509            // the next year or weekyear. This keeps the sequence unbroken during
510            // the cutover year.
511    
512            {
513                long cutover = gregorian.year().roundCeiling(iCutoverMillis);
514                fields.dayOfYear = new CutoverField(
515                    julian.dayOfYear(), fields.dayOfYear, cutover);
516            }
517    
518            {
519                long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis);
520                fields.weekOfWeekyear = new CutoverField(
521                    julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true);
522            }
523    
524            // These fields are special because they have imprecise durations. The
525            // family of addition methods need special attention. Override affected
526            // duration fields as well.
527            {
528                fields.year = new ImpreciseCutoverField(
529                    julian.year(), fields.year, iCutoverMillis);
530                fields.years = fields.year.getDurationField();
531                fields.yearOfEra = new ImpreciseCutoverField(
532                    julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis);
533                fields.yearOfCentury = new ImpreciseCutoverField(
534                    julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis);
535                
536                fields.centuryOfEra = new ImpreciseCutoverField(
537                    julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis);
538                fields.centuries = fields.centuryOfEra.getDurationField();
539                
540                fields.monthOfYear = new ImpreciseCutoverField(
541                    julian.monthOfYear(), fields.monthOfYear, iCutoverMillis);
542                fields.months = fields.monthOfYear.getDurationField();
543                
544                fields.weekyear = new ImpreciseCutoverField(
545                    julian.weekyear(), fields.weekyear, null, iCutoverMillis, true);
546                fields.weekyearOfCentury = new ImpreciseCutoverField(
547                    julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis);
548                fields.weekyears = fields.weekyear.getDurationField();
549            }
550    
551            // These fields require basic cutover support, except they must link to
552            // imprecise durations.
553            {
554                CutoverField cf = new CutoverField
555                    (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis);
556                cf.iRangeDurationField = fields.months;
557                fields.dayOfMonth = cf;
558            }
559        }
560    
561        long julianToGregorianByYear(long instant) {
562            return convertByYear(instant, iJulianChronology, iGregorianChronology);
563        }
564    
565        long gregorianToJulianByYear(long instant) {
566            return convertByYear(instant, iGregorianChronology, iJulianChronology);
567        }
568    
569        long julianToGregorianByWeekyear(long instant) {
570            return convertByWeekyear(instant, iJulianChronology, iGregorianChronology);
571        }
572    
573        long gregorianToJulianByWeekyear(long instant) {
574            return convertByWeekyear(instant, iGregorianChronology, iJulianChronology);
575        }
576    
577        //-----------------------------------------------------------------------
578        /**
579         * This basic cutover field adjusts calls to 'get' and 'set' methods, and
580         * assumes that calls to add and addWrapField are unaffected by the cutover.
581         */
582        private class CutoverField extends BaseDateTimeField {
583            private static final long serialVersionUID = 3528501219481026402L;
584    
585            final DateTimeField iJulianField;
586            final DateTimeField iGregorianField;
587            final long iCutover;
588            final boolean iConvertByWeekyear;
589    
590            protected DurationField iDurationField;
591            protected DurationField iRangeDurationField;
592    
593            /**
594             * @param julianField field from the chronology used before the cutover instant
595             * @param gregorianField field from the chronology used at and after the cutover
596             * @param cutoverMillis  the millis of the cutover
597             */
598            CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
599                this(julianField, gregorianField, cutoverMillis, false);
600            }
601    
602            /**
603             * @param julianField field from the chronology used before the cutover instant
604             * @param gregorianField field from the chronology used at and after the cutover
605             * @param cutoverMillis  the millis of the cutover
606             * @param convertByWeekyear
607             */
608            CutoverField(DateTimeField julianField, DateTimeField gregorianField,
609                         long cutoverMillis, boolean convertByWeekyear) {
610                super(gregorianField.getType());
611                iJulianField = julianField;
612                iGregorianField = gregorianField;
613                iCutover = cutoverMillis;
614                iConvertByWeekyear = convertByWeekyear;
615                // Although average length of Julian and Gregorian years differ,
616                // use the Gregorian duration field because it is more accurate.
617                iDurationField = gregorianField.getDurationField();
618    
619                DurationField rangeField = gregorianField.getRangeDurationField();
620                if (rangeField == null) {
621                    rangeField = julianField.getRangeDurationField();
622                }
623                iRangeDurationField = rangeField;
624            }
625    
626            public boolean isLenient() {
627                return false;
628            }
629    
630            public int get(long instant) {
631                if (instant >= iCutover) {
632                    return iGregorianField.get(instant);
633                } else {
634                    return iJulianField.get(instant);
635                }
636            }
637    
638            public String getAsText(long instant, Locale locale) {
639                if (instant >= iCutover) {
640                    return iGregorianField.getAsText(instant, locale);
641                } else {
642                    return iJulianField.getAsText(instant, locale);
643                }
644            }
645    
646            public String getAsText(int fieldValue, Locale locale) {
647                return iGregorianField.getAsText(fieldValue, locale);
648            }
649    
650            public String getAsShortText(long instant, Locale locale) {
651                if (instant >= iCutover) {
652                    return iGregorianField.getAsShortText(instant, locale);
653                } else {
654                    return iJulianField.getAsShortText(instant, locale);
655                }
656            }
657    
658            public String getAsShortText(int fieldValue, Locale locale) {
659                return iGregorianField.getAsShortText(fieldValue, locale);
660            }
661    
662            public long add(long instant, int value) {
663                return iGregorianField.add(instant, value);
664            }
665    
666            public long add(long instant, long value) {
667                return iGregorianField.add(instant, value);
668            }
669    
670            public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) {
671                // overridden as superclass algorithm can't handle
672                // 2004-02-29 + 48 months -> 2008-02-29 type dates
673                if (valueToAdd == 0) {
674                    return values;
675                }
676                if (DateTimeUtils.isContiguous(partial)) {
677                    long instant = 0L;
678                    for (int i = 0, isize = partial.size(); i < isize; i++) {
679                        instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]);
680                    }
681                    instant = add(instant, valueToAdd);
682                    return GJChronology.this.get(partial, instant);
683                } else {
684                    return super.add(partial, fieldIndex, values, valueToAdd);
685                }
686            }
687    
688            public int getDifference(long minuendInstant, long subtrahendInstant) {
689                return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
690            }
691    
692            public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
693                return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
694            }
695    
696            public long set(long instant, int value) {
697                if (instant >= iCutover) {
698                    instant = iGregorianField.set(instant, value);
699                    if (instant < iCutover) {
700                        // Only adjust if gap fully crossed.
701                        if (instant + iGapDuration < iCutover) {
702                            instant = gregorianToJulian(instant);
703                        }
704                        // Verify that new value stuck.
705                        if (get(instant) != value) {
706                            throw new IllegalFieldValueException
707                                (iGregorianField.getType(), Integer.valueOf(value), null, null);
708                        }
709                    }
710                } else {
711                    instant = iJulianField.set(instant, value);
712                    if (instant >= iCutover) {
713                        // Only adjust if gap fully crossed.
714                        if (instant - iGapDuration >= iCutover) {
715                            instant = julianToGregorian(instant);
716                        }
717                        // Verify that new value stuck.
718                        if (get(instant) != value) {
719                           throw new IllegalFieldValueException
720                                (iJulianField.getType(), Integer.valueOf(value), null, null);
721                        }
722                    }
723                }
724                return instant;
725            }
726    
727            public long set(long instant, String text, Locale locale) {
728                if (instant >= iCutover) {
729                    instant = iGregorianField.set(instant, text, locale);
730                    if (instant < iCutover) {
731                        // Only adjust if gap fully crossed.
732                        if (instant + iGapDuration < iCutover) {
733                            instant = gregorianToJulian(instant);
734                        }
735                        // Cannot verify that new value stuck because set may be lenient.
736                    }
737                } else {
738                    instant = iJulianField.set(instant, text, locale);
739                    if (instant >= iCutover) {
740                        // Only adjust if gap fully crossed.
741                        if (instant - iGapDuration >= iCutover) {
742                            instant = julianToGregorian(instant);
743                        }
744                        // Cannot verify that new value stuck because set may be lenient.
745                    }
746                }
747                return instant;
748            }
749    
750            public DurationField getDurationField() {
751                return iDurationField;
752            }
753    
754            public DurationField getRangeDurationField() {
755                return iRangeDurationField;
756            }
757    
758            public boolean isLeap(long instant) {
759                if (instant >= iCutover) {
760                    return iGregorianField.isLeap(instant);
761                } else {
762                    return iJulianField.isLeap(instant);
763                }
764            }
765    
766            public int getLeapAmount(long instant) {
767                if (instant >= iCutover) {
768                    return iGregorianField.getLeapAmount(instant);
769                } else {
770                    return iJulianField.getLeapAmount(instant);
771                }
772            }
773    
774            public DurationField getLeapDurationField() {
775                return iGregorianField.getLeapDurationField();
776            }
777    
778    
779            public int getMinimumValue() {
780                // For all precise fields, the Julian and Gregorian limits are
781                // identical. Choose Julian to tighten up the year limits.
782                return iJulianField.getMinimumValue();
783            }
784    
785            public int getMinimumValue(ReadablePartial partial) {
786                return iJulianField.getMinimumValue(partial);
787            }
788    
789            public int getMinimumValue(ReadablePartial partial, int[] values) {
790                return iJulianField.getMinimumValue(partial, values);
791            }
792    
793            public int getMinimumValue(long instant) {
794                if (instant < iCutover) {
795                    return iJulianField.getMinimumValue(instant);
796                }
797    
798                int min = iGregorianField.getMinimumValue(instant);
799    
800                // Because the cutover may reduce the length of this field, verify
801                // the minimum by setting it.
802                instant = iGregorianField.set(instant, min);
803                if (instant < iCutover) {
804                    min = iGregorianField.get(iCutover);
805                }
806    
807                return min;
808            }
809    
810            public int getMaximumValue() {
811                // For all precise fields, the Julian and Gregorian limits are
812                // identical.
813                return iGregorianField.getMaximumValue();
814            }
815    
816            public int getMaximumValue(long instant) {
817                if (instant >= iCutover) {
818                    return iGregorianField.getMaximumValue(instant);
819                }
820    
821                int max = iJulianField.getMaximumValue(instant);
822    
823                // Because the cutover may reduce the length of this field, verify
824                // the maximum by setting it.
825                instant = iJulianField.set(instant, max);
826                if (instant >= iCutover) {
827                    max = iJulianField.get(iJulianField.add(iCutover, -1));
828                }
829    
830                return max;
831            }
832    
833            public int getMaximumValue(ReadablePartial partial) {
834                long instant = GJChronology.getInstanceUTC().set(partial, 0L);
835                return getMaximumValue(instant);
836            }
837    
838            public int getMaximumValue(ReadablePartial partial, int[] values) {
839                Chronology chrono = GJChronology.getInstanceUTC();
840                long instant = 0L;
841                for (int i = 0, isize = partial.size(); i < isize; i++) {
842                    DateTimeField field = partial.getFieldType(i).getField(chrono);
843                    if (values[i] <= field.getMaximumValue(instant)) {
844                        instant = field.set(instant, values[i]);
845                    }
846                }
847                return getMaximumValue(instant);
848            }
849    
850            public long roundFloor(long instant) {
851                if (instant >= iCutover) {
852                    instant = iGregorianField.roundFloor(instant);
853                    if (instant < iCutover) {
854                        // Only adjust if gap fully crossed.
855                        if (instant + iGapDuration < iCutover) {
856                            instant = gregorianToJulian(instant);
857                        }
858                    }
859                } else {
860                    instant = iJulianField.roundFloor(instant);
861                }
862                return instant;
863            }
864    
865            public long roundCeiling(long instant) {
866                if (instant >= iCutover) {
867                    instant = iGregorianField.roundCeiling(instant);
868                } else {
869                    instant = iJulianField.roundCeiling(instant);
870                    if (instant >= iCutover) {
871                        // Only adjust if gap fully crossed.
872                        if (instant - iGapDuration >= iCutover) {
873                            instant = julianToGregorian(instant);
874                        }
875                    }
876                }
877                return instant;
878            }
879    
880            public int getMaximumTextLength(Locale locale) {
881                return Math.max(iJulianField.getMaximumTextLength(locale),
882                                iGregorianField.getMaximumTextLength(locale));
883            }
884    
885            public int getMaximumShortTextLength(Locale locale) {
886                return Math.max(iJulianField.getMaximumShortTextLength(locale),
887                                iGregorianField.getMaximumShortTextLength(locale));
888            }
889    
890            protected long julianToGregorian(long instant) {
891                if (iConvertByWeekyear) {
892                    return julianToGregorianByWeekyear(instant);
893                } else {
894                    return julianToGregorianByYear(instant);
895                }
896            }
897    
898            protected long gregorianToJulian(long instant) {
899                if (iConvertByWeekyear) {
900                    return gregorianToJulianByWeekyear(instant);
901                } else {
902                    return gregorianToJulianByYear(instant);
903                }
904            }
905        }
906    
907        //-----------------------------------------------------------------------
908        /**
909         * Cutover field for variable length fields. These fields internally call
910         * set whenever add is called. As a result, the same correction applied to
911         * set must be applied to add and addWrapField. Knowing when to use this
912         * field requires specific knowledge of how the GJ fields are implemented.
913         */
914        private final class ImpreciseCutoverField extends CutoverField {
915            private static final long serialVersionUID = 3410248757173576441L;
916    
917            /**
918             * Creates a duration field that links back to this.
919             */
920            ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
921                this(julianField, gregorianField, null, cutoverMillis, false);
922            }
923    
924            /**
925             * Uses a shared duration field rather than creating a new one.
926             *
927             * @param durationField shared duration field
928             */
929            ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
930                                  DurationField durationField, long cutoverMillis)
931            {
932                this(julianField, gregorianField, durationField, cutoverMillis, false);
933            }
934    
935            /**
936             * Uses a shared duration field rather than creating a new one.
937             *
938             * @param durationField shared duration field
939             */
940            ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
941                                  DurationField durationField,
942                                  long cutoverMillis, boolean convertByWeekyear)
943            {
944                super(julianField, gregorianField, cutoverMillis, convertByWeekyear);
945                if (durationField == null) {
946                    durationField = new LinkedDurationField(iDurationField, this);
947                }
948                iDurationField = durationField;
949            }
950    
951            public long add(long instant, int value) {
952                if (instant >= iCutover) {
953                    instant = iGregorianField.add(instant, value);
954                    if (instant < iCutover) {
955                        // Only adjust if gap fully crossed.
956                        if (instant + iGapDuration < iCutover) {
957                            instant = gregorianToJulian(instant);
958                        }
959                    }
960                } else {
961                    instant = iJulianField.add(instant, value);
962                    if (instant >= iCutover) {
963                        // Only adjust if gap fully crossed.
964                        if (instant - iGapDuration >= iCutover) {
965                            instant = julianToGregorian(instant);
966                        }
967                    }
968                }
969                return instant;
970            }
971            
972            public long add(long instant, long value) {
973                if (instant >= iCutover) {
974                    instant = iGregorianField.add(instant, value);
975                    if (instant < iCutover) {
976                        // Only adjust if gap fully crossed.
977                        if (instant + iGapDuration < iCutover) {
978                            instant = gregorianToJulian(instant);
979                        }
980                    }
981                } else {
982                    instant = iJulianField.add(instant, value);
983                    if (instant >= iCutover) {
984                        // Only adjust if gap fully crossed.
985                        if (instant - iGapDuration >= iCutover) {
986                            instant = julianToGregorian(instant);
987                        }
988                    }
989                }
990                return instant;
991            }
992    
993            public int getDifference(long minuendInstant, long subtrahendInstant) {
994                if (minuendInstant >= iCutover) {
995                    if (subtrahendInstant >= iCutover) {
996                        return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
997                    }
998                    // Remember, the add is being reversed. Since subtrahend is
999                    // Julian, convert minuend to Julian to match.
1000                    minuendInstant = gregorianToJulian(minuendInstant);
1001                    return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1002                } else {
1003                    if (subtrahendInstant < iCutover) {
1004                        return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1005                    }
1006                    // Remember, the add is being reversed. Since subtrahend is
1007                    // Gregorian, convert minuend to Gregorian to match.
1008                    minuendInstant = julianToGregorian(minuendInstant);
1009                    return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
1010                }
1011            }
1012    
1013            public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1014                if (minuendInstant >= iCutover) {
1015                    if (subtrahendInstant >= iCutover) {
1016                        return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1017                    }
1018                    // Remember, the add is being reversed. Since subtrahend is
1019                    // Julian, convert minuend to Julian to match.
1020                    minuendInstant = gregorianToJulian(minuendInstant);
1021                    return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1022                } else {
1023                    if (subtrahendInstant < iCutover) {
1024                        return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1025                    }
1026                    // Remember, the add is being reversed. Since subtrahend is
1027                    // Gregorian, convert minuend to Gregorian to match.
1028                    minuendInstant = julianToGregorian(minuendInstant);
1029                    return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1030                }
1031            }
1032    
1033            // Since the imprecise fields have durations longer than the gap
1034            // duration, keep these methods simple. The inherited implementations
1035            // produce incorrect results.
1036            //
1037            // Degenerate case: If this field is a month, and the cutover is set
1038            // far into the future, then the gap duration may be so large as to
1039            // reduce the number of months in a year. If the missing month(s) are
1040            // at the beginning or end of the year, then the minimum and maximum
1041            // values are not 1 and 12. I don't expect this case to ever occur.
1042    
1043            public int getMinimumValue(long instant) {
1044                if (instant >= iCutover) {
1045                    return iGregorianField.getMinimumValue(instant);
1046                } else {
1047                    return iJulianField.getMinimumValue(instant);
1048                }
1049            }
1050    
1051            public int getMaximumValue(long instant) {
1052                if (instant >= iCutover) {
1053                    return iGregorianField.getMaximumValue(instant);
1054                } else {
1055                    return iJulianField.getMaximumValue(instant);
1056                }
1057            }
1058        }
1059    
1060        //-----------------------------------------------------------------------
1061        /**
1062         * Links the duration back to a ImpreciseCutoverField.
1063         */
1064        private static class LinkedDurationField extends DecoratedDurationField {
1065            private static final long serialVersionUID = 4097975388007713084L;
1066    
1067            private final ImpreciseCutoverField iField;
1068    
1069            LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) {
1070                super(durationField, durationField.getType());
1071                iField = dateTimeField;
1072            }
1073    
1074            public long add(long instant, int value) {
1075                return iField.add(instant, value);
1076            }
1077    
1078            public long add(long instant, long value) {
1079                return iField.add(instant, value);
1080            }
1081    
1082            public int getDifference(long minuendInstant, long subtrahendInstant) {
1083                return iField.getDifference(minuendInstant, subtrahendInstant);
1084            }
1085    
1086            public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1087                return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1088            }
1089        }
1090    
1091    }