KickJava   Java API By Example, From Geeks To Geeks.

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


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.HashMap JavaDoc;
19 import java.util.Locale JavaDoc;
20
21 import org.joda.time.Chronology;
22 import org.joda.time.DateTimeConstants;
23 import org.joda.time.DateTimeField;
24 import org.joda.time.DateTimeZone;
25 import org.joda.time.DurationField;
26 import org.joda.time.IllegalFieldValueException;
27 import org.joda.time.Instant;
28 import org.joda.time.ReadablePartial;
29 import org.joda.time.field.BaseDateTimeField;
30 import org.joda.time.field.BaseDurationField;
31 import org.joda.time.format.DateTimeFormat;
32
33 /**
34  * Wraps another Chronology to add support for time zones.
35  * <p>
36  * ZonedChronology is thread-safe and immutable.
37  *
38  * @author Brian S O'Neill
39  * @author Stephen Colebourne
40  * @since 1.0
41  */

42 public final class ZonedChronology extends AssembledChronology {
43
44     /** Serialization lock */
45     private static final long serialVersionUID = -1079258847191166848L;
46
47     /**
48      * Create a ZonedChronology for any chronology, overriding any time zone it
49      * may already have.
50      *
51      * @param base base chronology to wrap
52      * @param zone the time zone
53      * @throws IllegalArgumentException if chronology or time zone is null
54      */

55     public static ZonedChronology getInstance(Chronology base, DateTimeZone zone) {
56         if (base == null) {
57             throw new IllegalArgumentException JavaDoc("Must supply a chronology");
58         }
59         base = base.withUTC();
60         if (base == null) {
61             throw new IllegalArgumentException JavaDoc("UTC chronology must not be null");
62         }
63         if (zone == null) {
64             throw new IllegalArgumentException JavaDoc("DateTimeZone must not be null");
65         }
66         return new ZonedChronology(base, zone);
67     }
68
69     static boolean useTimeArithmetic(DurationField field) {
70         // Use time of day arithmetic rules for unit durations less than
71
// typical time zone offsets.
72
return field != null && field.getUnitMillis() < DateTimeConstants.MILLIS_PER_HOUR * 12;
73     }
74
75     /**
76      * Restricted constructor
77      *
78      * @param base base chronology to wrap
79      * @param zone the time zone
80      */

81     private ZonedChronology(Chronology base, DateTimeZone zone) {
82         super(base, zone);
83     }
84
85     public DateTimeZone getZone() {
86         return (DateTimeZone)getParam();
87     }
88
89     public Chronology withUTC() {
90         return getBase();
91     }
92
93     public Chronology withZone(DateTimeZone zone) {
94         if (zone == null) {
95             zone = DateTimeZone.getDefault();
96         }
97         if (zone == getParam()) {
98             return this;
99         }
100         if (zone == DateTimeZone.UTC) {
101             return getBase();
102         }
103         return new ZonedChronology(getBase(), zone);
104     }
105
106     public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
107                                   int millisOfDay)
108         throws IllegalArgumentException JavaDoc
109     {
110         return localToUTC(getBase().getDateTimeMillis
111                           (year, monthOfYear, dayOfMonth, millisOfDay));
112     }
113
114     public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
115                                   int hourOfDay, int minuteOfHour,
116                                   int secondOfMinute, int millisOfSecond)
117         throws IllegalArgumentException JavaDoc
118     {
119         return localToUTC(getBase().getDateTimeMillis
120                           (year, monthOfYear, dayOfMonth,
121                            hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond));
122     }
123
124     public long getDateTimeMillis(long instant,
125                                   int hourOfDay, int minuteOfHour,
126                                   int secondOfMinute, int millisOfSecond)
127         throws IllegalArgumentException JavaDoc
128     {
129         return localToUTC(getBase().getDateTimeMillis
130                           (instant + getZone().getOffset(instant),
131                            hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond));
132     }
133
134     /**
135      * @param instant instant from 1970-01-01T00:00:00 local time
136      * @return instant from 1970-01-01T00:00:00Z
137      */

138     private long localToUTC(long instant) {
139         DateTimeZone zone = getZone();
140         int offset = zone.getOffsetFromLocal(instant);
141         instant -= offset;
142         if (offset != zone.getOffset(instant)) {
143             throw new IllegalArgumentException JavaDoc
144                 ("Illegal instant due to time zone offset transition: " +
145                     DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").print(new Instant(instant)));
146         }
147         return instant;
148     }
149
150     protected void assemble(Fields fields) {
151         // Keep a local cache of converted fields so as not to create redundant
152
// objects.
153
HashMap JavaDoc converted = new HashMap JavaDoc();
154
155         // Convert duration fields...
156

157         fields.eras = convertField(fields.eras, converted);
158         fields.centuries = convertField(fields.centuries, converted);
159         fields.years = convertField(fields.years, converted);
160         fields.months = convertField(fields.months, converted);
161         fields.weekyears = convertField(fields.weekyears, converted);
162         fields.weeks = convertField(fields.weeks, converted);
163         fields.days = convertField(fields.days, converted);
164
165         fields.halfdays = convertField(fields.halfdays, converted);
166         fields.hours = convertField(fields.hours, converted);
167         fields.minutes = convertField(fields.minutes, converted);
168         fields.seconds = convertField(fields.seconds, converted);
169         fields.millis = convertField(fields.millis, converted);
170
171         // Convert datetime fields...
172

173         fields.year = convertField(fields.year, converted);
174         fields.yearOfEra = convertField(fields.yearOfEra, converted);
175         fields.yearOfCentury = convertField(fields.yearOfCentury, converted);
176         fields.centuryOfEra = convertField(fields.centuryOfEra, converted);
177         fields.era = convertField(fields.era, converted);
178         fields.dayOfWeek = convertField(fields.dayOfWeek, converted);
179         fields.dayOfMonth = convertField(fields.dayOfMonth, converted);
180         fields.dayOfYear = convertField(fields.dayOfYear, converted);
181         fields.monthOfYear = convertField(fields.monthOfYear, converted);
182         fields.weekOfWeekyear = convertField(fields.weekOfWeekyear, converted);
183         fields.weekyear = convertField(fields.weekyear, converted);
184         fields.weekyearOfCentury = convertField(fields.weekyearOfCentury, converted);
185
186         fields.millisOfSecond = convertField(fields.millisOfSecond, converted);
187         fields.millisOfDay = convertField(fields.millisOfDay, converted);
188         fields.secondOfMinute = convertField(fields.secondOfMinute, converted);
189         fields.secondOfDay = convertField(fields.secondOfDay, converted);
190         fields.minuteOfHour = convertField(fields.minuteOfHour, converted);
191         fields.minuteOfDay = convertField(fields.minuteOfDay, converted);
192         fields.hourOfDay = convertField(fields.hourOfDay, converted);
193         fields.hourOfHalfday = convertField(fields.hourOfHalfday, converted);
194         fields.clockhourOfDay = convertField(fields.clockhourOfDay, converted);
195         fields.clockhourOfHalfday = convertField(fields.clockhourOfHalfday, converted);
196         fields.halfdayOfDay = convertField(fields.halfdayOfDay, converted);
197     }
198
199     private DurationField convertField(DurationField field, HashMap JavaDoc converted) {
200         if (field == null || !field.isSupported()) {
201             return field;
202         }
203         if (converted.containsKey(field)) {
204             return (DurationField)converted.get(field);
205         }
206         ZonedDurationField zonedField = new ZonedDurationField(field, getZone());
207         converted.put(field, zonedField);
208         return zonedField;
209     }
210
211     private DateTimeField convertField(DateTimeField field, HashMap JavaDoc converted) {
212         if (field == null || !field.isSupported()) {
213             return field;
214         }
215         if (converted.containsKey(field)) {
216             return (DateTimeField)converted.get(field);
217         }
218         ZonedDateTimeField zonedField =
219             new ZonedDateTimeField(field, getZone(),
220                                    convertField(field.getDurationField(), converted),
221                                    convertField(field.getRangeDurationField(), converted),
222                                    convertField(field.getLeapDurationField(), converted));
223         converted.put(field, zonedField);
224         return zonedField;
225     }
226
227     //-----------------------------------------------------------------------
228
/**
229      * A zoned chronology is only equal to a zoned chronology with the
230      * same base chronology and zone.
231      *
232      * @param obj the object to compare to
233      * @return true if equal
234      * @since 1.4
235      */

236     public boolean equals(Object JavaDoc obj) {
237         if (this == obj) {
238             return true;
239         }
240         if (obj instanceof ZonedChronology == false) {
241             return false;
242         }
243         ZonedChronology chrono = (ZonedChronology) obj;
244         return
245             getBase().equals(chrono.getBase()) &&
246             getZone().equals(chrono.getZone());
247     }
248
249     /**
250      * A suitable hashcode for the chronology.
251      *
252      * @return the hashcode
253      * @since 1.4
254      */

255     public int hashCode() {
256         return 326565 + getZone().hashCode() * 11 + getBase().hashCode() * 7;
257     }
258
259     /**
260      * A debugging string for the chronology.
261      *
262      * @return the debugging string
263      */

264     public String JavaDoc toString() {
265         return "ZonedChronology[" + getBase() + ", " + getZone().getID() + ']';
266     }
267
268     //-----------------------------------------------------------------------
269
/*
270      * Because time durations are typically smaller than time zone offsets, the
271      * arithmetic methods subtract the original offset. This produces a more
272      * expected behavior when crossing time zone offset transitions. For dates,
273      * the new offset is subtracted off. This behavior, if applied to time
274      * fields, can nullify or reverse an add when crossing a transition.
275      */

276     static class ZonedDurationField extends BaseDurationField {
277         private static final long serialVersionUID = -485345310999208286L;
278
279         final DurationField iField;
280         final boolean iTimeField;
281         final DateTimeZone iZone;
282
283         ZonedDurationField(DurationField field, DateTimeZone zone) {
284             super(field.getType());
285             if (!field.isSupported()) {
286                 throw new IllegalArgumentException JavaDoc();
287             }
288             iField = field;
289             iTimeField = useTimeArithmetic(field);
290             iZone = zone;
291         }
292
293         public boolean isPrecise() {
294             return iTimeField ? iField.isPrecise() : this.iZone.isFixed();
295         }
296
297         public long getUnitMillis() {
298             return iField.getUnitMillis();
299         }
300
301         public int getValue(long duration, long instant) {
302             return iField.getValue(duration, addOffset(instant));
303         }
304
305         public long getValueAsLong(long duration, long instant) {
306             return iField.getValueAsLong(duration, addOffset(instant));
307         }
308
309         public long getMillis(int value, long instant) {
310             return iField.getMillis(value, addOffset(instant));
311         }
312
313         public long getMillis(long value, long instant) {
314             return iField.getMillis(value, addOffset(instant));
315         }
316
317         public long add(long instant, int value) {
318             int offset = getOffsetToAdd(instant);
319             instant = iField.add(instant + offset, value);
320             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
321         }
322
323         public long add(long instant, long value) {
324             int offset = getOffsetToAdd(instant);
325             instant = iField.add(instant + offset, value);
326             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
327         }
328
329         public int getDifference(long minuendInstant, long subtrahendInstant) {
330             int offset = getOffsetToAdd(subtrahendInstant);
331             return iField.getDifference
332                 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
333                  subtrahendInstant + offset);
334         }
335
336         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
337             int offset = getOffsetToAdd(subtrahendInstant);
338             return iField.getDifferenceAsLong
339                 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
340                  subtrahendInstant + offset);
341         }
342
343         private int getOffsetToAdd(long instant) {
344             int offset = this.iZone.getOffset(instant);
345             long sum = instant + offset;
346             // If there is a sign change, but the two values have the same sign...
347
if ((instant ^ sum) < 0 && (instant ^ offset) >= 0) {
348                 throw new ArithmeticException JavaDoc("Adding time zone offset caused overflow");
349             }
350             return offset;
351         }
352
353         private int getOffsetFromLocalToSubtract(long instant) {
354             int offset = this.iZone.getOffsetFromLocal(instant);
355             long diff = instant - offset;
356             // If there is a sign change, but the two values have different signs...
357
if ((instant ^ diff) < 0 && (instant ^ offset) < 0) {
358                 throw new ArithmeticException JavaDoc("Subtracting time zone offset caused overflow");
359             }
360             return offset;
361         }
362
363         private long addOffset(long instant) {
364             int offset = this.iZone.getOffset(instant);
365             long sum = instant + offset;
366             // If there is a sign change, but the two values have the same sign...
367
if ((instant ^ sum) < 0 && (instant ^ offset) >= 0) {
368                 throw new ArithmeticException JavaDoc("Adding time zone offset caused overflow");
369             }
370             return sum;
371         }
372     }
373
374     /**
375      * A DateTimeField that decorates another to add timezone behaviour.
376      * <p>
377      * This class converts passed in instants to local wall time, and vice
378      * versa on output.
379      */

380     static final class ZonedDateTimeField extends BaseDateTimeField {
381         private static final long serialVersionUID = -3968986277775529794L;
382
383         final DateTimeField iField;
384         final DateTimeZone iZone;
385         final DurationField iDurationField;
386         final boolean iTimeField;
387         final DurationField iRangeDurationField;
388         final DurationField iLeapDurationField;
389
390         ZonedDateTimeField(DateTimeField field,
391                            DateTimeZone zone,
392                            DurationField durationField,
393                            DurationField rangeDurationField,
394                            DurationField leapDurationField) {
395             super(field.getType());
396             if (!field.isSupported()) {
397                 throw new IllegalArgumentException JavaDoc();
398             }
399             iField = field;
400             iZone = zone;
401             iDurationField = durationField;
402             iTimeField = useTimeArithmetic(durationField);
403             iRangeDurationField = rangeDurationField;
404             iLeapDurationField = leapDurationField;
405         }
406
407         public boolean isLenient() {
408             return iField.isLenient();
409         }
410
411         public int get(long instant) {
412             return iField.get(addOffset(instant));
413         }
414
415         public String JavaDoc getAsText(long instant, Locale JavaDoc locale) {
416             return iField.getAsText(addOffset(instant), locale);
417         }
418
419         public String JavaDoc getAsShortText(long instant, Locale JavaDoc locale) {
420             return iField.getAsShortText(addOffset(instant), locale);
421         }
422
423         public String JavaDoc getAsText(int fieldValue, Locale JavaDoc locale) {
424             return iField.getAsText(fieldValue, locale);
425         }
426
427         public String JavaDoc getAsShortText(int fieldValue, Locale JavaDoc locale) {
428             return iField.getAsShortText(fieldValue, locale);
429         }
430
431         public long add(long instant, int value) {
432             int offset = getOffsetToAdd(instant);
433             instant = iField.add(instant + offset, value);
434             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
435         }
436
437         public long add(long instant, long value) {
438             int offset = getOffsetToAdd(instant);
439             instant = iField.add(instant + offset, value);
440             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
441         }
442
443         public long addWrapField(long instant, int value) {
444             int offset = getOffsetToAdd(instant);
445             instant = iField.addWrapField(instant + offset, value);
446             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
447         }
448
449         public long set(long instant, int value) {
450             long offset = getOffsetToAdd(instant);
451
452             instant = iField.set(instant + offset, value);
453             long offsetFromLocal = getOffsetFromLocalToSubtract(instant);
454             instant -= offsetFromLocal;
455
456             if (offset != offsetFromLocal) {
457                 if (get(instant) != value) {
458                     throw new IllegalFieldValueException
459                         (iField.getType(), new Integer JavaDoc(value), null, null);
460                 }
461             }
462
463             return instant;
464         }
465
466         public long set(long instant, String JavaDoc text, Locale JavaDoc locale) {
467             instant = iField.set(addOffset(instant), text, locale);
468             // Cannot verify that new value stuck because set may be lenient.
469
return instant - getOffsetFromLocalToSubtract(instant);
470         }
471
472         public int getDifference(long minuendInstant, long subtrahendInstant) {
473             int offset = getOffsetToAdd(subtrahendInstant);
474             return iField.getDifference
475                 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
476                  subtrahendInstant + offset);
477         }
478
479         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
480             int offset = getOffsetToAdd(subtrahendInstant);
481             return iField.getDifferenceAsLong
482                 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
483                  subtrahendInstant + offset);
484         }
485
486         public final DurationField getDurationField() {
487             return iDurationField;
488         }
489
490         public final DurationField getRangeDurationField() {
491             return iRangeDurationField;
492         }
493
494         public boolean isLeap(long instant) {
495             return iField.isLeap(addOffset(instant));
496         }
497
498         public int getLeapAmount(long instant) {
499             return iField.getLeapAmount(addOffset(instant));
500         }
501
502         public final DurationField getLeapDurationField() {
503             return iLeapDurationField;
504         }
505
506         public long roundFloor(long instant) {
507             int offset = getOffsetToAdd(instant);
508             instant = iField.roundFloor(instant + offset);
509             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
510         }
511
512         public long roundCeiling(long instant) {
513             int offset = getOffsetToAdd(instant);
514             instant = iField.roundCeiling(instant + offset);
515             return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
516         }
517
518         public long remainder(long instant) {
519             return iField.remainder(addOffset(instant));
520         }
521
522         public int getMinimumValue() {
523             return iField.getMinimumValue();
524         }
525
526         public int getMinimumValue(long instant) {
527             return iField.getMinimumValue(addOffset(instant));
528         }
529
530         public int getMinimumValue(ReadablePartial instant) {
531             return iField.getMinimumValue(instant);
532         }
533
534         public int getMinimumValue(ReadablePartial instant, int[] values) {
535             return iField.getMinimumValue(instant, values);
536         }
537
538         public int getMaximumValue() {
539             return iField.getMaximumValue();
540         }
541
542         public int getMaximumValue(long instant) {
543             return iField.getMaximumValue(addOffset(instant));
544         }
545
546         public int getMaximumValue(ReadablePartial instant) {
547             return iField.getMaximumValue(instant);
548         }
549
550         public int getMaximumValue(ReadablePartial instant, int[] values) {
551             return iField.getMaximumValue(instant, values);
552         }
553
554         public int getMaximumTextLength(Locale JavaDoc locale) {
555             return iField.getMaximumTextLength(locale);
556         }
557
558         public int getMaximumShortTextLength(Locale JavaDoc locale) {
559             return iField.getMaximumShortTextLength(locale);
560         }
561
562         private int getOffsetToAdd(long instant) {
563             int offset = this.iZone.getOffset(instant);
564             long sum = instant + offset;
565             // If there is a sign change, but the two values have the same sign...
566
if ((instant ^ sum) < 0 && (instant ^ offset) >= 0) {
567                 throw new ArithmeticException JavaDoc("Adding time zone offset caused overflow");
568             }
569             return offset;
570         }
571
572         private int getOffsetFromLocalToSubtract(long instant) {
573             int offset = this.iZone.getOffsetFromLocal(instant);
574             long diff = instant - offset;
575             // If there is a sign change, but the two values have different signs...
576
if ((instant ^ diff) < 0 && (instant ^ offset) < 0) {
577                 throw new ArithmeticException JavaDoc("Subtracting time zone offset caused overflow");
578             }
579             return offset;
580         }
581
582         private long addOffset(long instant) {
583             int offset = this.iZone.getOffset(instant);
584             long sum = instant + offset;
585             // If there is a sign change, but the two values have the same sign...
586
if ((instant ^ sum) < 0 && (instant ^ offset) >= 0) {
587                 throw new ArithmeticException JavaDoc("Adding time zone offset caused overflow");
588             }
589             return sum;
590         }
591     }
592
593 }
594
Popular Tags