KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joda > time > chrono > GJChronology


1 /*
2  * Copyright 2001-2005 Stephen Colebourne
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.joda.time.chrono;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Locale JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import org.joda.time.Chronology;
24 import org.joda.time.DateTimeField;
25 import org.joda.time.DateTimeUtils;
26 import org.joda.time.DateTimeZone;
27 import org.joda.time.DurationField;
28 import org.joda.time.IllegalFieldValueException;
29 import org.joda.time.Instant;
30 import org.joda.time.ReadableInstant;
31 import org.joda.time.ReadablePartial;
32 import org.joda.time.field.BaseDateTimeField;
33 import org.joda.time.field.DecoratedDurationField;
34 import org.joda.time.format.DateTimeFormatter;
35 import org.joda.time.format.ISODateTimeFormat;
36
37 /**
38  * Implements the Gregorian/Julian calendar system which is the calendar system
39  * used in most of the world. Wherever possible, it is recommended to use the
40  * {@link ISOChronology} instead.
41  * <p>
42  * The Gregorian calendar replaced the Julian calendar, and the point in time
43  * when this chronology switches can be controlled using the second parameter
44  * of the getInstance method. By default this cutover is set to the date the
45  * Gregorian calendar was first instituted, October 15, 1582.
46  * <p>
47  * Before this date, this chronology uses the proleptic Julian calendar
48  * (proleptic means extending indefinitely). The Julian calendar has leap years
49  * every four years, whereas the Gregorian has special rules for 100 and 400
50  * years. A meaningful result will thus be obtained for all input values.
51  * However before 8 CE, Julian leap years were irregular, and before 45 BCE
52  * there was no Julian calendar.
53  * <p>
54  * This chronology differs from
55  * {@link java.util.GregorianCalendar GregorianCalendar} in that years
56  * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1.
57  * The yearOfEra field produces results compatible with GregorianCalendar.
58  * <p>
59  * The Julian calendar does not have a year zero, and so year -1 is followed by
60  * year 1. If the Gregorian cutover date is specified at or before year -1
61  * (Julian), year zero is defined. In other words, the proleptic Gregorian
62  * chronology used by this class has a year zero.
63  * <p>
64  * To create a pure proleptic Julian chronology, use {@link JulianChronology},
65  * and to create a pure proleptic Gregorian chronology, use
66  * {@link GregorianChronology}.
67  * <p>
68  * GJChronology is thread-safe and immutable.
69  *
70  * @author Brian S O'Neill
71  * @author Stephen Colebourne
72  * @since 1.0
73  */

74 public final class GJChronology extends AssembledChronology {
75
76     /** Serialization lock */
77     private static final long serialVersionUID = -2545574827706931671L;
78
79     /**
80      * Convert a datetime from one chronology to another.
81      */

82     private static long convertByYear(long instant, Chronology from, Chronology to) {
83         return to.getDateTimeMillis
84             (from.year().get(instant),
85              from.monthOfYear().get(instant),
86              from.dayOfMonth().get(instant),
87              from.millisOfDay().get(instant));
88     }
89
90     /**
91      * Convert a datetime from one chronology to another.
92      */

93     private static long convertByWeekyear(final long instant, Chronology from, Chronology to) {
94         long newInstant;
95         newInstant = to.weekyear().set(0, from.weekyear().get(instant));
96         newInstant = to.weekOfWeekyear().set(newInstant, from.weekOfWeekyear().get(instant));
97         newInstant = to.dayOfWeek().set(newInstant, from.dayOfWeek().get(instant));
98         newInstant = to.millisOfDay().set(newInstant, from.millisOfDay().get(instant));
99         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 JavaDoc cCache = new HashMap JavaDoc();
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 JavaDoc chronos = (ArrayList JavaDoc)cCache.get(zone);
200         if (chronos == null) {
201             chronos = new ArrayList JavaDoc(2);
202             cCache.put(zone, chronos);
203         } else {
204             for (int i=chronos.size(); --i>=0; ) {
205                 chrono = (GJChronology)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 JavaDoc[] {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 JavaDoc[] {julian, gregorian, cutoverInstant});
282     }
283
284     /**
285      * Serialization singleton
286      */

287     private Object JavaDoc 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 JavaDoc
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 JavaDoc("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 JavaDoc
354     {
355         Chronology base;
356         if ((base = getBase()) != null) {
357             return base.getDateTimeMillis
358                 (year, monthOfYear, dayOfMonth,
359                  hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
360         }
361
362         // Assume date is Gregorian.
363
long instant = iGregorianChronology.getDateTimeMillis
364             (year, monthOfYear, dayOfMonth,
365              hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
366         if (instant < iCutoverMillis) {
367             // Maybe it's Julian.
368
instant = iJulianChronology.getDateTimeMillis
369                 (year, monthOfYear, dayOfMonth,
370                  hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
371             if (instant >= iCutoverMillis) {
372                 // Okay, it's in the illegal cutover gap.
373
throw new IllegalArgumentException JavaDoc("Specified date does not exist");
374             }
375         }
376         return instant;
377     }
378
379     /**
380      * Gets the cutover instant between Gregorian and Julian chronologies.
381      * @return the cutover instant
382      */

383     public Instant getGregorianCutover() {
384         return iCutoverInstant;
385     }
386
387     /**
388      * Gets the minimum days needed for a week to be the first week in a year.
389      *
390      * @return the minimum days
391      */

392     public int getMinimumDaysInFirstWeek() {
393         return iGregorianChronology.getMinimumDaysInFirstWeek();
394     }
395
396     // Output
397
//-----------------------------------------------------------------------
398
/**
399      * Gets a debugging toString.
400      *
401      * @return a debugging string
402      */

403     public String JavaDoc toString() {
404         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(60);
405         sb.append("GJChronology");
406         sb.append('[');
407         sb.append(getZone().getID());
408         
409         if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) {
410             sb.append(",cutover=");
411             DateTimeFormatter printer;
412             if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) {
413                 printer = ISODateTimeFormat.date();
414             } else {
415                 printer = ISODateTimeFormat.dateTime();
416             }
417             printer.withChronology(withUTC()).printTo(sb, iCutoverMillis);
418         }
419         
420         if (getMinimumDaysInFirstWeek() != 4) {
421             sb.append(",mdfw=");
422             sb.append(getMinimumDaysInFirstWeek());
423         }
424         sb.append(']');
425         
426         return sb.toString();
427     }
428
429     protected void assemble(Fields fields) {
430         Object JavaDoc[] params = (Object JavaDoc[])getParam();
431
432         JulianChronology julian = (JulianChronology)params[0];
433         GregorianChronology gregorian = (GregorianChronology)params[1];
434         Instant cutoverInstant = (Instant)params[2];
435         iCutoverMillis = cutoverInstant.getMillis();
436
437         iJulianChronology = julian;
438         iGregorianChronology = gregorian;
439         iCutoverInstant = cutoverInstant;
440
441         if (getBase() != null) {
442             return;
443         }
444
445         if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) {
446             throw new IllegalArgumentException JavaDoc();
447         }
448
449         // Compute difference between the chronologies at the cutover instant
450
iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis);
451
452         // Begin field definitions.
453

454         // First just copy all the Gregorian fields and then override those
455
// that need special attention.
456
fields.copyFieldsFrom(gregorian);
457         
458         // Assuming cutover is at midnight, all time of day fields can be
459
// gregorian since they are unaffected by cutover.
460

461         // Verify assumption.
462
if (gregorian.millisOfDay().get(iCutoverMillis) == 0) {
463             // Cutover is sometime in the day, so cutover fields are required
464
// for time of day.
465

466             fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis);
467             fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis);
468             fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis);
469             fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis);
470             fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis);
471             fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis);
472             fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis);
473             fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis);
474             fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis);
475             fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(),
476                                                          fields.clockhourOfHalfday, iCutoverMillis);
477             fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis);
478         }
479
480         // These fields just require basic cutover support.
481
{
482             fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis);
483         }
484
485         // DayOfYear and weekOfWeekyear require special handling since cutover
486
// year has fewer days and weeks. Extend the cutover to the start of
487
// the next year or weekyear. This keeps the sequence unbroken during
488
// the cutover year.
489

490         {
491             long cutover = gregorian.year().roundCeiling(iCutoverMillis);
492             fields.dayOfYear = new CutoverField(
493                 julian.dayOfYear(), fields.dayOfYear, cutover);
494         }
495
496         {
497             long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis);
498             fields.weekOfWeekyear = new CutoverField(
499                 julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true);
500         }
501
502         // These fields are special because they have imprecise durations. The
503
// family of addition methods need special attention. Override affected
504
// duration fields as well.
505
{
506             fields.year = new ImpreciseCutoverField(
507                 julian.year(), fields.year, iCutoverMillis);
508             fields.years = fields.year.getDurationField();
509             fields.yearOfEra = new ImpreciseCutoverField(
510                 julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis);
511             fields.yearOfCentury = new ImpreciseCutoverField(
512                 julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis);
513             
514             fields.centuryOfEra = new ImpreciseCutoverField(
515                 julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis);
516             fields.centuries = fields.centuryOfEra.getDurationField();
517             
518             fields.monthOfYear = new ImpreciseCutoverField(
519                 julian.monthOfYear(), fields.monthOfYear, iCutoverMillis);
520             fields.months = fields.monthOfYear.getDurationField();
521             
522             fields.weekyear = new ImpreciseCutoverField(
523                 julian.weekyear(), fields.weekyear, null, iCutoverMillis, true);
524             fields.weekyearOfCentury = new ImpreciseCutoverField(
525                 julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis);
526             fields.weekyears = fields.weekyear.getDurationField();
527         }
528
529         // These fields require basic cutover support, except they must link to
530
// imprecise durations.
531
{
532             CutoverField cf = new CutoverField
533                 (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis);
534             cf.iRangeDurationField = fields.months;
535             fields.dayOfMonth = cf;
536         }
537     }
538
539     long julianToGregorianByYear(long instant) {
540         return convertByYear(instant, iJulianChronology, iGregorianChronology);
541     }
542
543     long gregorianToJulianByYear(long instant) {
544         return convertByYear(instant, iGregorianChronology, iJulianChronology);
545     }
546
547     long julianToGregorianByWeekyear(long instant) {
548         return convertByWeekyear(instant, iJulianChronology, iGregorianChronology);
549     }
550
551     long gregorianToJulianByWeekyear(long instant) {
552         return convertByWeekyear(instant, iGregorianChronology, iJulianChronology);
553     }
554
555     //-----------------------------------------------------------------------
556
/**
557      * This basic cutover field adjusts calls to 'get' and 'set' methods, and
558      * assumes that calls to add and addWrapField are unaffected by the cutover.
559      */

560     private class CutoverField extends BaseDateTimeField {
561         private static final long serialVersionUID = 3528501219481026402L;
562
563         final DateTimeField iJulianField;
564         final DateTimeField iGregorianField;
565         final long iCutover;
566         final boolean iConvertByWeekyear;
567
568         protected DurationField iDurationField;
569         protected DurationField iRangeDurationField;
570
571         /**
572          * @param julianField field from the chronology used before the cutover instant
573          * @param gregorianField field from the chronology used at and after the cutover
574          * @param cutoverMillis the millis of the cutover
575          */

576         CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
577             this(julianField, gregorianField, cutoverMillis, false);
578         }
579
580         /**
581          * @param julianField field from the chronology used before the cutover instant
582          * @param gregorianField field from the chronology used at and after the cutover
583          * @param cutoverMillis the millis of the cutover
584          * @param convertByWeekyear
585          */

586         CutoverField(DateTimeField julianField, DateTimeField gregorianField,
587                      long cutoverMillis, boolean convertByWeekyear) {
588             super(gregorianField.getType());
589             iJulianField = julianField;
590             iGregorianField = gregorianField;
591             iCutover = cutoverMillis;
592             iConvertByWeekyear = convertByWeekyear;
593             // Although average length of Julian and Gregorian years differ,
594
// use the Gregorian duration field because it is more accurate.
595
iDurationField = gregorianField.getDurationField();
596
597             DurationField rangeField = gregorianField.getRangeDurationField();
598             if (rangeField == null) {
599                 rangeField = julianField.getRangeDurationField();
600             }
601             iRangeDurationField = rangeField;
602         }
603
604         public boolean isLenient() {
605             return false;
606         }
607
608         public int get(long instant) {
609             if (instant >= iCutover) {
610                 return iGregorianField.get(instant);
611             } else {
612                 return iJulianField.get(instant);
613             }
614         }
615
616         public String JavaDoc getAsText(long instant, Locale JavaDoc locale) {
617             if (instant >= iCutover) {
618                 return iGregorianField.getAsText(instant, locale);
619             } else {
620                 return iJulianField.getAsText(instant, locale);
621             }
622         }
623
624         public String JavaDoc getAsText(int fieldValue, Locale JavaDoc locale) {
625             return iGregorianField.getAsText(fieldValue, locale);
626         }
627
628         public String JavaDoc getAsShortText(long instant, Locale JavaDoc locale) {
629             if (instant >= iCutover) {
630                 return iGregorianField.getAsShortText(instant, locale);
631             } else {
632                 return iJulianField.getAsShortText(instant, locale);
633             }
634         }
635
636         public String JavaDoc getAsShortText(int fieldValue, Locale JavaDoc locale) {
637             return iGregorianField.getAsShortText(fieldValue, locale);
638         }
639
640         public long add(long instant, int value) {
641             return iGregorianField.add(instant, value);
642         }
643
644         public long add(long instant, long value) {
645             return iGregorianField.add(instant, value);
646         }
647
648         public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) {
649             // overridden as superclass algorithm can't handle
650
// 2004-02-29 + 48 months -> 2008-02-29 type dates
651
if (valueToAdd == 0) {
652                 return values;
653             }
654             if (DateTimeUtils.isContiguous(partial)) {
655                 long instant = 0L;
656                 for (int i = 0, isize = partial.size(); i < isize; i++) {
657                     instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]);
658                 }
659                 instant = add(instant, valueToAdd);
660                 return GJChronology.this.get(partial, instant);
661             } else {
662                 return super.add(partial, fieldIndex, values, valueToAdd);
663             }
664         }
665
666         public int getDifference(long minuendInstant, long subtrahendInstant) {
667             return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
668         }
669
670         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
671             return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
672         }
673
674         public long set(long instant, int value) {
675             if (instant >= iCutover) {
676                 instant = iGregorianField.set(instant, value);
677                 if (instant < iCutover) {
678                     // Only adjust if gap fully crossed.
679
if (instant + iGapDuration < iCutover) {
680                         instant = gregorianToJulian(instant);
681                     }
682                     // Verify that new value stuck.
683
if (get(instant) != value) {
684                         throw new IllegalFieldValueException
685                             (iGregorianField.getType(), new Integer JavaDoc(value), null, null);
686                     }
687                 }
688             } else {
689                 instant = iJulianField.set(instant, value);
690                 if (instant >= iCutover) {
691                     // Only adjust if gap fully crossed.
692
if (instant - iGapDuration >= iCutover) {
693                         instant = julianToGregorian(instant);
694                     }
695                     // Verify that new value stuck.
696
if (get(instant) != value) {
697                        throw new IllegalFieldValueException
698                             (iJulianField.getType(), new Integer JavaDoc(value), null, null);
699                     }
700                 }
701             }
702             return instant;
703         }
704
705         public long set(long instant, String JavaDoc text, Locale JavaDoc locale) {
706             if (instant >= iCutover) {
707                 instant = iGregorianField.set(instant, text, locale);
708                 if (instant < iCutover) {
709                     // Only adjust if gap fully crossed.
710
if (instant + iGapDuration < iCutover) {
711                         instant = gregorianToJulian(instant);
712                     }
713                     // Cannot verify that new value stuck because set may be lenient.
714
}
715             } else {
716                 instant = iJulianField.set(instant, text, locale);
717                 if (instant >= iCutover) {
718                     // Only adjust if gap fully crossed.
719
if (instant - iGapDuration >= iCutover) {
720                         instant = julianToGregorian(instant);
721                     }
722                     // Cannot verify that new value stuck because set may be lenient.
723
}
724             }
725             return instant;
726         }
727
728         public DurationField getDurationField() {
729             return iDurationField;
730         }
731
732         public DurationField getRangeDurationField() {
733             return iRangeDurationField;
734         }
735
736         public boolean isLeap(long instant) {
737             if (instant >= iCutover) {
738                 return iGregorianField.isLeap(instant);
739             } else {
740                 return iJulianField.isLeap(instant);
741             }
742         }
743
744         public int getLeapAmount(long instant) {
745             if (instant >= iCutover) {
746                 return iGregorianField.getLeapAmount(instant);
747             } else {
748                 return iJulianField.getLeapAmount(instant);
749             }
750         }
751
752         public DurationField getLeapDurationField() {
753             return iGregorianField.getLeapDurationField();
754         }
755
756
757         public int getMinimumValue() {
758             // For all precise fields, the Julian and Gregorian limits are
759
// identical. Choose Julian to tighten up the year limits.
760
return iJulianField.getMinimumValue();
761         }
762
763         public int getMinimumValue(ReadablePartial partial) {
764             return iJulianField.getMinimumValue(partial);
765         }
766
767         public int getMinimumValue(ReadablePartial partial, int[] values) {
768             return iJulianField.getMinimumValue(partial, values);
769         }
770
771         public int getMinimumValue(long instant) {
772             if (instant < iCutover) {
773                 return iJulianField.getMinimumValue(instant);
774             }
775
776             int min = iGregorianField.getMinimumValue(instant);
777
778             // Because the cutover may reduce the length of this field, verify
779
// the minimum by setting it.
780
instant = iGregorianField.set(instant, min);
781             if (instant < iCutover) {
782                 min = iGregorianField.get(iCutover);
783             }
784
785             return min;
786         }
787
788         public int getMaximumValue() {
789             // For all precise fields, the Julian and Gregorian limits are
790
// identical.
791
return iGregorianField.getMaximumValue();
792         }
793
794         public int getMaximumValue(long instant) {
795             if (instant >= iCutover) {
796                 return iGregorianField.getMaximumValue(instant);
797             }
798
799             int max = iJulianField.getMaximumValue(instant);
800
801             // Because the cutover may reduce the length of this field, verify
802
// the maximum by setting it.
803
instant = iJulianField.set(instant, max);
804             if (instant >= iCutover) {
805                 max = iJulianField.get(iJulianField.add(iCutover, -1));
806             }
807
808             return max;
809         }
810
811         public int getMaximumValue(ReadablePartial partial) {
812             long instant = GJChronology.getInstanceUTC().set(partial, 0L);
813             return getMaximumValue(instant);
814         }
815
816         public int getMaximumValue(ReadablePartial partial, int[] values) {
817             Chronology chrono = GJChronology.getInstanceUTC();
818             long instant = 0L;
819             for (int i = 0, isize = partial.size(); i < isize; i++) {
820                 DateTimeField field = partial.getFieldType(i).getField(chrono);
821                 if (values[i] <= field.getMaximumValue(instant)) {
822                     instant = field.set(instant, values[i]);
823                 }
824             }
825             return getMaximumValue(instant);
826         }
827
828         public long roundFloor(long instant) {
829             if (instant >= iCutover) {
830                 instant = iGregorianField.roundFloor(instant);
831                 if (instant < iCutover) {
832                     // Only adjust if gap fully crossed.
833
if (instant + iGapDuration < iCutover) {
834                         instant = gregorianToJulian(instant);
835                     }
836                 }
837             } else {
838                 instant = iJulianField.roundFloor(instant);
839             }
840             return instant;
841         }
842
843         public long roundCeiling(long instant) {
844             if (instant >= iCutover) {
845                 instant = iGregorianField.roundCeiling(instant);
846             } else {
847                 instant = iJulianField.roundCeiling(instant);
848                 if (instant >= iCutover) {
849                     // Only adjust if gap fully crossed.
850
if (instant - iGapDuration >= iCutover) {
851                         instant = julianToGregorian(instant);
852                     }
853                 }
854             }
855             return instant;
856         }
857
858         public int getMaximumTextLength(Locale JavaDoc locale) {
859             return Math.max(iJulianField.getMaximumTextLength(locale),
860                             iGregorianField.getMaximumTextLength(locale));
861         }
862
863         public int getMaximumShortTextLength(Locale JavaDoc locale) {
864             return Math.max(iJulianField.getMaximumShortTextLength(locale),
865                             iGregorianField.getMaximumShortTextLength(locale));
866         }
867
868         protected long julianToGregorian(long instant) {
869             if (iConvertByWeekyear) {
870                 return julianToGregorianByWeekyear(instant);
871             } else {
872                 return julianToGregorianByYear(instant);
873             }
874         }
875
876         protected long gregorianToJulian(long instant) {
877             if (iConvertByWeekyear) {
878                 return gregorianToJulianByWeekyear(instant);
879             } else {
880                 return gregorianToJulianByYear(instant);
881             }
882         }
883     }
884
885     //-----------------------------------------------------------------------
886
/**
887      * Cutover field for variable length fields. These fields internally call
888      * set whenever add is called. As a result, the same correction applied to
889      * set must be applied to add and addWrapField. Knowing when to use this
890      * field requires specific knowledge of how the GJ fields are implemented.
891      */

892     private final class ImpreciseCutoverField extends CutoverField {
893         private static final long serialVersionUID = 3410248757173576441L;
894
895         /**
896          * Creates a duration field that links back to this.
897          */

898         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) {
899             this(julianField, gregorianField, null, cutoverMillis, false);
900         }
901
902         /**
903          * Uses a shared duration field rather than creating a new one.
904          *
905          * @param durationField shared duration field
906          */

907         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
908                               DurationField durationField, long cutoverMillis)
909         {
910             this(julianField, gregorianField, durationField, cutoverMillis, false);
911         }
912
913         /**
914          * Uses a shared duration field rather than creating a new one.
915          *
916          * @param durationField shared duration field
917          */

918         ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField,
919                               DurationField durationField,
920                               long cutoverMillis, boolean convertByWeekyear)
921         {
922             super(julianField, gregorianField, cutoverMillis, convertByWeekyear);
923             if (durationField == null) {
924                 durationField = new LinkedDurationField(iDurationField, this);
925             }
926             iDurationField = durationField;
927         }
928
929         public long add(long instant, int value) {
930             if (instant >= iCutover) {
931                 instant = iGregorianField.add(instant, value);
932                 if (instant < iCutover) {
933                     // Only adjust if gap fully crossed.
934
if (instant + iGapDuration < iCutover) {
935                         instant = gregorianToJulian(instant);
936                     }
937                 }
938             } else {
939                 instant = iJulianField.add(instant, value);
940                 if (instant >= iCutover) {
941                     // Only adjust if gap fully crossed.
942
if (instant - iGapDuration >= iCutover) {
943                         instant = julianToGregorian(instant);
944                     }
945                 }
946             }
947             return instant;
948         }
949         
950         public long add(long instant, long value) {
951             if (instant >= iCutover) {
952                 instant = iGregorianField.add(instant, value);
953                 if (instant < iCutover) {
954                     // Only adjust if gap fully crossed.
955
if (instant + iGapDuration < iCutover) {
956                         instant = gregorianToJulian(instant);
957                     }
958                 }
959             } else {
960                 instant = iJulianField.add(instant, value);
961                 if (instant >= iCutover) {
962                     // Only adjust if gap fully crossed.
963
if (instant - iGapDuration >= iCutover) {
964                         instant = julianToGregorian(instant);
965                     }
966                 }
967             }
968             return instant;
969         }
970
971         public int getDifference(long minuendInstant, long subtrahendInstant) {
972             if (minuendInstant >= iCutover) {
973                 if (subtrahendInstant >= iCutover) {
974                     return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
975                 }
976                 // Remember, the add is being reversed. Since subtrahend is
977
// Julian, convert minuend to Julian to match.
978
minuendInstant = gregorianToJulian(minuendInstant);
979                 return iJulianField.getDifference(minuendInstant, subtrahendInstant);
980             } else {
981                 if (subtrahendInstant < iCutover) {
982                     return iJulianField.getDifference(minuendInstant, subtrahendInstant);
983                 }
984                 // Remember, the add is being reversed. Since subtrahend is
985
// Gregorian, convert minuend to Gregorian to match.
986
minuendInstant = julianToGregorian(minuendInstant);
987                 return iGregorianField.getDifference(minuendInstant, subtrahendInstant);
988             }
989         }
990
991         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
992             if (minuendInstant >= iCutover) {
993                 if (subtrahendInstant >= iCutover) {
994                     return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
995                 }
996                 // Remember, the add is being reversed. Since subtrahend is
997
// Julian, convert minuend to Julian to match.
998
minuendInstant = gregorianToJulian(minuendInstant);
999                 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1000            } else {
1001                if (subtrahendInstant < iCutover) {
1002                    return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1003                }
1004                // Remember, the add is being reversed. Since subtrahend is
1005
// Gregorian, convert minuend to Gregorian to match.
1006
minuendInstant = julianToGregorian(minuendInstant);
1007                return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1008            }
1009        }
1010
1011        // Since the imprecise fields have durations longer than the gap
1012
// duration, keep these methods simple. The inherited implementations
1013
// produce incorrect results.
1014
//
1015
// Degenerate case: If this field is a month, and the cutover is set
1016
// far into the future, then the gap duration may be so large as to
1017
// reduce the number of months in a year. If the missing month(s) are
1018
// at the beginning or end of the year, then the minimum and maximum
1019
// values are not 1 and 12. I don't expect this case to ever occur.
1020

1021        public int getMinimumValue(long instant) {
1022            if (instant >= iCutover) {
1023                return iGregorianField.getMinimumValue(instant);
1024            } else {
1025                return iJulianField.getMinimumValue(instant);
1026            }
1027        }
1028
1029        public int getMaximumValue(long instant) {
1030            if (instant >= iCutover) {
1031                return iGregorianField.getMaximumValue(instant);
1032            } else {
1033                return iJulianField.getMaximumValue(instant);
1034            }
1035        }
1036    }
1037
1038    //-----------------------------------------------------------------------
1039
/**
1040     * Links the duration back to a ImpreciseCutoverField.
1041     */

1042    private static class LinkedDurationField extends DecoratedDurationField {
1043        private static final long serialVersionUID = 4097975388007713084L;
1044
1045        private final ImpreciseCutoverField iField;
1046
1047        LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) {
1048            super(durationField, durationField.getType());
1049            iField = dateTimeField;
1050        }
1051
1052        public long add(long instant, int value) {
1053            return iField.add(instant, value);
1054        }
1055
1056        public long add(long instant, long value) {
1057            return iField.add(instant, value);
1058        }
1059
1060        public int getDifference(long minuendInstant, long subtrahendInstant) {
1061            return iField.getDifference(minuendInstant, subtrahendInstant);
1062        }
1063
1064        public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
1065            return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant);
1066        }
1067    }
1068
1069}
1070
Popular Tags