001 /*
002 * Copyright 2001-2009 Stephen Colebourne
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.joda.time.chrono;
017
018 import java.util.ArrayList;
019 import java.util.HashMap;
020 import java.util.Locale;
021 import java.util.Map;
022
023 import org.joda.time.Chronology;
024 import org.joda.time.DateTimeField;
025 import org.joda.time.DateTimeUtils;
026 import org.joda.time.DateTimeZone;
027 import org.joda.time.DurationField;
028 import org.joda.time.IllegalFieldValueException;
029 import org.joda.time.Instant;
030 import org.joda.time.ReadableInstant;
031 import org.joda.time.ReadablePartial;
032 import org.joda.time.field.BaseDateTimeField;
033 import org.joda.time.field.DecoratedDurationField;
034 import org.joda.time.format.DateTimeFormatter;
035 import org.joda.time.format.ISODateTimeFormat;
036
037 /**
038 * Implements the Gregorian/Julian calendar system which is the calendar system
039 * used in most of the world. Wherever possible, it is recommended to use the
040 * {@link ISOChronology} instead.
041 * <p>
042 * The Gregorian calendar replaced the Julian calendar, and the point in time
043 * when this chronology switches can be controlled using the second parameter
044 * of the getInstance method. By default this cutover is set to the date the
045 * Gregorian calendar was first instituted, October 15, 1582.
046 * <p>
047 * Before this date, this chronology uses the proleptic Julian calendar
048 * (proleptic means extending indefinitely). The Julian calendar has leap years
049 * every four years, whereas the Gregorian has special rules for 100 and 400
050 * years. A meaningful result will thus be obtained for all input values.
051 * However before 8 CE, Julian leap years were irregular, and before 45 BCE
052 * there was no Julian calendar.
053 * <p>
054 * This chronology differs from
055 * {@link java.util.GregorianCalendar GregorianCalendar} in that years
056 * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1.
057 * The yearOfEra field produces results compatible with GregorianCalendar.
058 * <p>
059 * The Julian calendar does not have a year zero, and so year -1 is followed by
060 * year 1. If the Gregorian cutover date is specified at or before year -1
061 * (Julian), year zero is defined. In other words, the proleptic Gregorian
062 * chronology used by this class has a year zero.
063 * <p>
064 * To create a pure proleptic Julian chronology, use {@link JulianChronology},
065 * and to create a pure proleptic Gregorian chronology, use
066 * {@link GregorianChronology}.
067 * <p>
068 * GJChronology is thread-safe and immutable.
069 *
070 * @author Brian S O'Neill
071 * @author Stephen Colebourne
072 * @since 1.0
073 */
074 public final class GJChronology extends AssembledChronology {
075
076 /** Serialization lock */
077 private static final long serialVersionUID = -2545574827706931671L;
078
079 /**
080 * Convert a datetime from one chronology to another.
081 */
082 private static long convertByYear(long instant, Chronology from, Chronology to) {
083 return to.getDateTimeMillis
084 (from.year().get(instant),
085 from.monthOfYear().get(instant),
086 from.dayOfMonth().get(instant),
087 from.millisOfDay().get(instant));
088 }
089
090 /**
091 * Convert a datetime from one chronology to another.
092 */
093 private static long convertByWeekyear(final long instant, Chronology from, Chronology to) {
094 long newInstant;
095 newInstant = to.weekyear().set(0, from.weekyear().get(instant));
096 newInstant = to.weekOfWeekyear().set(newInstant, from.weekOfWeekyear().get(instant));
097 newInstant = to.dayOfWeek().set(newInstant, from.dayOfWeek().get(instant));
098 newInstant = to.millisOfDay().set(newInstant, from.millisOfDay().get(instant));
099 return newInstant;
100 }
101
102 /**
103 * The default GregorianJulian cutover point.
104 */
105 static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L);
106
107 /** Cache of zone to chronology list */
108 private static final Map<DateTimeZone, ArrayList<GJChronology>> cCache = new HashMap<DateTimeZone, ArrayList<GJChronology>>();
109
110 /**
111 * Factory method returns instances of the default GJ cutover
112 * chronology. This uses a cutover date of October 15, 1582 (Gregorian)
113 * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by
114 * October 15, 1582 (Gregorian).
115 *
116 * <p>The first day of the week is designated to be
117 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
118 * and the minimum days in the first week of the year is 4.
119 *
120 * <p>The time zone of the returned instance is UTC.
121 */
122 public static GJChronology getInstanceUTC() {
123 return getInstance(DateTimeZone.UTC, DEFAULT_CUTOVER, 4);
124 }
125
126 /**
127 * Factory method returns instances of the default GJ cutover
128 * chronology. This uses a cutover date of October 15, 1582 (Gregorian)
129 * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by
130 * October 15, 1582 (Gregorian).
131 *
132 * <p>The first day of the week is designated to be
133 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
134 * and the minimum days in the first week of the year is 4.
135 *
136 * <p>The returned chronology is in the default time zone.
137 */
138 public static GJChronology getInstance() {
139 return getInstance(DateTimeZone.getDefault(), DEFAULT_CUTOVER, 4);
140 }
141
142 /**
143 * Factory method returns instances of the GJ cutover chronology. This uses
144 * a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this
145 * value, October 4, 1582 (Julian) is followed by October 15, 1582
146 * (Gregorian).
147 *
148 * <p>The first day of the week is designated to be
149 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
150 * and the minimum days in the first week of the year is 4.
151 *
152 * @param zone the time zone to use, null is default
153 */
154 public static GJChronology getInstance(DateTimeZone zone) {
155 return getInstance(zone, DEFAULT_CUTOVER, 4);
156 }
157
158 /**
159 * Factory method returns instances of the GJ cutover chronology. Any
160 * cutover date may be specified.
161 *
162 * <p>The first day of the week is designated to be
163 * {@link org.joda.time.DateTimeConstants#MONDAY Monday},
164 * and the minimum days in the first week of the year is 4.
165 *
166 * @param zone the time zone to use, null is default
167 * @param gregorianCutover the cutover to use, null means default
168 */
169 public static GJChronology getInstance(
170 DateTimeZone zone,
171 ReadableInstant gregorianCutover) {
172
173 return getInstance(zone, gregorianCutover, 4);
174 }
175
176 /**
177 * Factory method returns instances of the GJ cutover chronology. Any
178 * cutover date may be specified.
179 *
180 * @param zone the time zone to use, null is default
181 * @param gregorianCutover the cutover to use, null means default
182 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4
183 */
184 public static synchronized GJChronology getInstance(
185 DateTimeZone zone,
186 ReadableInstant gregorianCutover,
187 int minDaysInFirstWeek) {
188
189 zone = DateTimeUtils.getZone(zone);
190 Instant cutoverInstant;
191 if (gregorianCutover == null) {
192 cutoverInstant = DEFAULT_CUTOVER;
193 } else {
194 cutoverInstant = gregorianCutover.toInstant();
195 }
196
197 GJChronology chrono;
198
199 ArrayList<GJChronology> chronos = cCache.get(zone);
200 if (chronos == null) {
201 chronos = new ArrayList<GJChronology>(2);
202 cCache.put(zone, chronos);
203 } else {
204 for (int i=chronos.size(); --i>=0; ) {
205 chrono = chronos.get(i);
206 if (minDaysInFirstWeek == chrono.getMinimumDaysInFirstWeek() &&
207 cutoverInstant.equals(chrono.getGregorianCutover())) {
208
209 return chrono;
210 }
211 }
212 }
213
214 if (zone == DateTimeZone.UTC) {
215 chrono = new GJChronology
216 (JulianChronology.getInstance(zone, minDaysInFirstWeek),
217 GregorianChronology.getInstance(zone, minDaysInFirstWeek),
218 cutoverInstant);
219 } else {
220 chrono = getInstance(DateTimeZone.UTC, cutoverInstant, minDaysInFirstWeek);
221 chrono = new GJChronology
222 (ZonedChronology.getInstance(chrono, zone),
223 chrono.iJulianChronology,
224 chrono.iGregorianChronology,
225 chrono.iCutoverInstant);
226 }
227
228 chronos.add(chrono);
229
230 return chrono;
231 }
232
233 /**
234 * Factory method returns instances of the GJ cutover chronology. Any
235 * cutover date may be specified.
236 *
237 * @param zone the time zone to use, null is default
238 * @param gregorianCutover the cutover to use
239 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4
240 */
241 public static GJChronology getInstance(
242 DateTimeZone zone,
243 long gregorianCutover,
244 int minDaysInFirstWeek) {
245
246 Instant cutoverInstant;
247 if (gregorianCutover == DEFAULT_CUTOVER.getMillis()) {
248 cutoverInstant = null;
249 } else {
250 cutoverInstant = new Instant(gregorianCutover);
251 }
252 return getInstance(zone, cutoverInstant, minDaysInFirstWeek);
253 }
254
255 //-----------------------------------------------------------------------
256 private JulianChronology iJulianChronology;
257 private GregorianChronology iGregorianChronology;
258 private Instant iCutoverInstant;
259
260 private long iCutoverMillis;
261 private long iGapDuration;
262
263 /**
264 * @param julian chronology used before the cutover instant
265 * @param gregorian chronology used at and after the cutover instant
266 * @param cutoverInstant instant when the gregorian chronology began
267 */
268 private GJChronology(JulianChronology julian,
269 GregorianChronology gregorian,
270 Instant cutoverInstant) {
271 super(null, new Object[] {julian, gregorian, cutoverInstant});
272 }
273
274 /**
275 * Called when applying a time zone.
276 */
277 private GJChronology(Chronology base,
278 JulianChronology julian,
279 GregorianChronology gregorian,
280 Instant cutoverInstant) {
281 super(base, new Object[] {julian, gregorian, cutoverInstant});
282 }
283
284 /**
285 * Serialization singleton
286 */
287 private Object readResolve() {
288 return getInstance(getZone(), iCutoverInstant, getMinimumDaysInFirstWeek());
289 }
290
291 public DateTimeZone getZone() {
292 Chronology base;
293 if ((base = getBase()) != null) {
294 return base.getZone();
295 }
296 return DateTimeZone.UTC;
297 }
298
299 // Conversion
300 //-----------------------------------------------------------------------
301 /**
302 * Gets the Chronology in the UTC time zone.
303 *
304 * @return the chronology in UTC
305 */
306 public Chronology withUTC() {
307 return withZone(DateTimeZone.UTC);
308 }
309
310 /**
311 * Gets the Chronology in a specific time zone.
312 *
313 * @param zone the zone to get the chronology in, null is default
314 * @return the chronology
315 */
316 public Chronology withZone(DateTimeZone zone) {
317 if (zone == null) {
318 zone = DateTimeZone.getDefault();
319 }
320 if (zone == getZone()) {
321 return this;
322 }
323 return getInstance(zone, iCutoverInstant, getMinimumDaysInFirstWeek());
324 }
325
326 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
327 int millisOfDay)
328 throws IllegalArgumentException
329 {
330 Chronology base;
331 if ((base = getBase()) != null) {
332 return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay);
333 }
334
335 // Assume date is Gregorian.
336 long instant = iGregorianChronology.getDateTimeMillis
337 (year, monthOfYear, dayOfMonth, millisOfDay);
338 if (instant < iCutoverMillis) {
339 // Maybe it's Julian.
340 instant = iJulianChronology.getDateTimeMillis
341 (year, monthOfYear, dayOfMonth, millisOfDay);
342 if (instant >= iCutoverMillis) {
343 // Okay, it's in the illegal cutover gap.
344 throw new IllegalArgumentException("Specified date does not exist");
345 }
346 }
347 return instant;
348 }
349
350 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
351 int hourOfDay, int minuteOfHour,
352 int secondOfMinute, int millisOfSecond)
353 throws IllegalArgumentException
354 {
355 Chronology base;
356 if ((base = getBase()) != null) {
357 return base.getDateTimeMillis
358 (year, monthOfYear, dayOfMonth,
359 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
360 }
361
362 // Assume date is Gregorian.
363 long instant;
364 try {
365 instant = iGregorianChronology.getDateTimeMillis
366 (year, monthOfYear, dayOfMonth,
367 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
368 } catch (IllegalFieldValueException ex) {
369 if (monthOfYear != 2 || dayOfMonth != 29) {
370 throw ex;
371 }
372 instant = iGregorianChronology.getDateTimeMillis
373 (year, monthOfYear, 28,
374 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
375 if (instant >= iCutoverMillis) {
376 throw ex;
377 }
378 }
379 if (instant < iCutoverMillis) {
380 // Maybe it's Julian.
381 instant = iJulianChronology.getDateTimeMillis
382 (year, monthOfYear, dayOfMonth,
383 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
384 if (instant >= iCutoverMillis) {
385 // Okay, it's in the illegal cutover gap.
386 throw new IllegalArgumentException("Specified date does not exist");
387 }
388 }
389 return instant;
390 }
391
392 /**
393 * Gets the cutover instant between Gregorian and Julian chronologies.
394 * @return the cutover instant
395 */
396 public Instant getGregorianCutover() {
397 return iCutoverInstant;
398 }
399
400 /**
401 * Gets the minimum days needed for a week to be the first week in a year.
402 *
403 * @return the minimum days
404 */
405 public int getMinimumDaysInFirstWeek() {
406 return iGregorianChronology.getMinimumDaysInFirstWeek();
407 }
408
409 /**
410 * Checks if this chronology instance equals another.
411 *
412 * @param obj the object to compare to
413 * @return true if equal
414 * @since 1.6
415 */
416 public boolean equals(Object obj) {
417 return super.equals(obj);
418 }
419
420 /**
421 * A suitable hash code for the chronology.
422 *
423 * @return the hash code
424 * @since 1.6
425 */
426 public int hashCode() {
427 return "GJ".hashCode() * 11 + iJulianChronology.hashCode() +
428 iGregorianChronology.hashCode() + iCutoverInstant.hashCode();
429 }
430
431 // Output
432 //-----------------------------------------------------------------------
433 /**
434 * Gets a debugging toString.
435 *
436 * @return a debugging string
437 */
438 public String toString() {
439 StringBuffer sb = new StringBuffer(60);
440 sb.append("GJChronology");
441 sb.append('[');
442 sb.append(getZone().getID());
443
444 if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) {
445 sb.append(",cutover=");
446 DateTimeFormatter printer;
447 if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) {
448 printer = ISODateTimeFormat.date();
449 } else {
450 printer = ISODateTimeFormat.dateTime();
451 }
452 printer.withChronology(withUTC()).printTo(sb, iCutoverMillis);
453 }
454
455 if (getMinimumDaysInFirstWeek() != 4) {
456 sb.append(",mdfw=");
457 sb.append(getMinimumDaysInFirstWeek());
458 }
459 sb.append(']');
460
461 return sb.toString();
462 }
463
464 protected void assemble(Fields fields) {
465 Object[] params = (Object[])getParam();
466
467 JulianChronology julian = (JulianChronology)params[0];
468 GregorianChronology gregorian = (GregorianChronology)params[1];
469 Instant cutoverInstant = (Instant)params[2];
470 iCutoverMillis = cutoverInstant.getMillis();
471
472 iJulianChronology = julian;
473 iGregorianChronology = gregorian;
474 iCutoverInstant = cutoverInstant;
475
476 if (getBase() != null) {
477 return;
478 }
479
480 if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) {
481 throw new IllegalArgumentException();
482 }
483
484 // Compute difference between the chronologies at the cutover instant
485 iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis);
486
487 // Begin field definitions.
488
489 // First just copy all the Gregorian fields and then override those
490 // that need special attention.
491 fields.copyFieldsFrom(gregorian);
492
493 // Assuming cutover is at midnight, all time of day fields can be
494 // gregorian since they are unaffected by cutover.
495
496 // Verify assumption.
497 if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
498 // Cutover is sometime in the day, so cutover fields are required
499 // for time of day.
500
501 fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis);
502 fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis);
503 fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis);
504 fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis);
505 fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis);
506 fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis);
507 fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis);
508 fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis);
509 fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis);
510 fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(),
511 fields.clockhourOfHalfday, iCutoverMillis);
512 fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis);
513 }
514
515 // These fields just require basic cutover support.
516 {
517 fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis);
518 }
519
520 // DayOfYear and weekOfWeekyear require special handling since cutover
521 // year has fewer days and weeks. Extend the cutover to the start of
522 // the next year or weekyear. This keeps the sequence unbroken during
523 // the cutover year.
524
525 {
526 long cutover = gregorian.year().roundCeiling(iCutoverMillis);
527 fields.dayOfYear = new CutoverField(
528 julian.dayOfYear(), fields.dayOfYear, cutover);
529 }
530
531 {
532 long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis);
533 fields.weekOfWeekyear = new CutoverField(
534 julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true);
535 }
536
537 // These fields are special because they have imprecise durations. The
538 // family of addition methods need special attention. Override affected
539 // duration fields as well.
540 {
541 fields.year = new ImpreciseCutoverField(
542 julian.year(), fields.year, iCutoverMillis);
543 fields.years = fields.year.getDurationField();
544 fields.yearOfEra = new ImpreciseCutoverField(
545 julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis);
546 fields.yearOfCentury = new ImpreciseCutoverField(
547 julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis);
548
549 fields.centuryOfEra = new ImpreciseCutoverField(
550 julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis);
551 fields.centuries = fields.centuryOfEra.getDurationField();
552
553 fields.monthOfYear = new ImpreciseCutoverField(
554 julian.monthOfYear(), fields.monthOfYear, iCutoverMillis);
555 fields.months = fields.monthOfYear.getDurationField();
556
557 fields.weekyear = new ImpreciseCutoverField(
558 julian.weekyear(), fields.weekyear, null, iCutoverMillis, true);
559 fields.weekyearOfCentury = new ImpreciseCutoverField(
560 julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis);
561 fields.weekyears = fields.weekyear.getDurationField();
562 }
563
564 // These fields require basic cutover support, except they must link to
565 // imprecise durations.
566 {
567 CutoverField cf = new CutoverField
568 (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis);
569 cf.iRangeDurationField = fields.months;
570 fields.dayOfMonth = cf;
571 }
572 }
573
574 long julianToGregorianByYear(long instant) {
575 return convertByYear(instant, iJulianChronology, iGregorianChronology);
576 }
577
578 long gregorianToJulianByYear(long instant) {
579 return convertByYear(instant, iGregorianChronology, iJulianChronology);
580 }
581
582 long julianToGregorianByWeekyear(long instant) {
583 return convertByWeekyear(instant, iJulianChronology, iGregorianChronology);
584 }
585
586 long gregorianToJulianByWeekyear(long instant) {
587 return convertByWeekyear(instant, iGregorianChronology, iJulianChronology);
588 }
589
590 //-----------------------------------------------------------------------
591 /**
592 * This basic cutover field adjusts calls to 'get' and 'set' methods, and
593 * assumes that calls to add and addWrapField are unaffected by the cutover.
594 */
595 private class CutoverField extends BaseDateTimeField {
596 private static final long serialVersionUID = 3528501219481026402L;
597
598 final DateTimeField iJulianField;
599 final DateTimeField iGregorianField;
600 final long iCutover;
601 final boolean iConvertByWeekyear;
602
603 protected DurationField iDurationField;
604 protected DurationField iRangeDurationField;
605
606 /**
607 * @param julianField field from the chronology used before the cutover instant
608 * @param gregorianField field from the chronology used at and after the cutover
609 * @param cutoverMillis the millis of the cutover
610 */
611 CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
612 this(julianField, gregorianField, cutoverMillis, false);
613 }
614
615 /**
616 * @param julianField field from the chronology used before the cutover instant
617 * @param gregorianField field from the chronology used at and after the cutover
618 * @param cutoverMillis the millis of the cutover
619 * @param convertByWeekyear
620 */
621 CutoverField(DateTimeField julianField, DateTimeField gregorianField,
622 long cutoverMillis, boolean convertByWeekyear) {
623 super(gregorianField.getType());
624 iJulianField = julianField;
625 iGregorianField = gregorianField;
626 iCutover = cutoverMillis;
627 iConvertByWeekyear = convertByWeekyear;
628 // Although average length of Julian and Gregorian years differ,
629 // use the Gregorian duration field because it is more accurate.
630 iDurationField = gregorianField.getDurationField();
631
632 DurationField rangeField = gregorianField.getRangeDurationField();
633 if (rangeField == null) {
634 rangeField = julianField.getRangeDurationField();
635 }
636 iRangeDurationField = rangeField;
637 }
638
639 public boolean isLenient() {
640 return false;
641 }
642
643 public int get(long instant) {
644 if (instant >= iCutover) {
645 return iGregorianField.get(instant);
646 } else {
647 return iJulianField.get(instant);
648 }
649 }
650
651 public String getAsText(long instant, Locale locale) {
652 if (instant >= iCutover) {
653 return iGregorianField.getAsText(instant, locale);
654 } else {
655 return iJulianField.getAsText(instant, locale);
656 }
657 }
658
659 public String getAsText(int fieldValue, Locale locale) {
660 return iGregorianField.getAsText(fieldValue, locale);
661 }
662
663 public String getAsShortText(long instant, Locale locale) {
664 if (instant >= iCutover) {
665 return iGregorianField.getAsShortText(instant, locale);
666 } else {
667 return iJulianField.getAsShortText(instant, locale);
668 }
669 }
670
671 public String getAsShortText(int fieldValue, Locale locale) {
672 return iGregorianField.getAsShortText(fieldValue, locale);
673 }
674
675 public long add(long instant, int value) {
676 return iGregorianField.add(instant, value);
677 }
678
679 public long add(long instant, long value) {
680 return iGregorianField.add(instant, value);
681 }
682
683 public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) {
684 // overridden as superclass algorithm can't handle
685 // 2004-02-29 + 48 months -> 2008-02-29 type dates
686 if (valueToAdd == 0) {
687 return values;
688 }
689 if (DateTimeUtils.isContiguous(partial)) {
690 long instant = 0L;
691 for (int i = 0, isize = partial.size(); i < isize; i++) {
692 instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]);
693 }
694 instant = add(instant, valueToAdd);
695 return GJChronology.this.get(partial, instant);
696 } else {
697 return super.add(partial, fieldIndex, values, valueToAdd);
698 }
699 }
700
701 public int getDifference(long minuendInstant, long subtrahendInstant) {
702 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
703 }
704
705 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
706 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
707 }
708
709 public long set(long instant, int value) {
710 if (instant >= iCutover) {
711 instant = iGregorianField.set(instant, value);
712 if (instant < iCutover) {
713 // Only adjust if gap fully crossed.
714 if (instant + iGapDuration < iCutover) {
715 instant = gregorianToJulian(instant);
716 }
717 // Verify that new value stuck.
718 if (get(instant) != value) {
719 throw new IllegalFieldValueException
720 (iGregorianField.getType(), Integer.valueOf(value), null, null);
721 }
722 }
723 } else {
724 instant = iJulianField.set(instant, value);
725 if (instant >= iCutover) {
726 // Only adjust if gap fully crossed.
727 if (instant - iGapDuration >= iCutover) {
728 instant = julianToGregorian(instant);
729 }
730 // Verify that new value stuck.
731 if (get(instant) != value) {
732 throw new IllegalFieldValueException
733 (iJulianField.getType(), Integer.valueOf(value), null, null);
734 }
735 }
736 }
737 return instant;
738 }
739
740 public long set(long instant, String text, Locale locale) {
741 if (instant >= iCutover) {
742 instant = iGregorianField.set(instant, text, locale);
743 if (instant < iCutover) {
744 // Only adjust if gap fully crossed.
745 if (instant + iGapDuration < iCutover) {
746 instant = gregorianToJulian(instant);
747 }
748 // Cannot verify that new value stuck because set may be lenient.
749 }
750 } else {
751 instant = iJulianField.set(instant, text, locale);
752 if (instant >= iCutover) {
753 // Only adjust if gap fully crossed.
754 if (instant - iGapDuration >= iCutover) {
755 instant = julianToGregorian(instant);
756 }
757 // Cannot verify that new value stuck because set may be lenient.
758 }
759 }
760 return instant;
761 }
762
763 public DurationField getDurationField() {
764 return iDurationField;
765 }
766
767 public DurationField getRangeDurationField() {
768 return iRangeDurationField;
769 }
770
771 public boolean isLeap(long instant) {
772 if (instant >= iCutover) {
773 return iGregorianField.isLeap(instant);
774 } else {
775 return iJulianField.isLeap(instant);
776 }
777 }
778
779 public int getLeapAmount(long instant) {
780 if (instant >= iCutover) {
781 return iGregorianField.getLeapAmount(instant);
782 } else {
783 return iJulianField.getLeapAmount(instant);
784 }
785 }
786
787 public DurationField getLeapDurationField() {
788 return iGregorianField.getLeapDurationField();
789 }
790
791
792 public int getMinimumValue() {
793 // For all precise fields, the Julian and Gregorian limits are
794 // identical. Choose Julian to tighten up the year limits.
795 return iJulianField.getMinimumValue();
796 }
797
798 public int getMinimumValue(ReadablePartial partial) {
799 return iJulianField.getMinimumValue(partial);
800 }
801
802 public int getMinimumValue(ReadablePartial partial, int[] values) {
803 return iJulianField.getMinimumValue(partial, values);
804 }
805
806 public int getMinimumValue(long instant) {
807 if (instant < iCutover) {
808 return iJulianField.getMinimumValue(instant);
809 }
810
811 int min = iGregorianField.getMinimumValue(instant);
812
813 // Because the cutover may reduce the length of this field, verify
814 // the minimum by setting it.
815 instant = iGregorianField.set(instant, min);
816 if (instant < iCutover) {
817 min = iGregorianField.get(iCutover);
818 }
819
820 return min;
821 }
822
823 public int getMaximumValue() {
824 // For all precise fields, the Julian and Gregorian limits are
825 // identical.
826 return iGregorianField.getMaximumValue();
827 }
828
829 public int getMaximumValue(long instant) {
830 if (instant >= iCutover) {
831 return iGregorianField.getMaximumValue(instant);
832 }
833
834 int max = iJulianField.getMaximumValue(instant);
835
836 // Because the cutover may reduce the length of this field, verify
837 // the maximum by setting it.
838 instant = iJulianField.set(instant, max);
839 if (instant >= iCutover) {
840 max = iJulianField.get(iJulianField.add(iCutover, -1));
841 }
842
843 return max;
844 }
845
846 public int getMaximumValue(ReadablePartial partial) {
847 long instant = GJChronology.getInstanceUTC().set(partial, 0L);
848 return getMaximumValue(instant);
849 }
850
851 public int getMaximumValue(ReadablePartial partial, int[] values) {
852 Chronology chrono = GJChronology.getInstanceUTC();
853 long instant = 0L;
854 for (int i = 0, isize = partial.size(); i < isize; i++) {
855 DateTimeField field = partial.getFieldType(i).getField(chrono);
856 if (values[i] <= field.getMaximumValue(instant)) {
857 instant = field.set(instant, values[i]);
858 }
859 }
860 return getMaximumValue(instant);
861 }
862
863 public long roundFloor(long instant) {
864 if (instant >= iCutover) {
865 instant = iGregorianField.roundFloor(instant);
866 if (instant < iCutover) {
867 // Only adjust if gap fully crossed.
868 if (instant + iGapDuration < iCutover) {
869 instant = gregorianToJulian(instant);
870 }
871 }
872 } else {
873 instant = iJulianField.roundFloor(instant);
874 }
875 return instant;
876 }
877
878 public long roundCeiling(long instant) {
879 if (instant >= iCutover) {
880 instant = iGregorianField.roundCeiling(instant);
881 } else {
882 instant = iJulianField.roundCeiling(instant);
883 if (instant >= iCutover) {
884 // Only adjust if gap fully crossed.
885 if (instant - iGapDuration >= iCutover) {
886 instant = julianToGregorian(instant);
887 }
888 }
889 }
890 return instant;
891 }
892
893 public int getMaximumTextLength(Locale locale) {
894 return Math.max(iJulianField.getMaximumTextLength(locale),
895 iGregorianField.getMaximumTextLength(locale));
896 }
897
898 public int getMaximumShortTextLength(Locale locale) {
899 return Math.max(iJulianField.getMaximumShortTextLength(locale),
900 iGregorianField.getMaximumShortTextLength(locale));
901 }
902
903 protected long julianToGregorian(long instant) {
904 if (iConvertByWeekyear) {
905 return julianToGregorianByWeekyear(instant);
906 } else {
907 return julianToGregorianByYear(instant);
908 }
909 }
910
911 protected long gregorianToJulian(long instant) {
912 if (iConvertByWeekyear) {
913 return gregorianToJulianByWeekyear(instant);
914 } else {
915 return gregorianToJulianByYear(instant);
916 }
917 }
918 }
919
920 //-----------------------------------------------------------------------
921 /**
922 * Cutover field for variable length fields. These fields internally call
923 * set whenever add is called. As a result, the same correction applied to
924 * set must be applied to add and addWrapField. Knowing when to use this
925 * field requires specific knowledge of how the GJ fields are implemented.
926 */
927 private final class ImpreciseCutoverField extends CutoverField {
928 private static final long serialVersionUID = 3410248757173576441L;
929
930 /**
931 * Creates a duration field that links back to this.
932 */
933 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
934 this(julianField, gregorianField, null, cutoverMillis, false);
935 }
936
937 /**
938 * Uses a shared duration field rather than creating a new one.
939 *
940 * @param durationField shared duration field
941 */
942 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
943 DurationField durationField, long cutoverMillis)
944 {
945 this(julianField, gregorianField, durationField, cutoverMillis, false);
946 }
947
948 /**
949 * Uses a shared duration field rather than creating a new one.
950 *
951 * @param durationField shared duration field
952 */
953 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
954 DurationField durationField,
955 long cutoverMillis, boolean convertByWeekyear)
956 {
957 super(julianField, gregorianField, cutoverMillis, convertByWeekyear);
958 if (durationField == null) {
959 durationField = new LinkedDurationField(iDurationField, this);
960 }
961 iDurationField = durationField;
962 }
963
964 public long add(long instant, int value) {
965 if (instant >= iCutover) {
966 instant = iGregorianField.add(instant, value);
967 if (instant < iCutover) {
968 // Only adjust if gap fully crossed.
969 if (instant + iGapDuration < iCutover) {
970 instant = gregorianToJulian(instant);
971 }
972 }
973 } else {
974 instant = iJulianField.add(instant, value);
975 if (instant >= iCutover) {
976 // Only adjust if gap fully crossed.
977 if (instant - iGapDuration >= iCutover) {
978 instant = julianToGregorian(instant);
979 }
980 }
981 }
982 return instant;
983 }
984
985 public long add(long instant, long value) {
986 if (instant >= iCutover) {
987 instant = iGregorianField.add(instant, value);
988 if (instant < iCutover) {
989 // Only adjust if gap fully crossed.
990 if (instant + iGapDuration < iCutover) {
991 instant = gregorianToJulian(instant);
992 }
993 }
994 } else {
995 instant = iJulianField.add(instant, value);
996 if (instant >= iCutover) {
997 // Only adjust if gap fully crossed.
998 if (instant - iGapDuration >= iCutover) {
999 instant = julianToGregorian(instant);
1000 }
1001 }
1002 }
1003 return instant;
1004 }
1005
1006 public int getDifference(long minuendInstant, long subtrahendInstant) {
1007 if (minuendInstant >= iCutover) {
1008 if (subtrahendInstant >= iCutover) {
1009 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
1010 }
1011 // Remember, the add is being reversed. Since subtrahend is
1012 // Julian, convert minuend to Julian to match.
1013 minuendInstant = gregorianToJulian(minuendInstant);
1014 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1015 } else {
1016 if (subtrahendInstant < iCutover) {
1017 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
1018 }
1019 // Remember, the add is being reversed. Since subtrahend is
1020 // Gregorian, convert minuend to Gregorian to match.
1021 minuendInstant = julianToGregorian(minuendInstant);
1022 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
1023 }
1024 }
1025
1026 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1027 if (minuendInstant >= iCutover) {
1028 if (subtrahendInstant >= iCutover) {
1029 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1030 }
1031 // Remember, the add is being reversed. Since subtrahend is
1032 // Julian, convert minuend to Julian to match.
1033 minuendInstant = gregorianToJulian(minuendInstant);
1034 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1035 } else {
1036 if (subtrahendInstant < iCutover) {
1037 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1038 }
1039 // Remember, the add is being reversed. Since subtrahend is
1040 // Gregorian, convert minuend to Gregorian to match.
1041 minuendInstant = julianToGregorian(minuendInstant);
1042 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1043 }
1044 }
1045
1046 // Since the imprecise fields have durations longer than the gap
1047 // duration, keep these methods simple. The inherited implementations
1048 // produce incorrect results.
1049 //
1050 // Degenerate case: If this field is a month, and the cutover is set
1051 // far into the future, then the gap duration may be so large as to
1052 // reduce the number of months in a year. If the missing month(s) are
1053 // at the beginning or end of the year, then the minimum and maximum
1054 // values are not 1 and 12. I don't expect this case to ever occur.
1055
1056 public int getMinimumValue(long instant) {
1057 if (instant >= iCutover) {
1058 return iGregorianField.getMinimumValue(instant);
1059 } else {
1060 return iJulianField.getMinimumValue(instant);
1061 }
1062 }
1063
1064 public int getMaximumValue(long instant) {
1065 if (instant >= iCutover) {
1066 return iGregorianField.getMaximumValue(instant);
1067 } else {
1068 return iJulianField.getMaximumValue(instant);
1069 }
1070 }
1071 }
1072
1073 //-----------------------------------------------------------------------
1074 /**
1075 * Links the duration back to a ImpreciseCutoverField.
1076 */
1077 private static class LinkedDurationField extends DecoratedDurationField {
1078 private static final long serialVersionUID = 4097975388007713084L;
1079
1080 private final ImpreciseCutoverField iField;
1081
1082 LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) {
1083 super(durationField, durationField.getType());
1084 iField = dateTimeField;
1085 }
1086
1087 public long add(long instant, int value) {
1088 return iField.add(instant, value);
1089 }
1090
1091 public long add(long instant, long value) {
1092 return iField.add(instant, value);
1093 }
1094
1095 public int getDifference(long minuendInstant, long subtrahendInstant) {
1096 return iField.getDifference(minuendInstant, subtrahendInstant);
1097 }
1098
1099 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1100 return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1101 }
1102 }
1103
1104 }