0001: /*
0002: * Copyright 2001-2005 Stephen Colebourne
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.joda.time.chrono;
0017:
0018: import java.util.ArrayList;
0019: import java.util.HashMap;
0020: import java.util.Locale;
0021: import java.util.Map;
0022:
0023: import org.joda.time.Chronology;
0024: import org.joda.time.DateTimeField;
0025: import org.joda.time.DateTimeUtils;
0026: import org.joda.time.DateTimeZone;
0027: import org.joda.time.DurationField;
0028: import org.joda.time.IllegalFieldValueException;
0029: import org.joda.time.Instant;
0030: import org.joda.time.ReadableInstant;
0031: import org.joda.time.ReadablePartial;
0032: import org.joda.time.field.BaseDateTimeField;
0033: import org.joda.time.field.DecoratedDurationField;
0034: import org.joda.time.format.DateTimeFormatter;
0035: import org.joda.time.format.ISODateTimeFormat;
0036:
0037: /**
0038: * Implements the Gregorian/Julian calendar system which is the calendar system
0039: * used in most of the world. Wherever possible, it is recommended to use the
0040: * {@link ISOChronology} instead.
0041: * <p>
0042: * The Gregorian calendar replaced the Julian calendar, and the point in time
0043: * when this chronology switches can be controlled using the second parameter
0044: * of the getInstance method. By default this cutover is set to the date the
0045: * Gregorian calendar was first instituted, October 15, 1582.
0046: * <p>
0047: * Before this date, this chronology uses the proleptic Julian calendar
0048: * (proleptic means extending indefinitely). The Julian calendar has leap years
0049: * every four years, whereas the Gregorian has special rules for 100 and 400
0050: * years. A meaningful result will thus be obtained for all input values.
0051: * However before 8 CE, Julian leap years were irregular, and before 45 BCE
0052: * there was no Julian calendar.
0053: * <p>
0054: * This chronology differs from
0055: * {@link java.util.GregorianCalendar GregorianCalendar} in that years
0056: * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1.
0057: * The yearOfEra field produces results compatible with GregorianCalendar.
0058: * <p>
0059: * The Julian calendar does not have a year zero, and so year -1 is followed by
0060: * year 1. If the Gregorian cutover date is specified at or before year -1
0061: * (Julian), year zero is defined. In other words, the proleptic Gregorian
0062: * chronology used by this class has a year zero.
0063: * <p>
0064: * To create a pure proleptic Julian chronology, use {@link JulianChronology},
0065: * and to create a pure proleptic Gregorian chronology, use
0066: * {@link GregorianChronology}.
0067: * <p>
0068: * GJChronology is thread-safe and immutable.
0069: *
0070: * @author Brian S O'Neill
0071: * @author Stephen Colebourne
0072: * @since 1.0
0073: */
0074: public final class GJChronology extends AssembledChronology {
0075:
0076: /** Serialization lock */
0077: private static final long serialVersionUID = -2545574827706931671L;
0078:
0079: /**
0080: * Convert a datetime from one chronology to another.
0081: */
0082: private static long convertByYear(long instant, Chronology from,
0083: Chronology to) {
0084: return to.getDateTimeMillis(from.year().get(instant), from
0085: .monthOfYear().get(instant), from.dayOfMonth().get(
0086: instant), from.millisOfDay().get(instant));
0087: }
0088:
0089: /**
0090: * Convert a datetime from one chronology to another.
0091: */
0092: private static long convertByWeekyear(final long instant,
0093: Chronology from, Chronology to) {
0094: long newInstant;
0095: newInstant = to.weekyear().set(0, from.weekyear().get(instant));
0096: newInstant = to.weekOfWeekyear().set(newInstant,
0097: from.weekOfWeekyear().get(instant));
0098: newInstant = to.dayOfWeek().set(newInstant,
0099: from.dayOfWeek().get(instant));
0100: newInstant = to.millisOfDay().set(newInstant,
0101: from.millisOfDay().get(instant));
0102: return newInstant;
0103: }
0104:
0105: /**
0106: * The default GregorianJulian cutover point.
0107: */
0108: static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L);
0109:
0110: /** Cache of zone to chronology list */
0111: private static final Map cCache = new HashMap();
0112:
0113: /**
0114: * Factory method returns instances of the default GJ cutover
0115: * chronology. This uses a cutover date of October 15, 1582 (Gregorian)
0116: * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by
0117: * October 15, 1582 (Gregorian).
0118: *
0119: * <p>The first day of the week is designated to be
0120: * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
0121: * and the minimum days in the first week of the year is 4.
0122: *
0123: * <p>The time zone of the returned instance is UTC.
0124: */
0125: public static GJChronology getInstanceUTC() {
0126: return getInstance(DateTimeZone.UTC, DEFAULT_CUTOVER, 4);
0127: }
0128:
0129: /**
0130: * Factory method returns instances of the default GJ cutover
0131: * chronology. This uses a cutover date of October 15, 1582 (Gregorian)
0132: * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by
0133: * October 15, 1582 (Gregorian).
0134: *
0135: * <p>The first day of the week is designated to be
0136: * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
0137: * and the minimum days in the first week of the year is 4.
0138: *
0139: * <p>The returned chronology is in the default time zone.
0140: */
0141: public static GJChronology getInstance() {
0142: return getInstance(DateTimeZone.getDefault(), DEFAULT_CUTOVER,
0143: 4);
0144: }
0145:
0146: /**
0147: * Factory method returns instances of the GJ cutover chronology. This uses
0148: * a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this
0149: * value, October 4, 1582 (Julian) is followed by October 15, 1582
0150: * (Gregorian).
0151: *
0152: * <p>The first day of the week is designated to be
0153: * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
0154: * and the minimum days in the first week of the year is 4.
0155: *
0156: * @param zone the time zone to use, null is default
0157: */
0158: public static GJChronology getInstance(DateTimeZone zone) {
0159: return getInstance(zone, DEFAULT_CUTOVER, 4);
0160: }
0161:
0162: /**
0163: * Factory method returns instances of the GJ cutover chronology. Any
0164: * cutover date may be specified.
0165: *
0166: * <p>The first day of the week is designated to be
0167: * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
0168: * and the minimum days in the first week of the year is 4.
0169: *
0170: * @param zone the time zone to use, null is default
0171: * @param gregorianCutover the cutover to use, null means default
0172: */
0173: public static GJChronology getInstance(DateTimeZone zone,
0174: ReadableInstant gregorianCutover) {
0175:
0176: return getInstance(zone, gregorianCutover, 4);
0177: }
0178:
0179: /**
0180: * Factory method returns instances of the GJ cutover chronology. Any
0181: * cutover date may be specified.
0182: *
0183: * @param zone the time zone to use, null is default
0184: * @param gregorianCutover the cutover to use, null means default
0185: * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4
0186: */
0187: public static synchronized GJChronology getInstance(
0188: DateTimeZone zone, ReadableInstant gregorianCutover,
0189: int minDaysInFirstWeek) {
0190:
0191: zone = DateTimeUtils.getZone(zone);
0192: Instant cutoverInstant;
0193: if (gregorianCutover == null) {
0194: cutoverInstant = DEFAULT_CUTOVER;
0195: } else {
0196: cutoverInstant = gregorianCutover.toInstant();
0197: }
0198:
0199: GJChronology chrono;
0200:
0201: ArrayList chronos = (ArrayList) cCache.get(zone);
0202: if (chronos == null) {
0203: chronos = new ArrayList(2);
0204: cCache.put(zone, chronos);
0205: } else {
0206: for (int i = chronos.size(); --i >= 0;) {
0207: chrono = (GJChronology) chronos.get(i);
0208: if (minDaysInFirstWeek == chrono
0209: .getMinimumDaysInFirstWeek()
0210: && cutoverInstant.equals(chrono
0211: .getGregorianCutover())) {
0212:
0213: return chrono;
0214: }
0215: }
0216: }
0217:
0218: if (zone == DateTimeZone.UTC) {
0219: chrono = new GJChronology(JulianChronology.getInstance(
0220: zone, minDaysInFirstWeek), GregorianChronology
0221: .getInstance(zone, minDaysInFirstWeek),
0222: cutoverInstant);
0223: } else {
0224: chrono = getInstance(DateTimeZone.UTC, cutoverInstant,
0225: minDaysInFirstWeek);
0226: chrono = new GJChronology(ZonedChronology.getInstance(
0227: chrono, zone), chrono.iJulianChronology,
0228: chrono.iGregorianChronology, chrono.iCutoverInstant);
0229: }
0230:
0231: chronos.add(chrono);
0232:
0233: return chrono;
0234: }
0235:
0236: /**
0237: * Factory method returns instances of the GJ cutover chronology. Any
0238: * cutover date may be specified.
0239: *
0240: * @param zone the time zone to use, null is default
0241: * @param gregorianCutover the cutover to use
0242: * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4
0243: */
0244: public static GJChronology getInstance(DateTimeZone zone,
0245: long gregorianCutover, int minDaysInFirstWeek) {
0246:
0247: Instant cutoverInstant;
0248: if (gregorianCutover == DEFAULT_CUTOVER.getMillis()) {
0249: cutoverInstant = null;
0250: } else {
0251: cutoverInstant = new Instant(gregorianCutover);
0252: }
0253: return getInstance(zone, cutoverInstant, minDaysInFirstWeek);
0254: }
0255:
0256: //-----------------------------------------------------------------------
0257: private JulianChronology iJulianChronology;
0258: private GregorianChronology iGregorianChronology;
0259: private Instant iCutoverInstant;
0260:
0261: private long iCutoverMillis;
0262: private long iGapDuration;
0263:
0264: /**
0265: * @param julian chronology used before the cutover instant
0266: * @param gregorian chronology used at and after the cutover instant
0267: * @param cutoverInstant instant when the gregorian chronology began
0268: */
0269: private GJChronology(JulianChronology julian,
0270: GregorianChronology gregorian, Instant cutoverInstant) {
0271: super (null, new Object[] { julian, gregorian, cutoverInstant });
0272: }
0273:
0274: /**
0275: * Called when applying a time zone.
0276: */
0277: private GJChronology(Chronology base, JulianChronology julian,
0278: GregorianChronology gregorian, Instant cutoverInstant) {
0279: super (base, new Object[] { julian, gregorian, cutoverInstant });
0280: }
0281:
0282: /**
0283: * Serialization singleton
0284: */
0285: private Object readResolve() {
0286: return getInstance(getZone(), iCutoverInstant,
0287: getMinimumDaysInFirstWeek());
0288: }
0289:
0290: public DateTimeZone getZone() {
0291: Chronology base;
0292: if ((base = getBase()) != null) {
0293: return base.getZone();
0294: }
0295: return DateTimeZone.UTC;
0296: }
0297:
0298: // Conversion
0299: //-----------------------------------------------------------------------
0300: /**
0301: * Gets the Chronology in the UTC time zone.
0302: *
0303: * @return the chronology in UTC
0304: */
0305: public Chronology withUTC() {
0306: return withZone(DateTimeZone.UTC);
0307: }
0308:
0309: /**
0310: * Gets the Chronology in a specific time zone.
0311: *
0312: * @param zone the zone to get the chronology in, null is default
0313: * @return the chronology
0314: */
0315: public Chronology withZone(DateTimeZone zone) {
0316: if (zone == null) {
0317: zone = DateTimeZone.getDefault();
0318: }
0319: if (zone == getZone()) {
0320: return this ;
0321: }
0322: return getInstance(zone, iCutoverInstant,
0323: getMinimumDaysInFirstWeek());
0324: }
0325:
0326: public long getDateTimeMillis(int year, int monthOfYear,
0327: int dayOfMonth, int millisOfDay)
0328: throws IllegalArgumentException {
0329: Chronology base;
0330: if ((base = getBase()) != null) {
0331: return base.getDateTimeMillis(year, monthOfYear,
0332: dayOfMonth, millisOfDay);
0333: }
0334:
0335: // Assume date is Gregorian.
0336: long instant = iGregorianChronology.getDateTimeMillis(year,
0337: monthOfYear, dayOfMonth, millisOfDay);
0338: if (instant < iCutoverMillis) {
0339: // Maybe it's Julian.
0340: instant = iJulianChronology.getDateTimeMillis(year,
0341: monthOfYear, dayOfMonth, millisOfDay);
0342: if (instant >= iCutoverMillis) {
0343: // Okay, it's in the illegal cutover gap.
0344: throw new IllegalArgumentException(
0345: "Specified date does not exist");
0346: }
0347: }
0348: return instant;
0349: }
0350:
0351: public long getDateTimeMillis(int year, int monthOfYear,
0352: int dayOfMonth, int hourOfDay, int minuteOfHour,
0353: int secondOfMinute, int millisOfSecond)
0354: throws IllegalArgumentException {
0355: Chronology base;
0356: if ((base = getBase()) != null) {
0357: return base.getDateTimeMillis(year, monthOfYear,
0358: dayOfMonth, hourOfDay, minuteOfHour,
0359: secondOfMinute, millisOfSecond);
0360: }
0361:
0362: // Assume date is Gregorian.
0363: long instant = iGregorianChronology.getDateTimeMillis(year,
0364: monthOfYear, dayOfMonth, hourOfDay, minuteOfHour,
0365: secondOfMinute, millisOfSecond);
0366: if (instant < iCutoverMillis) {
0367: // Maybe it's Julian.
0368: instant = iJulianChronology.getDateTimeMillis(year,
0369: monthOfYear, dayOfMonth, hourOfDay, minuteOfHour,
0370: secondOfMinute, millisOfSecond);
0371: if (instant >= iCutoverMillis) {
0372: // Okay, it's in the illegal cutover gap.
0373: throw new IllegalArgumentException(
0374: "Specified date does not exist");
0375: }
0376: }
0377: return instant;
0378: }
0379:
0380: /**
0381: * Gets the cutover instant between Gregorian and Julian chronologies.
0382: * @return the cutover instant
0383: */
0384: public Instant getGregorianCutover() {
0385: return iCutoverInstant;
0386: }
0387:
0388: /**
0389: * Gets the minimum days needed for a week to be the first week in a year.
0390: *
0391: * @return the minimum days
0392: */
0393: public int getMinimumDaysInFirstWeek() {
0394: return iGregorianChronology.getMinimumDaysInFirstWeek();
0395: }
0396:
0397: // Output
0398: //-----------------------------------------------------------------------
0399: /**
0400: * Gets a debugging toString.
0401: *
0402: * @return a debugging string
0403: */
0404: public String toString() {
0405: StringBuffer sb = new StringBuffer(60);
0406: sb.append("GJChronology");
0407: sb.append('[');
0408: sb.append(getZone().getID());
0409:
0410: if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) {
0411: sb.append(",cutover=");
0412: DateTimeFormatter printer;
0413: if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) {
0414: printer = ISODateTimeFormat.date();
0415: } else {
0416: printer = ISODateTimeFormat.dateTime();
0417: }
0418: printer.withChronology(withUTC()).printTo(sb,
0419: iCutoverMillis);
0420: }
0421:
0422: if (getMinimumDaysInFirstWeek() != 4) {
0423: sb.append(",mdfw=");
0424: sb.append(getMinimumDaysInFirstWeek());
0425: }
0426: sb.append(']');
0427:
0428: return sb.toString();
0429: }
0430:
0431: protected void assemble(Fields fields) {
0432: Object[] params = (Object[]) getParam();
0433:
0434: JulianChronology julian = (JulianChronology) params[0];
0435: GregorianChronology gregorian = (GregorianChronology) params[1];
0436: Instant cutoverInstant = (Instant) params[2];
0437: iCutoverMillis = cutoverInstant.getMillis();
0438:
0439: iJulianChronology = julian;
0440: iGregorianChronology = gregorian;
0441: iCutoverInstant = cutoverInstant;
0442:
0443: if (getBase() != null) {
0444: return;
0445: }
0446:
0447: if (julian.getMinimumDaysInFirstWeek() != gregorian
0448: .getMinimumDaysInFirstWeek()) {
0449: throw new IllegalArgumentException();
0450: }
0451:
0452: // Compute difference between the chronologies at the cutover instant
0453: iGapDuration = iCutoverMillis
0454: - julianToGregorianByYear(iCutoverMillis);
0455:
0456: // Begin field definitions.
0457:
0458: // First just copy all the Gregorian fields and then override those
0459: // that need special attention.
0460: fields.copyFieldsFrom(gregorian);
0461:
0462: // Assuming cutover is at midnight, all time of day fields can be
0463: // gregorian since they are unaffected by cutover.
0464:
0465: // Verify assumption.
0466: if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
0467: // Cutover is sometime in the day, so cutover fields are required
0468: // for time of day.
0469:
0470: fields.millisOfSecond = new CutoverField(julian
0471: .millisOfSecond(), fields.millisOfSecond,
0472: iCutoverMillis);
0473: fields.millisOfDay = new CutoverField(julian.millisOfDay(),
0474: fields.millisOfDay, iCutoverMillis);
0475: fields.secondOfMinute = new CutoverField(julian
0476: .secondOfMinute(), fields.secondOfMinute,
0477: iCutoverMillis);
0478: fields.secondOfDay = new CutoverField(julian.secondOfDay(),
0479: fields.secondOfDay, iCutoverMillis);
0480: fields.minuteOfHour = new CutoverField(julian
0481: .minuteOfHour(), fields.minuteOfHour,
0482: iCutoverMillis);
0483: fields.minuteOfDay = new CutoverField(julian.minuteOfDay(),
0484: fields.minuteOfDay, iCutoverMillis);
0485: fields.hourOfDay = new CutoverField(julian.hourOfDay(),
0486: fields.hourOfDay, iCutoverMillis);
0487: fields.hourOfHalfday = new CutoverField(julian
0488: .hourOfHalfday(), fields.hourOfHalfday,
0489: iCutoverMillis);
0490: fields.clockhourOfDay = new CutoverField(julian
0491: .clockhourOfDay(), fields.clockhourOfDay,
0492: iCutoverMillis);
0493: fields.clockhourOfHalfday = new CutoverField(julian
0494: .clockhourOfHalfday(), fields.clockhourOfHalfday,
0495: iCutoverMillis);
0496: fields.halfdayOfDay = new CutoverField(julian
0497: .halfdayOfDay(), fields.halfdayOfDay,
0498: iCutoverMillis);
0499: }
0500:
0501: // These fields just require basic cutover support.
0502: {
0503: fields.era = new CutoverField(julian.era(), fields.era,
0504: iCutoverMillis);
0505: }
0506:
0507: // DayOfYear and weekOfWeekyear require special handling since cutover
0508: // year has fewer days and weeks. Extend the cutover to the start of
0509: // the next year or weekyear. This keeps the sequence unbroken during
0510: // the cutover year.
0511:
0512: {
0513: long cutover = gregorian.year()
0514: .roundCeiling(iCutoverMillis);
0515: fields.dayOfYear = new CutoverField(julian.dayOfYear(),
0516: fields.dayOfYear, cutover);
0517: }
0518:
0519: {
0520: long cutover = gregorian.weekyear().roundCeiling(
0521: iCutoverMillis);
0522: fields.weekOfWeekyear = new CutoverField(julian
0523: .weekOfWeekyear(), fields.weekOfWeekyear, cutover,
0524: true);
0525: }
0526:
0527: // These fields are special because they have imprecise durations. The
0528: // family of addition methods need special attention. Override affected
0529: // duration fields as well.
0530: {
0531: fields.year = new ImpreciseCutoverField(julian.year(),
0532: fields.year, iCutoverMillis);
0533: fields.years = fields.year.getDurationField();
0534: fields.yearOfEra = new ImpreciseCutoverField(julian
0535: .yearOfEra(), fields.yearOfEra, fields.years,
0536: iCutoverMillis);
0537: fields.yearOfCentury = new ImpreciseCutoverField(julian
0538: .yearOfCentury(), fields.yearOfCentury,
0539: fields.years, iCutoverMillis);
0540:
0541: fields.centuryOfEra = new ImpreciseCutoverField(julian
0542: .centuryOfEra(), fields.centuryOfEra,
0543: iCutoverMillis);
0544: fields.centuries = fields.centuryOfEra.getDurationField();
0545:
0546: fields.monthOfYear = new ImpreciseCutoverField(julian
0547: .monthOfYear(), fields.monthOfYear, iCutoverMillis);
0548: fields.months = fields.monthOfYear.getDurationField();
0549:
0550: fields.weekyear = new ImpreciseCutoverField(julian
0551: .weekyear(), fields.weekyear, null, iCutoverMillis,
0552: true);
0553: fields.weekyearOfCentury = new ImpreciseCutoverField(julian
0554: .weekyearOfCentury(), fields.weekyearOfCentury,
0555: fields.weekyears, iCutoverMillis);
0556: fields.weekyears = fields.weekyear.getDurationField();
0557: }
0558:
0559: // These fields require basic cutover support, except they must link to
0560: // imprecise durations.
0561: {
0562: CutoverField cf = new CutoverField(julian.dayOfMonth(),
0563: fields.dayOfMonth, iCutoverMillis);
0564: cf.iRangeDurationField = fields.months;
0565: fields.dayOfMonth = cf;
0566: }
0567: }
0568:
0569: long julianToGregorianByYear(long instant) {
0570: return convertByYear(instant, iJulianChronology,
0571: iGregorianChronology);
0572: }
0573:
0574: long gregorianToJulianByYear(long instant) {
0575: return convertByYear(instant, iGregorianChronology,
0576: iJulianChronology);
0577: }
0578:
0579: long julianToGregorianByWeekyear(long instant) {
0580: return convertByWeekyear(instant, iJulianChronology,
0581: iGregorianChronology);
0582: }
0583:
0584: long gregorianToJulianByWeekyear(long instant) {
0585: return convertByWeekyear(instant, iGregorianChronology,
0586: iJulianChronology);
0587: }
0588:
0589: //-----------------------------------------------------------------------
0590: /**
0591: * This basic cutover field adjusts calls to 'get' and 'set' methods, and
0592: * assumes that calls to add and addWrapField are unaffected by the cutover.
0593: */
0594: private class CutoverField extends BaseDateTimeField {
0595: private static final long serialVersionUID = 3528501219481026402L;
0596:
0597: final DateTimeField iJulianField;
0598: final DateTimeField iGregorianField;
0599: final long iCutover;
0600: final boolean iConvertByWeekyear;
0601:
0602: protected DurationField iDurationField;
0603: protected DurationField iRangeDurationField;
0604:
0605: /**
0606: * @param julianField field from the chronology used before the cutover instant
0607: * @param gregorianField field from the chronology used at and after the cutover
0608: * @param cutoverMillis the millis of the cutover
0609: */
0610: CutoverField(DateTimeField julianField,
0611: DateTimeField gregorianField, long cutoverMillis) {
0612: this (julianField, gregorianField, cutoverMillis, false);
0613: }
0614:
0615: /**
0616: * @param julianField field from the chronology used before the cutover instant
0617: * @param gregorianField field from the chronology used at and after the cutover
0618: * @param cutoverMillis the millis of the cutover
0619: * @param convertByWeekyear
0620: */
0621: CutoverField(DateTimeField julianField,
0622: DateTimeField gregorianField, long cutoverMillis,
0623: boolean convertByWeekyear) {
0624: super (gregorianField.getType());
0625: iJulianField = julianField;
0626: iGregorianField = gregorianField;
0627: iCutover = cutoverMillis;
0628: iConvertByWeekyear = convertByWeekyear;
0629: // Although average length of Julian and Gregorian years differ,
0630: // use the Gregorian duration field because it is more accurate.
0631: iDurationField = gregorianField.getDurationField();
0632:
0633: DurationField rangeField = gregorianField
0634: .getRangeDurationField();
0635: if (rangeField == null) {
0636: rangeField = julianField.getRangeDurationField();
0637: }
0638: iRangeDurationField = rangeField;
0639: }
0640:
0641: public boolean isLenient() {
0642: return false;
0643: }
0644:
0645: public int get(long instant) {
0646: if (instant >= iCutover) {
0647: return iGregorianField.get(instant);
0648: } else {
0649: return iJulianField.get(instant);
0650: }
0651: }
0652:
0653: public String getAsText(long instant, Locale locale) {
0654: if (instant >= iCutover) {
0655: return iGregorianField.getAsText(instant, locale);
0656: } else {
0657: return iJulianField.getAsText(instant, locale);
0658: }
0659: }
0660:
0661: public String getAsText(int fieldValue, Locale locale) {
0662: return iGregorianField.getAsText(fieldValue, locale);
0663: }
0664:
0665: public String getAsShortText(long instant, Locale locale) {
0666: if (instant >= iCutover) {
0667: return iGregorianField.getAsShortText(instant, locale);
0668: } else {
0669: return iJulianField.getAsShortText(instant, locale);
0670: }
0671: }
0672:
0673: public String getAsShortText(int fieldValue, Locale locale) {
0674: return iGregorianField.getAsShortText(fieldValue, locale);
0675: }
0676:
0677: public long add(long instant, int value) {
0678: return iGregorianField.add(instant, value);
0679: }
0680:
0681: public long add(long instant, long value) {
0682: return iGregorianField.add(instant, value);
0683: }
0684:
0685: public int[] add(ReadablePartial partial, int fieldIndex,
0686: int[] values, int valueToAdd) {
0687: // overridden as superclass algorithm can't handle
0688: // 2004-02-29 + 48 months -> 2008-02-29 type dates
0689: if (valueToAdd == 0) {
0690: return values;
0691: }
0692: if (DateTimeUtils.isContiguous(partial)) {
0693: long instant = 0L;
0694: for (int i = 0, isize = partial.size(); i < isize; i++) {
0695: instant = partial.getFieldType(i).getField(
0696: GJChronology.this ).set(instant, values[i]);
0697: }
0698: instant = add(instant, valueToAdd);
0699: return GJChronology.this .get(partial, instant);
0700: } else {
0701: return super .add(partial, fieldIndex, values,
0702: valueToAdd);
0703: }
0704: }
0705:
0706: public int getDifference(long minuendInstant,
0707: long subtrahendInstant) {
0708: return iGregorianField.getDifference(minuendInstant,
0709: subtrahendInstant);
0710: }
0711:
0712: public long getDifferenceAsLong(long minuendInstant,
0713: long subtrahendInstant) {
0714: return iGregorianField.getDifferenceAsLong(minuendInstant,
0715: subtrahendInstant);
0716: }
0717:
0718: public long set(long instant, int value) {
0719: if (instant >= iCutover) {
0720: instant = iGregorianField.set(instant, value);
0721: if (instant < iCutover) {
0722: // Only adjust if gap fully crossed.
0723: if (instant + iGapDuration < iCutover) {
0724: instant = gregorianToJulian(instant);
0725: }
0726: // Verify that new value stuck.
0727: if (get(instant) != value) {
0728: throw new IllegalFieldValueException(
0729: iGregorianField.getType(), new Integer(
0730: value), null, null);
0731: }
0732: }
0733: } else {
0734: instant = iJulianField.set(instant, value);
0735: if (instant >= iCutover) {
0736: // Only adjust if gap fully crossed.
0737: if (instant - iGapDuration >= iCutover) {
0738: instant = julianToGregorian(instant);
0739: }
0740: // Verify that new value stuck.
0741: if (get(instant) != value) {
0742: throw new IllegalFieldValueException(
0743: iJulianField.getType(), new Integer(
0744: value), null, null);
0745: }
0746: }
0747: }
0748: return instant;
0749: }
0750:
0751: public long set(long instant, String text, Locale locale) {
0752: if (instant >= iCutover) {
0753: instant = iGregorianField.set(instant, text, locale);
0754: if (instant < iCutover) {
0755: // Only adjust if gap fully crossed.
0756: if (instant + iGapDuration < iCutover) {
0757: instant = gregorianToJulian(instant);
0758: }
0759: // Cannot verify that new value stuck because set may be lenient.
0760: }
0761: } else {
0762: instant = iJulianField.set(instant, text, locale);
0763: if (instant >= iCutover) {
0764: // Only adjust if gap fully crossed.
0765: if (instant - iGapDuration >= iCutover) {
0766: instant = julianToGregorian(instant);
0767: }
0768: // Cannot verify that new value stuck because set may be lenient.
0769: }
0770: }
0771: return instant;
0772: }
0773:
0774: public DurationField getDurationField() {
0775: return iDurationField;
0776: }
0777:
0778: public DurationField getRangeDurationField() {
0779: return iRangeDurationField;
0780: }
0781:
0782: public boolean isLeap(long instant) {
0783: if (instant >= iCutover) {
0784: return iGregorianField.isLeap(instant);
0785: } else {
0786: return iJulianField.isLeap(instant);
0787: }
0788: }
0789:
0790: public int getLeapAmount(long instant) {
0791: if (instant >= iCutover) {
0792: return iGregorianField.getLeapAmount(instant);
0793: } else {
0794: return iJulianField.getLeapAmount(instant);
0795: }
0796: }
0797:
0798: public DurationField getLeapDurationField() {
0799: return iGregorianField.getLeapDurationField();
0800: }
0801:
0802: public int getMinimumValue() {
0803: // For all precise fields, the Julian and Gregorian limits are
0804: // identical. Choose Julian to tighten up the year limits.
0805: return iJulianField.getMinimumValue();
0806: }
0807:
0808: public int getMinimumValue(ReadablePartial partial) {
0809: return iJulianField.getMinimumValue(partial);
0810: }
0811:
0812: public int getMinimumValue(ReadablePartial partial, int[] values) {
0813: return iJulianField.getMinimumValue(partial, values);
0814: }
0815:
0816: public int getMinimumValue(long instant) {
0817: if (instant < iCutover) {
0818: return iJulianField.getMinimumValue(instant);
0819: }
0820:
0821: int min = iGregorianField.getMinimumValue(instant);
0822:
0823: // Because the cutover may reduce the length of this field, verify
0824: // the minimum by setting it.
0825: instant = iGregorianField.set(instant, min);
0826: if (instant < iCutover) {
0827: min = iGregorianField.get(iCutover);
0828: }
0829:
0830: return min;
0831: }
0832:
0833: public int getMaximumValue() {
0834: // For all precise fields, the Julian and Gregorian limits are
0835: // identical.
0836: return iGregorianField.getMaximumValue();
0837: }
0838:
0839: public int getMaximumValue(long instant) {
0840: if (instant >= iCutover) {
0841: return iGregorianField.getMaximumValue(instant);
0842: }
0843:
0844: int max = iJulianField.getMaximumValue(instant);
0845:
0846: // Because the cutover may reduce the length of this field, verify
0847: // the maximum by setting it.
0848: instant = iJulianField.set(instant, max);
0849: if (instant >= iCutover) {
0850: max = iJulianField.get(iJulianField.add(iCutover, -1));
0851: }
0852:
0853: return max;
0854: }
0855:
0856: public int getMaximumValue(ReadablePartial partial) {
0857: long instant = GJChronology.getInstanceUTC().set(partial,
0858: 0L);
0859: return getMaximumValue(instant);
0860: }
0861:
0862: public int getMaximumValue(ReadablePartial partial, int[] values) {
0863: Chronology chrono = GJChronology.getInstanceUTC();
0864: long instant = 0L;
0865: for (int i = 0, isize = partial.size(); i < isize; i++) {
0866: DateTimeField field = partial.getFieldType(i).getField(
0867: chrono);
0868: if (values[i] <= field.getMaximumValue(instant)) {
0869: instant = field.set(instant, values[i]);
0870: }
0871: }
0872: return getMaximumValue(instant);
0873: }
0874:
0875: public long roundFloor(long instant) {
0876: if (instant >= iCutover) {
0877: instant = iGregorianField.roundFloor(instant);
0878: if (instant < iCutover) {
0879: // Only adjust if gap fully crossed.
0880: if (instant + iGapDuration < iCutover) {
0881: instant = gregorianToJulian(instant);
0882: }
0883: }
0884: } else {
0885: instant = iJulianField.roundFloor(instant);
0886: }
0887: return instant;
0888: }
0889:
0890: public long roundCeiling(long instant) {
0891: if (instant >= iCutover) {
0892: instant = iGregorianField.roundCeiling(instant);
0893: } else {
0894: instant = iJulianField.roundCeiling(instant);
0895: if (instant >= iCutover) {
0896: // Only adjust if gap fully crossed.
0897: if (instant - iGapDuration >= iCutover) {
0898: instant = julianToGregorian(instant);
0899: }
0900: }
0901: }
0902: return instant;
0903: }
0904:
0905: public int getMaximumTextLength(Locale locale) {
0906: return Math.max(iJulianField.getMaximumTextLength(locale),
0907: iGregorianField.getMaximumTextLength(locale));
0908: }
0909:
0910: public int getMaximumShortTextLength(Locale locale) {
0911: return Math.max(iJulianField
0912: .getMaximumShortTextLength(locale), iGregorianField
0913: .getMaximumShortTextLength(locale));
0914: }
0915:
0916: protected long julianToGregorian(long instant) {
0917: if (iConvertByWeekyear) {
0918: return julianToGregorianByWeekyear(instant);
0919: } else {
0920: return julianToGregorianByYear(instant);
0921: }
0922: }
0923:
0924: protected long gregorianToJulian(long instant) {
0925: if (iConvertByWeekyear) {
0926: return gregorianToJulianByWeekyear(instant);
0927: } else {
0928: return gregorianToJulianByYear(instant);
0929: }
0930: }
0931: }
0932:
0933: //-----------------------------------------------------------------------
0934: /**
0935: * Cutover field for variable length fields. These fields internally call
0936: * set whenever add is called. As a result, the same correction applied to
0937: * set must be applied to add and addWrapField. Knowing when to use this
0938: * field requires specific knowledge of how the GJ fields are implemented.
0939: */
0940: private final class ImpreciseCutoverField extends CutoverField {
0941: private static final long serialVersionUID = 3410248757173576441L;
0942:
0943: /**
0944: * Creates a duration field that links back to this.
0945: */
0946: ImpreciseCutoverField(DateTimeField julianField,
0947: DateTimeField gregorianField, long cutoverMillis) {
0948: this (julianField, gregorianField, null, cutoverMillis,
0949: false);
0950: }
0951:
0952: /**
0953: * Uses a shared duration field rather than creating a new one.
0954: *
0955: * @param durationField shared duration field
0956: */
0957: ImpreciseCutoverField(DateTimeField julianField,
0958: DateTimeField gregorianField,
0959: DurationField durationField, long cutoverMillis) {
0960: this (julianField, gregorianField, durationField,
0961: cutoverMillis, false);
0962: }
0963:
0964: /**
0965: * Uses a shared duration field rather than creating a new one.
0966: *
0967: * @param durationField shared duration field
0968: */
0969: ImpreciseCutoverField(DateTimeField julianField,
0970: DateTimeField gregorianField,
0971: DurationField durationField, long cutoverMillis,
0972: boolean convertByWeekyear) {
0973: super (julianField, gregorianField, cutoverMillis,
0974: convertByWeekyear);
0975: if (durationField == null) {
0976: durationField = new LinkedDurationField(iDurationField,
0977: this );
0978: }
0979: iDurationField = durationField;
0980: }
0981:
0982: public long add(long instant, int value) {
0983: if (instant >= iCutover) {
0984: instant = iGregorianField.add(instant, value);
0985: if (instant < iCutover) {
0986: // Only adjust if gap fully crossed.
0987: if (instant + iGapDuration < iCutover) {
0988: instant = gregorianToJulian(instant);
0989: }
0990: }
0991: } else {
0992: instant = iJulianField.add(instant, value);
0993: if (instant >= iCutover) {
0994: // Only adjust if gap fully crossed.
0995: if (instant - iGapDuration >= iCutover) {
0996: instant = julianToGregorian(instant);
0997: }
0998: }
0999: }
1000: return instant;
1001: }
1002:
1003: public long add(long instant, long value) {
1004: if (instant >= iCutover) {
1005: instant = iGregorianField.add(instant, value);
1006: if (instant < iCutover) {
1007: // Only adjust if gap fully crossed.
1008: if (instant + iGapDuration < iCutover) {
1009: instant = gregorianToJulian(instant);
1010: }
1011: }
1012: } else {
1013: instant = iJulianField.add(instant, value);
1014: if (instant >= iCutover) {
1015: // Only adjust if gap fully crossed.
1016: if (instant - iGapDuration >= iCutover) {
1017: instant = julianToGregorian(instant);
1018: }
1019: }
1020: }
1021: return instant;
1022: }
1023:
1024: public int getDifference(long minuendInstant,
1025: long subtrahendInstant) {
1026: if (minuendInstant >= iCutover) {
1027: if (subtrahendInstant >= iCutover) {
1028: return iGregorianField.getDifference(
1029: 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.getDifference(minuendInstant,
1035: subtrahendInstant);
1036: } else {
1037: if (subtrahendInstant < iCutover) {
1038: return iJulianField.getDifference(minuendInstant,
1039: subtrahendInstant);
1040: }
1041: // Remember, the add is being reversed. Since subtrahend is
1042: // Gregorian, convert minuend to Gregorian to match.
1043: minuendInstant = julianToGregorian(minuendInstant);
1044: return iGregorianField.getDifference(minuendInstant,
1045: subtrahendInstant);
1046: }
1047: }
1048:
1049: public long getDifferenceAsLong(long minuendInstant,
1050: long subtrahendInstant) {
1051: if (minuendInstant >= iCutover) {
1052: if (subtrahendInstant >= iCutover) {
1053: return iGregorianField.getDifferenceAsLong(
1054: minuendInstant, subtrahendInstant);
1055: }
1056: // Remember, the add is being reversed. Since subtrahend is
1057: // Julian, convert minuend to Julian to match.
1058: minuendInstant = gregorianToJulian(minuendInstant);
1059: return iJulianField.getDifferenceAsLong(minuendInstant,
1060: subtrahendInstant);
1061: } else {
1062: if (subtrahendInstant < iCutover) {
1063: return iJulianField.getDifferenceAsLong(
1064: minuendInstant, subtrahendInstant);
1065: }
1066: // Remember, the add is being reversed. Since subtrahend is
1067: // Gregorian, convert minuend to Gregorian to match.
1068: minuendInstant = julianToGregorian(minuendInstant);
1069: return iGregorianField.getDifferenceAsLong(
1070: minuendInstant, subtrahendInstant);
1071: }
1072: }
1073:
1074: // Since the imprecise fields have durations longer than the gap
1075: // duration, keep these methods simple. The inherited implementations
1076: // produce incorrect results.
1077: //
1078: // Degenerate case: If this field is a month, and the cutover is set
1079: // far into the future, then the gap duration may be so large as to
1080: // reduce the number of months in a year. If the missing month(s) are
1081: // at the beginning or end of the year, then the minimum and maximum
1082: // values are not 1 and 12. I don't expect this case to ever occur.
1083:
1084: public int getMinimumValue(long instant) {
1085: if (instant >= iCutover) {
1086: return iGregorianField.getMinimumValue(instant);
1087: } else {
1088: return iJulianField.getMinimumValue(instant);
1089: }
1090: }
1091:
1092: public int getMaximumValue(long instant) {
1093: if (instant >= iCutover) {
1094: return iGregorianField.getMaximumValue(instant);
1095: } else {
1096: return iJulianField.getMaximumValue(instant);
1097: }
1098: }
1099: }
1100:
1101: //-----------------------------------------------------------------------
1102: /**
1103: * Links the duration back to a ImpreciseCutoverField.
1104: */
1105: private static class LinkedDurationField extends
1106: DecoratedDurationField {
1107: private static final long serialVersionUID = 4097975388007713084L;
1108:
1109: private final ImpreciseCutoverField iField;
1110:
1111: LinkedDurationField(DurationField durationField,
1112: ImpreciseCutoverField dateTimeField) {
1113: super (durationField, durationField.getType());
1114: iField = dateTimeField;
1115: }
1116:
1117: public long add(long instant, int value) {
1118: return iField.add(instant, value);
1119: }
1120:
1121: public long add(long instant, long value) {
1122: return iField.add(instant, value);
1123: }
1124:
1125: public int getDifference(long minuendInstant,
1126: long subtrahendInstant) {
1127: return iField.getDifference(minuendInstant,
1128: subtrahendInstant);
1129: }
1130:
1131: public long getDifferenceAsLong(long minuendInstant,
1132: long subtrahendInstant) {
1133: return iField.getDifferenceAsLong(minuendInstant,
1134: subtrahendInstant);
1135: }
1136: }
1137:
1138: }
|