View Javadoc

1   /*
2    *  Copyright 2001-2009 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.io.IOException;
19  import java.io.ObjectInputStream;
20  import java.io.ObjectOutputStream;
21  import java.io.Serializable;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import org.joda.time.Chronology;
26  import org.joda.time.DateTimeFieldType;
27  import org.joda.time.DateTimeZone;
28  import org.joda.time.field.DividedDateTimeField;
29  import org.joda.time.field.RemainderDateTimeField;
30  
31  /**
32   * Implements a chronology that follows the rules of the ISO8601 standard,
33   * which is compatible with Gregorian for all modern dates.
34   * When ISO does not define a field, but it can be determined (such as AM/PM)
35   * it is included.
36   * <p>
37   * With the exception of century related fields, ISOChronology is exactly the
38   * same as {@link GregorianChronology}. In this chronology, centuries and year
39   * of century are zero based. For all years, the century is determined by
40   * dropping the last two digits of the year, ignoring sign. The year of century
41   * is the value of the last two year digits.
42   * <p>
43   * ISOChronology is thread-safe and immutable.
44   *
45   * @author Stephen Colebourne
46   * @author Brian S O'Neill
47   * @since 1.0
48   */
49  public final class ISOChronology extends AssembledChronology {
50      
51      /** Serialization lock */
52      private static final long serialVersionUID = -6212696554273812441L;
53  
54      /** Singleton instance of a UTC ISOChronology */
55      private static final ISOChronology INSTANCE_UTC;
56          
57      private static final int FAST_CACHE_SIZE = 64;
58  
59      /** Fast cache of zone to chronology */
60      private static final ISOChronology[] cFastCache;
61  
62      /** Cache of zone to chronology */
63      private static final Map<DateTimeZone, ISOChronology> cCache = new HashMap<DateTimeZone, ISOChronology>();
64      static {
65          cFastCache = new ISOChronology[FAST_CACHE_SIZE];
66          INSTANCE_UTC = new ISOChronology(GregorianChronology.getInstanceUTC());
67          cCache.put(DateTimeZone.UTC, INSTANCE_UTC);
68      }
69  
70      /**
71       * Gets an instance of the ISOChronology.
72       * The time zone of the returned instance is UTC.
73       * 
74       * @return a singleton UTC instance of the chronology
75       */
76      public static ISOChronology getInstanceUTC() {
77          return INSTANCE_UTC;
78      }
79  
80      /**
81       * Gets an instance of the ISOChronology in the default time zone.
82       * 
83       * @return a chronology in the default time zone
84       */
85      public static ISOChronology getInstance() {
86          return getInstance(DateTimeZone.getDefault());
87      }
88  
89      /**
90       * Gets an instance of the ISOChronology in the given time zone.
91       * 
92       * @param zone  the time zone to get the chronology in, null is default
93       * @return a chronology in the specified time zone
94       */
95      public static ISOChronology getInstance(DateTimeZone zone) {
96          if (zone == null) {
97              zone = DateTimeZone.getDefault();
98          }
99          int index = System.identityHashCode(zone) & (FAST_CACHE_SIZE - 1);
100         ISOChronology chrono = cFastCache[index];
101         if (chrono != null && chrono.getZone() == zone) {
102             return chrono;
103         }
104         synchronized (cCache) {
105             chrono = cCache.get(zone);
106             if (chrono == null) {
107                 chrono = new ISOChronology(ZonedChronology.getInstance(INSTANCE_UTC, zone));
108                 cCache.put(zone, chrono);
109             }
110         }
111         cFastCache[index] = chrono;
112         return chrono;
113     }
114 
115     // Constructors and instance variables
116     //-----------------------------------------------------------------------
117 
118     /**
119      * Restricted constructor
120      */
121     private ISOChronology(Chronology base) {
122         super(base, null);
123     }
124 
125     // Conversion
126     //-----------------------------------------------------------------------
127     /**
128      * Gets the Chronology in the UTC time zone.
129      * 
130      * @return the chronology in UTC
131      */
132     public Chronology withUTC() {
133         return INSTANCE_UTC;
134     }
135 
136     /**
137      * Gets the Chronology in a specific time zone.
138      * 
139      * @param zone  the zone to get the chronology in, null is default
140      * @return the chronology
141      */
142     public Chronology withZone(DateTimeZone zone) {
143         if (zone == null) {
144             zone = DateTimeZone.getDefault();
145         }
146         if (zone == getZone()) {
147             return this;
148         }
149         return getInstance(zone);
150     }
151 
152     // Output
153     //-----------------------------------------------------------------------
154     /**
155      * Gets a debugging toString.
156      * 
157      * @return a debugging string
158      */
159     public String toString() {
160         String str = "ISOChronology";
161         DateTimeZone zone = getZone();
162         if (zone != null) {
163             str = str + '[' + zone.getID() + ']';
164         }
165         return str;
166     }
167 
168     protected void assemble(Fields fields) {
169         if (getBase().getZone() == DateTimeZone.UTC) {
170             // Use zero based century and year of century.
171             fields.centuryOfEra = new DividedDateTimeField(
172                 ISOYearOfEraDateTimeField.INSTANCE, DateTimeFieldType.centuryOfEra(), 100);
173             fields.yearOfCentury = new RemainderDateTimeField(
174                 (DividedDateTimeField) fields.centuryOfEra, DateTimeFieldType.yearOfCentury());
175             fields.weekyearOfCentury = new RemainderDateTimeField(
176                 (DividedDateTimeField) fields.centuryOfEra, DateTimeFieldType.weekyearOfCentury());
177 
178             fields.centuries = fields.centuryOfEra.getDurationField();
179         }
180     }
181 
182     /**
183      * Checks if this chronology instance equals another.
184      * 
185      * @param obj  the object to compare to
186      * @return true if equal
187      * @since 1.6
188      */
189     public boolean equals(Object obj) {
190         return super.equals(obj);
191     }
192 
193     /**
194      * A suitable hash code for the chronology.
195      * 
196      * @return the hash code
197      * @since 1.6
198      */
199     public int hashCode() {
200         return "ISO".hashCode() * 11 + getZone().hashCode();
201     }
202 
203     /**
204      * Serialize ISOChronology instances using a small stub. This reduces the
205      * serialized size, and deserialized instances come from the cache.
206      */
207     private Object writeReplace() {
208         return new Stub(getZone());
209     }
210 
211     private static final class Stub implements Serializable {
212         private static final long serialVersionUID = -6212696554273812441L;
213 
214         private transient DateTimeZone iZone;
215 
216         Stub(DateTimeZone zone) {
217             iZone = zone;
218         }
219 
220         private Object readResolve() {
221             return ISOChronology.getInstance(iZone);
222         }
223 
224         private void writeObject(ObjectOutputStream out) throws IOException {
225             out.writeObject(iZone);
226         }
227 
228         private void readObject(ObjectInputStream in)
229             throws IOException, ClassNotFoundException
230         {
231             iZone = (DateTimeZone)in.readObject();
232         }
233     }
234 
235 }