Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    *  Copyright 2001-2005 Stephen Colebourne
    *
    *  Licensed under the Apache License, Version 2.0 (the "License");
    *  you may not use this file except in compliance with the License.
    *  You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  package org.joda.time.chrono;
  
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  
Implements the Gregorian/Julian calendar system which is the calendar system used in most of the world. Wherever possible, it is recommended to use the ISOChronology instead.

The Gregorian calendar replaced the Julian calendar, and the point in time when this chronology switches can be controlled using the second parameter of the getInstance method. By default this cutover is set to the date the Gregorian calendar was first instituted, October 15, 1582.

Before this date, this chronology uses the proleptic Julian calendar (proleptic means extending indefinitely). The Julian calendar has leap years every four years, whereas the Gregorian has special rules for 100 and 400 years. A meaningful result will thus be obtained for all input values. However before 8 CE, Julian leap years were irregular, and before 45 BCE there was no Julian calendar.

This chronology differs from GregorianCalendar in that years in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1. The yearOfEra field produces results compatible with GregorianCalendar.

The Julian calendar does not have a year zero, and so year -1 is followed by year 1. If the Gregorian cutover date is specified at or before year -1 (Julian), year zero is defined. In other words, the proleptic Gregorian chronology used by this class has a year zero.

To create a pure proleptic Julian chronology, use JulianChronology, and to create a pure proleptic Gregorian chronology, use GregorianChronology.

GJChronology is thread-safe and immutable.

Author(s):
Brian S O'Neill
Stephen Colebourne
Since:
1.0
  
  public final class GJChronology extends AssembledChronology {

    
Serialization lock
  
      private static final long serialVersionUID = -2545574827706931671L;

    
Convert a datetime from one chronology to another.
  
      private static long convertByYear(long instantChronology fromChronology to) {
          return to.getDateTimeMillis
              (from.year().get(instant),
               from.monthOfYear().get(instant),
               from.dayOfMonth().get(instant),
               from.millisOfDay().get(instant));
      }

    
Convert a datetime from one chronology to another.
  
      private static long convertByWeekyear(final long instantChronology fromChronology to) {
          long newInstant;
          newInstant = to.weekyear().set(0, from.weekyear().get(instant));
          newInstant = to.weekOfWeekyear().set(newInstantfrom.weekOfWeekyear().get(instant));
          newInstant = to.dayOfWeek().set(newInstantfrom.dayOfWeek().get(instant));
          newInstant = to.millisOfDay().set(newInstantfrom.millisOfDay().get(instant));
          return newInstant;
     }

    
The default GregorianJulian cutover point.
 
     static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L);

    
Cache of zone to chronology list
 
     private static final Map cCache = new HashMap();

    
Factory method returns instances of the default GJ cutover chronology. This uses a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by October 15, 1582 (Gregorian).

The first day of the week is designated to be Monday, and the minimum days in the first week of the year is 4.

The time zone of the returned instance is UTC.

 
     public static GJChronology getInstanceUTC() {
         return getInstance(., 4);
     }

    
Factory method returns instances of the default GJ cutover chronology. This uses a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by October 15, 1582 (Gregorian).

The first day of the week is designated to be Monday, and the minimum days in the first week of the year is 4.

The returned chronology is in the default time zone.

 
     public static GJChronology getInstance() {
         return getInstance(DateTimeZone.getDefault(), , 4);
     }

    
Factory method returns instances of the GJ cutover chronology. This uses a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by October 15, 1582 (Gregorian).

The first day of the week is designated to be Monday, and the minimum days in the first week of the year is 4.

Parameters:
zone the time zone to use, null is default
 
     public static GJChronology getInstance(DateTimeZone zone) {
         return getInstance(zone, 4);
     }

    
Factory method returns instances of the GJ cutover chronology. Any cutover date may be specified.

The first day of the week is designated to be Monday, and the minimum days in the first week of the year is 4.

Parameters:
zone the time zone to use, null is default
gregorianCutover the cutover to use, null means default
 
     public static GJChronology getInstance(
             DateTimeZone zone,
             ReadableInstant gregorianCutover) {
         
         return getInstance(zonegregorianCutover, 4);
     }
    
    
Factory method returns instances of the GJ cutover chronology. Any cutover date may be specified.

Parameters:
zone the time zone to use, null is default
gregorianCutover the cutover to use, null means default
minDaysInFirstWeek minimum number of days in first week of the year; default is 4
 
     public static synchronized GJChronology getInstance(
             DateTimeZone zone,
             ReadableInstant gregorianCutover,
             int minDaysInFirstWeek) {
         
         zone = DateTimeUtils.getZone(zone);
         Instant cutoverInstant;
         if (gregorianCutover == null) {
             cutoverInstant = ;
         } else {
             cutoverInstant = gregorianCutover.toInstant();
         }
 
         GJChronology chrono;
 
         ArrayList chronos = (ArrayList).get(zone);
         if (chronos == null) {
             chronos = new ArrayList(2);
             .put(zonechronos);
         } else {
             for (int i=chronos.size(); --i>=0; ) {
                 chrono = (GJChronology)chronos.get(i);
                 if (minDaysInFirstWeek == chrono.getMinimumDaysInFirstWeek() &&
                     cutoverInstant.equals(chrono.getGregorianCutover())) {
                     
                     return chrono;
                 }
             }
         }
 
         if (zone == .) {
             chrono = new GJChronology
                 (JulianChronology.getInstance(zoneminDaysInFirstWeek),
                  GregorianChronology.getInstance(zoneminDaysInFirstWeek),
                  cutoverInstant);
         } else {
             chrono = getInstance(.cutoverInstantminDaysInFirstWeek);
             chrono = new GJChronology
                 (ZonedChronology.getInstance(chronozone),
                  chrono.iJulianChronology,
                  chrono.iGregorianChronology,
                  chrono.iCutoverInstant);
         }
 
         chronos.add(chrono);
 
         return chrono;
     }

    
Factory method returns instances of the GJ cutover chronology. Any cutover date may be specified.

Parameters:
zone the time zone to use, null is default
gregorianCutover the cutover to use
minDaysInFirstWeek minimum number of days in first week of the year; default is 4
 
     public static GJChronology getInstance(
             DateTimeZone zone,
             long gregorianCutover,
             int minDaysInFirstWeek) {
         
         Instant cutoverInstant;
         if (gregorianCutover == .getMillis()) {
             cutoverInstant = null;
         } else {
             cutoverInstant = new Instant(gregorianCutover);
         }
         return getInstance(zonecutoverInstantminDaysInFirstWeek);
     }
 
     //-----------------------------------------------------------------------
     private Instant iCutoverInstant;
 
     private long iCutoverMillis;
     private long iGapDuration;

    

Parameters:
julian chronology used before the cutover instant
gregorian chronology used at and after the cutover instant
cutoverInstant instant when the gregorian chronology began
 
     private GJChronology(JulianChronology julian,
                          GregorianChronology gregorian,
                          Instant cutoverInstant) {
         super(nullnew Object[] {juliangregoriancutoverInstant});
     }

    
Called when applying a time zone.
 
     private GJChronology(Chronology base,
                          JulianChronology julian,
                          GregorianChronology gregorian,
                          Instant cutoverInstant) {
         super(basenew Object[] {juliangregoriancutoverInstant});
     }

    
Serialization singleton
 
     private Object readResolve() {
     }
 
     public DateTimeZone getZone() {
         Chronology base;
         if ((base = getBase()) != null) {
             return base.getZone();
         }
         return .;
     }
 
     // Conversion
     //-----------------------------------------------------------------------
     
Gets the Chronology in the UTC time zone.

Returns:
the chronology in UTC
 
     public Chronology withUTC() {
         return withZone(.);
     }

    
Gets the Chronology in a specific time zone.

Parameters:
zone the zone to get the chronology in, null is default
Returns:
the chronology
 
     public Chronology withZone(DateTimeZone zone) {
         if (zone == null) {
             zone = DateTimeZone.getDefault();
         }
         if (zone == getZone()) {
             return this;
         }
         return getInstance(zonegetMinimumDaysInFirstWeek());
     }
 
     public long getDateTimeMillis(int yearint monthOfYearint dayOfMonth,
                                   int millisOfDay)
         throws IllegalArgumentException
     {
         Chronology base;
         if ((base = getBase()) != null) {
             return base.getDateTimeMillis(yearmonthOfYeardayOfMonthmillisOfDay);
         }
 
         // Assume date is Gregorian.
         long instant = .getDateTimeMillis
             (yearmonthOfYeardayOfMonthmillisOfDay);
         if (instant < ) {
             // Maybe it's Julian.
             instant = .getDateTimeMillis
                 (yearmonthOfYeardayOfMonthmillisOfDay);
             if (instant >= ) {
                 // Okay, it's in the illegal cutover gap.
                 throw new IllegalArgumentException("Specified date does not exist");
             }
         }
         return instant;
     }
 
     public long getDateTimeMillis(int yearint monthOfYearint dayOfMonth,
                                   int hourOfDayint minuteOfHour,
                                   int secondOfMinuteint millisOfSecond)
         throws IllegalArgumentException
     {
         Chronology base;
         if ((base = getBase()) != null) {
             return base.getDateTimeMillis
                 (yearmonthOfYeardayOfMonth,
                  hourOfDayminuteOfHoursecondOfMinutemillisOfSecond);
         }
 
         // Assume date is Gregorian.
         long instant = .getDateTimeMillis
             (yearmonthOfYeardayOfMonth,
              hourOfDayminuteOfHoursecondOfMinutemillisOfSecond);
         if (instant < ) {
             // Maybe it's Julian.
             instant = .getDateTimeMillis
                 (yearmonthOfYeardayOfMonth,
                  hourOfDayminuteOfHoursecondOfMinutemillisOfSecond);
             if (instant >= ) {
                 // Okay, it's in the illegal cutover gap.
                 throw new IllegalArgumentException("Specified date does not exist");
             }
         }
         return instant;
     }

    
Gets the cutover instant between Gregorian and Julian chronologies.

Returns:
the cutover instant
 
     public Instant getGregorianCutover() {
         return ;
     }

    
Gets the minimum days needed for a week to be the first week in a year.

Returns:
the minimum days
 
     public int getMinimumDaysInFirstWeek() {
     }

    
Checks if this chronology instance equals another.

Parameters:
obj the object to compare to
Returns:
true if equal
Since:
1.6
 
     public boolean equals(Object obj) {
         return super.equals(obj);
     }

    
A suitable hash code for the chronology.

Returns:
the hash code
Since:
1.6
 
     public int hashCode() {
         return "GJ".hashCode() * 11 + .hashCode() +
             .hashCode() + .hashCode();
     }
 
     // Output
     //-----------------------------------------------------------------------
     
Gets a debugging toString.

Returns:
a debugging string
 
     public String toString() {
         StringBuffer sb = new StringBuffer(60);
         sb.append("GJChronology");
         sb.append('[');
         sb.append(getZone().getID());
         
         if ( != .getMillis()) {
             sb.append(",cutover=");
             DateTimeFormatter printer;
             if (withUTC().dayOfYear().remainder() == 0) {
                 printer = ISODateTimeFormat.date();
             } else {
                 printer = ISODateTimeFormat.dateTime();
             }
             printer.withChronology(withUTC()).printTo(sb);
         }
         
         if (getMinimumDaysInFirstWeek() != 4) {
             sb.append(",mdfw=");
             sb.append(getMinimumDaysInFirstWeek());
         }
         sb.append(']');
         
         return sb.toString();
     }
 
     protected void assemble(Fields fields) {
         Object[] params = (Object[])getParam();
 
         JulianChronology julian = (JulianChronology)params[0];
         GregorianChronology gregorian = (GregorianChronology)params[1];
         Instant cutoverInstant = (Instant)params[2];
          = cutoverInstant.getMillis();
 
          = julian;
          = gregorian;
          = cutoverInstant;
 
         if (getBase() != null) {
             return;
         }
 
         if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) {
             throw new IllegalArgumentException();
         }
 
         // Compute difference between the chronologies at the cutover instant
 
         // Begin field definitions.
 
         // First just copy all the Gregorian fields and then override those
         // that need special attention.
         fields.copyFieldsFrom(gregorian);
         
         // Assuming cutover is at midnight, all time of day fields can be
         // gregorian since they are unaffected by cutover.
 
         // Verify assumption.
         if (gregorian.millisOfDay().get() == 0) {
             // Cutover is sometime in the day, so cutover fields are required
             // for time of day.
 
             fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond);
             fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay);
             fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute);
             fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay);
             fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour);
             fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay);
             fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay);
             fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday);
             fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay);
             fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(),
                                                          fields.clockhourOfHalfday);
             fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay);
         }
 
         // These fields just require basic cutover support.
         {
             fields.era = new CutoverField(julian.era(), fields.era);
         }
 
         // DayOfYear and weekOfWeekyear require special handling since cutover
         // year has fewer days and weeks. Extend the cutover to the start of
         // the next year or weekyear. This keeps the sequence unbroken during
         // the cutover year.
 
         {
             long cutover = gregorian.year().roundCeiling();
             fields.dayOfYear = new CutoverField(
                 julian.dayOfYear(), fields.dayOfYearcutover);
         }
 
         {
             long cutover = gregorian.weekyear().roundCeiling();
             fields.weekOfWeekyear = new CutoverField(
                 julian.weekOfWeekyear(), fields.weekOfWeekyearcutovertrue);
         }
 
         // These fields are special because they have imprecise durations. The
         // family of addition methods need special attention. Override affected
         // duration fields as well.
         {
             fields.year = new ImpreciseCutoverField(
                 julian.year(), fields.year);
             fields.years = fields.year.getDurationField();
             fields.yearOfEra = new ImpreciseCutoverField(
                 julian.yearOfEra(), fields.yearOfErafields.years);
             fields.yearOfCentury = new ImpreciseCutoverField(
                 julian.yearOfCentury(), fields.yearOfCenturyfields.years);
             
             fields.centuryOfEra = new ImpreciseCutoverField(
                 julian.centuryOfEra(), fields.centuryOfEra);
             fields.centuries = fields.centuryOfEra.getDurationField();
             
             fields.monthOfYear = new ImpreciseCutoverField(
                 julian.monthOfYear(), fields.monthOfYear);
             fields.months = fields.monthOfYear.getDurationField();
             
             fields.weekyear = new ImpreciseCutoverField(
                 julian.weekyear(), fields.weekyearnulltrue);
             fields.weekyearOfCentury = new ImpreciseCutoverField(
                 julian.weekyearOfCentury(), fields.weekyearOfCenturyfields.weekyears);
             fields.weekyears = fields.weekyear.getDurationField();
         }
 
         // These fields require basic cutover support, except they must link to
         // imprecise durations.
         {
             CutoverField cf = new CutoverField
                 (julian.dayOfMonth(), fields.dayOfMonth);
             cf.iRangeDurationField = fields.months;
             fields.dayOfMonth = cf;
         }
     }
 
     long julianToGregorianByYear(long instant) {
         return convertByYear(instant);
     }
 
     long gregorianToJulianByYear(long instant) {
         return convertByYear(instant);
     }
 
     long julianToGregorianByWeekyear(long instant) {
         return convertByWeekyear(instant);
     }
 
     long gregorianToJulianByWeekyear(long instant) {
         return convertByWeekyear(instant);
     }
 
     //-----------------------------------------------------------------------
     
This basic cutover field adjusts calls to 'get' and 'set' methods, and assumes that calls to add and addWrapField are unaffected by the cutover.
 
     private class CutoverField extends BaseDateTimeField {
         private static final long serialVersionUID = 3528501219481026402L;
 
         final DateTimeField iJulianField;
         final DateTimeField iGregorianField;
         final long iCutover;
         final boolean iConvertByWeekyear;
 
         protected DurationField iDurationField;
         protected DurationField iRangeDurationField;

        

Parameters:
julianField field from the chronology used before the cutover instant
gregorianField field from the chronology used at and after the cutover
cutoverMillis the millis of the cutover
 
         CutoverField(DateTimeField julianFieldDateTimeField gregorianFieldlong cutoverMillis) {
             this(julianFieldgregorianFieldcutoverMillisfalse);
         }

        

Parameters:
julianField field from the chronology used before the cutover instant
gregorianField field from the chronology used at and after the cutover
cutoverMillis the millis of the cutover
convertByWeekyear
 
         CutoverField(DateTimeField julianFieldDateTimeField gregorianField,
                      long cutoverMillisboolean convertByWeekyear) {
             super(gregorianField.getType());
              = julianField;
              = gregorianField;
              = cutoverMillis;
              = convertByWeekyear;
             // Although average length of Julian and Gregorian years differ,
             // use the Gregorian duration field because it is more accurate.
              = gregorianField.getDurationField();
 
             DurationField rangeField = gregorianField.getRangeDurationField();
             if (rangeField == null) {
                 rangeField = julianField.getRangeDurationField();
             }
              = rangeField;
         }
 
         public boolean isLenient() {
             return false;
         }
 
         public int get(long instant) {
             if (instant >= ) {
                 return .get(instant);
             } else {
                 return .get(instant);
             }
         }
 
         public String getAsText(long instantLocale locale) {
             if (instant >= ) {
                 return .getAsText(instantlocale);
             } else {
                 return .getAsText(instantlocale);
             }
         }
 
         public String getAsText(int fieldValueLocale locale) {
             return .getAsText(fieldValuelocale);
         }
 
         public String getAsShortText(long instantLocale locale) {
             if (instant >= ) {
                 return .getAsShortText(instantlocale);
             } else {
                 return .getAsShortText(instantlocale);
             }
         }
 
         public String getAsShortText(int fieldValueLocale locale) {
             return .getAsShortText(fieldValuelocale);
         }
 
         public long add(long instantint value) {
             return .add(instantvalue);
         }
 
         public long add(long instantlong value) {
             return .add(instantvalue);
         }
 
         public int[] add(ReadablePartial partialint fieldIndexint[] valuesint valueToAdd) {
             // overridden as superclass algorithm can't handle
             // 2004-02-29 + 48 months -> 2008-02-29 type dates
             if (valueToAdd == 0) {
                 return values;
             }
             if (DateTimeUtils.isContiguous(partial)) {
                 long instant = 0L;
                 for (int i = 0, isize = partial.size(); i < isizei++) {
                     instant = partial.getFieldType(i).getField(GJChronology.this).set(instantvalues[i]);
                 }
                 instant = add(instantvalueToAdd);
                 return GJChronology.this.get(partialinstant);
             } else {
                 return super.add(partialfieldIndexvaluesvalueToAdd);
             }
         }
 
         public int getDifference(long minuendInstantlong subtrahendInstant) {
             return .getDifference(minuendInstantsubtrahendInstant);
         }
 
         public long getDifferenceAsLong(long minuendInstantlong subtrahendInstant) {
             return .getDifferenceAsLong(minuendInstantsubtrahendInstant);
         }
 
         public long set(long instantint value) {
             if (instant >= ) {
                 instant = .set(instantvalue);
                 if (instant < ) {
                     // Only adjust if gap fully crossed.
                     if (instant +  < ) {
                         instant = gregorianToJulian(instant);
                     }
                     // Verify that new value stuck.
                     if (get(instant) != value) {
                         throw new IllegalFieldValueException
                             (.getType(), new Integer(value), nullnull);
                     }
                 }
             } else {
                 instant = .set(instantvalue);
                 if (instant >= ) {
                     // Only adjust if gap fully crossed.
                     if (instant -  >= ) {
                         instant = julianToGregorian(instant);
                     }
                     // Verify that new value stuck.
                     if (get(instant) != value) {
                        throw new IllegalFieldValueException
                             (.getType(), new Integer(value), nullnull);
                     }
                 }
             }
             return instant;
         }
 
         public long set(long instantString textLocale locale) {
             if (instant >= ) {
                 instant = .set(instanttextlocale);
                 if (instant < ) {
                     // Only adjust if gap fully crossed.
                     if (instant +  < ) {
                         instant = gregorianToJulian(instant);
                     }
                     // Cannot verify that new value stuck because set may be lenient.
                 }
             } else {
                 instant = .set(instanttextlocale);
                 if (instant >= ) {
                     // Only adjust if gap fully crossed.
                     if (instant -  >= ) {
                         instant = julianToGregorian(instant);
                     }
                     // Cannot verify that new value stuck because set may be lenient.
                 }
             }
             return instant;
         }
 
         public DurationField getDurationField() {
             return ;
         }
 
         public DurationField getRangeDurationField() {
             return ;
         }
 
         public boolean isLeap(long instant) {
             if (instant >= ) {
                 return .isLeap(instant);
             } else {
                 return .isLeap(instant);
             }
         }
 
         public int getLeapAmount(long instant) {
             if (instant >= ) {
                 return .getLeapAmount(instant);
             } else {
                 return .getLeapAmount(instant);
             }
         }
 
         public DurationField getLeapDurationField() {
             return .getLeapDurationField();
         }
 
 
         public int getMinimumValue() {
             // For all precise fields, the Julian and Gregorian limits are
             // identical. Choose Julian to tighten up the year limits.
             return .getMinimumValue();
         }
 
         public int getMinimumValue(ReadablePartial partial) {
             return .getMinimumValue(partial);
         }
 
         public int getMinimumValue(ReadablePartial partialint[] values) {
             return .getMinimumValue(partialvalues);
         }
 
         public int getMinimumValue(long instant) {
             if (instant < ) {
                 return .getMinimumValue(instant);
             }
 
             int min = .getMinimumValue(instant);
 
             // Because the cutover may reduce the length of this field, verify
             // the minimum by setting it.
             instant = .set(instantmin);
             if (instant < ) {
                 min = .get();
             }
 
             return min;
         }
 
         public int getMaximumValue() {
             // For all precise fields, the Julian and Gregorian limits are
             // identical.
             return .getMaximumValue();
         }
 
         public int getMaximumValue(long instant) {
             if (instant >= ) {
                 return .getMaximumValue(instant);
             }
 
             int max = .getMaximumValue(instant);
 
             // Because the cutover may reduce the length of this field, verify
             // the maximum by setting it.
             instant = .set(instantmax);
             if (instant >= ) {
                 max = .get(.add(, -1));
             }
 
             return max;
         }
 
         public int getMaximumValue(ReadablePartial partial) {
             long instant = GJChronology.getInstanceUTC().set(partial, 0L);
             return getMaximumValue(instant);
         }
 
         public int getMaximumValue(ReadablePartial partialint[] values) {
             Chronology chrono = GJChronology.getInstanceUTC();
             long instant = 0L;
             for (int i = 0, isize = partial.size(); i < isizei++) {
                 DateTimeField field = partial.getFieldType(i).getField(chrono);
                 if (values[i] <= field.getMaximumValue(instant)) {
                     instant = field.set(instantvalues[i]);
                 }
             }
             return getMaximumValue(instant);
         }
 
         public long roundFloor(long instant) {
             if (instant >= ) {
                 instant = .roundFloor(instant);
                 if (instant < ) {
                     // Only adjust if gap fully crossed.
                     if (instant +  < ) {
                         instant = gregorianToJulian(instant);
                     }
                 }
             } else {
                 instant = .roundFloor(instant);
             }
             return instant;
         }
 
         public long roundCeiling(long instant) {
             if (instant >= ) {
                 instant = .roundCeiling(instant);
             } else {
                 instant = .roundCeiling(instant);
                 if (instant >= ) {
                     // Only adjust if gap fully crossed.
                     if (instant -  >= ) {
                         instant = julianToGregorian(instant);
                     }
                 }
             }
             return instant;
         }
 
         public int getMaximumTextLength(Locale locale) {
             return Math.max(.getMaximumTextLength(locale),
                             .getMaximumTextLength(locale));
         }
 
         public int getMaximumShortTextLength(Locale locale) {
             return Math.max(.getMaximumShortTextLength(locale),
                             .getMaximumShortTextLength(locale));
         }
 
         protected long julianToGregorian(long instant) {
             if () {
                 return julianToGregorianByWeekyear(instant);
             } else {
                 return julianToGregorianByYear(instant);
             }
         }
 
         protected long gregorianToJulian(long instant) {
             if () {
                 return gregorianToJulianByWeekyear(instant);
             } else {
                 return gregorianToJulianByYear(instant);
             }
         }
     }
 
     //-----------------------------------------------------------------------
     
Cutover field for variable length fields. These fields internally call set whenever add is called. As a result, the same correction applied to set must be applied to add and addWrapField. Knowing when to use this field requires specific knowledge of how the GJ fields are implemented.
 
     private final class ImpreciseCutoverField extends CutoverField {
         private static final long serialVersionUID = 3410248757173576441L;

        
Creates a duration field that links back to this.
 
         ImpreciseCutoverField(DateTimeField julianFieldDateTimeField gregorianFieldlong cutoverMillis) {
             this(julianFieldgregorianFieldnullcutoverMillisfalse);
         }

        
Uses a shared duration field rather than creating a new one.

Parameters:
durationField shared duration field
 
         ImpreciseCutoverField(DateTimeField julianFieldDateTimeField gregorianField,
                               DurationField durationFieldlong cutoverMillis)
         {
             this(julianFieldgregorianFielddurationFieldcutoverMillisfalse);
         }

        
Uses a shared duration field rather than creating a new one.

Parameters:
durationField shared duration field
 
         ImpreciseCutoverField(DateTimeField julianFieldDateTimeField gregorianField,
                               DurationField durationField,
                               long cutoverMillisboolean convertByWeekyear)
         {
             super(julianFieldgregorianFieldcutoverMillisconvertByWeekyear);
             if (durationField == null) {
                 durationField = new LinkedDurationField(this);
             }
              = durationField;
         }
 
         public long add(long instantint value) {
             if (instant >= ) {
                 instant = .add(instantvalue);
                 if (instant < ) {
                     // Only adjust if gap fully crossed.
                     if (instant +  < ) {
                         instant = gregorianToJulian(instant);
                     }
                 }
             } else {
                 instant = .add(instantvalue);
                 if (instant >= ) {
                     // Only adjust if gap fully crossed.
                     if (instant -  >= ) {
                         instant = julianToGregorian(instant);
                     }
                 }
             }
             return instant;
         }
         
         public long add(long instantlong value) {
             if (instant >= ) {
                 instant = .add(instantvalue);
                 if (instant < ) {
                     // Only adjust if gap fully crossed.
                     if (instant +  < ) {
                         instant = gregorianToJulian(instant);
                     }
                 }
             } else {
                 instant = .add(instantvalue);
                 if (instant >= ) {
                     // Only adjust if gap fully crossed.
                     if (instant -  >= ) {
                         instant = julianToGregorian(instant);
                     }
                 }
             }
             return instant;
         }
 
         public int getDifference(long minuendInstantlong subtrahendInstant) {
             if (minuendInstant >= ) {
                 if (subtrahendInstant >= ) {
                     return .getDifference(minuendInstantsubtrahendInstant);
                 }
                 // Remember, the add is being reversed. Since subtrahend is
                 // Julian, convert minuend to Julian to match.
                minuendInstant = gregorianToJulian(minuendInstant);
                return .getDifference(minuendInstantsubtrahendInstant);
            } else {
                if (subtrahendInstant < ) {
                    return .getDifference(minuendInstantsubtrahendInstant);
                }
                // Remember, the add is being reversed. Since subtrahend is
                // Gregorian, convert minuend to Gregorian to match.
                minuendInstant = julianToGregorian(minuendInstant);
                return .getDifference(minuendInstantsubtrahendInstant);
            }
        }
        public long getDifferenceAsLong(long minuendInstantlong subtrahendInstant) {
            if (minuendInstant >= ) {
                if (subtrahendInstant >= ) {
                    return .getDifferenceAsLong(minuendInstantsubtrahendInstant);
                }
                // Remember, the add is being reversed. Since subtrahend is
                // Julian, convert minuend to Julian to match.
                minuendInstant = gregorianToJulian(minuendInstant);
                return .getDifferenceAsLong(minuendInstantsubtrahendInstant);
            } else {
                if (subtrahendInstant < ) {
                    return .getDifferenceAsLong(minuendInstantsubtrahendInstant);
                }
                // Remember, the add is being reversed. Since subtrahend is
                // Gregorian, convert minuend to Gregorian to match.
                minuendInstant = julianToGregorian(minuendInstant);
                return .getDifferenceAsLong(minuendInstantsubtrahendInstant);
            }
        }
        // Since the imprecise fields have durations longer than the gap
        // duration, keep these methods simple. The inherited implementations
        // produce incorrect results.
        //
        // Degenerate case: If this field is a month, and the cutover is set
        // far into the future, then the gap duration may be so large as to
        // reduce the number of months in a year. If the missing month(s) are
        // at the beginning or end of the year, then the minimum and maximum
        // values are not 1 and 12. I don't expect this case to ever occur.
        public int getMinimumValue(long instant) {
            if (instant >= ) {
                return .getMinimumValue(instant);
            } else {
                return .getMinimumValue(instant);
            }
        }
        public int getMaximumValue(long instant) {
            if (instant >= ) {
                return .getMaximumValue(instant);
            } else {
                return .getMaximumValue(instant);
            }
        }
    }
    //-----------------------------------------------------------------------
    
Links the duration back to a ImpreciseCutoverField.
    private static class LinkedDurationField extends DecoratedDurationField {
        private static final long serialVersionUID = 4097975388007713084L;
        private final ImpreciseCutoverField iField;
        LinkedDurationField(DurationField durationFieldImpreciseCutoverField dateTimeField) {
            super(durationFielddurationField.getType());
             = dateTimeField;
        }
        public long add(long instantint value) {
            return .add(instantvalue);
        }
        public long add(long instantlong value) {
            return .add(instantvalue);
        }
        public int getDifference(long minuendInstantlong subtrahendInstant) {
            return .getDifference(minuendInstantsubtrahendInstant);
        }
        public long getDifferenceAsLong(long minuendInstantlong subtrahendInstant) {
            return .getDifferenceAsLong(minuendInstantsubtrahendInstant);
        }
    }