001: /*
002: * Copyright 2001-2005 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.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.ObjectOutputStream;
021: import java.io.Serializable;
022: import java.util.HashMap;
023: import java.util.Map;
024:
025: import org.joda.time.Chronology;
026: import org.joda.time.DateTimeFieldType;
027: import org.joda.time.DateTimeZone;
028: import org.joda.time.field.DividedDateTimeField;
029: import org.joda.time.field.RemainderDateTimeField;
030:
031: /**
032: * Implements a chronology that follows the rules of the ISO8601 standard,
033: * which is compatible with Gregorian for all modern dates.
034: * When ISO does not define a field, but it can be determined (such as AM/PM)
035: * it is included.
036: * <p>
037: * With the exception of century related fields, ISOChronology is exactly the
038: * same as {@link GregorianChronology}. In this chronology, centuries and year
039: * of century are zero based. For all years, the century is determined by
040: * dropping the last two digits of the year, ignoring sign. The year of century
041: * is the value of the last two year digits.
042: * <p>
043: * ISOChronology is thread-safe and immutable.
044: *
045: * @author Stephen Colebourne
046: * @author Brian S O'Neill
047: * @since 1.0
048: */
049: public final class ISOChronology extends AssembledChronology {
050:
051: /** Serialization lock */
052: private static final long serialVersionUID = -6212696554273812441L;
053:
054: /** Singleton instance of a UTC ISOChronology */
055: private static final ISOChronology INSTANCE_UTC;
056:
057: private static final int FAST_CACHE_SIZE = 64;
058:
059: /** Fast cache of zone to chronology */
060: private static final ISOChronology[] cFastCache;
061:
062: /** Cache of zone to chronology */
063: private static final Map cCache = new HashMap();
064: static {
065: cFastCache = new ISOChronology[FAST_CACHE_SIZE];
066: INSTANCE_UTC = new ISOChronology(GregorianChronology
067: .getInstanceUTC());
068: cCache.put(DateTimeZone.UTC, INSTANCE_UTC);
069: }
070:
071: /**
072: * Gets an instance of the ISOChronology.
073: * The time zone of the returned instance is UTC.
074: *
075: * @return a singleton UTC instance of the chronology
076: */
077: public static ISOChronology getInstanceUTC() {
078: return INSTANCE_UTC;
079: }
080:
081: /**
082: * Gets an instance of the ISOChronology in the default time zone.
083: *
084: * @return a chronology in the default time zone
085: */
086: public static ISOChronology getInstance() {
087: return getInstance(DateTimeZone.getDefault());
088: }
089:
090: /**
091: * Gets an instance of the ISOChronology in the given time zone.
092: *
093: * @param zone the time zone to get the chronology in, null is default
094: * @return a chronology in the specified time zone
095: */
096: public static ISOChronology getInstance(DateTimeZone zone) {
097: if (zone == null) {
098: zone = DateTimeZone.getDefault();
099: }
100: int index = System.identityHashCode(zone)
101: & (FAST_CACHE_SIZE - 1);
102: ISOChronology chrono = cFastCache[index];
103: if (chrono != null && chrono.getZone() == zone) {
104: return chrono;
105: }
106: synchronized (cCache) {
107: chrono = (ISOChronology) cCache.get(zone);
108: if (chrono == null) {
109: chrono = new ISOChronology(ZonedChronology.getInstance(
110: INSTANCE_UTC, zone));
111: cCache.put(zone, chrono);
112: }
113: }
114: cFastCache[index] = chrono;
115: return chrono;
116: }
117:
118: // Constructors and instance variables
119: //-----------------------------------------------------------------------
120:
121: /**
122: * Restricted constructor
123: */
124: private ISOChronology(Chronology base) {
125: super (base, null);
126: }
127:
128: // Conversion
129: //-----------------------------------------------------------------------
130: /**
131: * Gets the Chronology in the UTC time zone.
132: *
133: * @return the chronology in UTC
134: */
135: public Chronology withUTC() {
136: return INSTANCE_UTC;
137: }
138:
139: /**
140: * Gets the Chronology in a specific time zone.
141: *
142: * @param zone the zone to get the chronology in, null is default
143: * @return the chronology
144: */
145: public Chronology withZone(DateTimeZone zone) {
146: if (zone == null) {
147: zone = DateTimeZone.getDefault();
148: }
149: if (zone == getZone()) {
150: return this ;
151: }
152: return getInstance(zone);
153: }
154:
155: // Output
156: //-----------------------------------------------------------------------
157: /**
158: * Gets a debugging toString.
159: *
160: * @return a debugging string
161: */
162: public String toString() {
163: String str = "ISOChronology";
164: DateTimeZone zone = getZone();
165: if (zone != null) {
166: str = str + '[' + zone.getID() + ']';
167: }
168: return str;
169: }
170:
171: protected void assemble(Fields fields) {
172: if (getBase().getZone() == DateTimeZone.UTC) {
173: // Use zero based century and year of century.
174: fields.centuryOfEra = new DividedDateTimeField(
175: ISOYearOfEraDateTimeField.INSTANCE,
176: DateTimeFieldType.centuryOfEra(), 100);
177: fields.yearOfCentury = new RemainderDateTimeField(
178: (DividedDateTimeField) fields.centuryOfEra,
179: DateTimeFieldType.yearOfCentury());
180: fields.weekyearOfCentury = new RemainderDateTimeField(
181: (DividedDateTimeField) fields.centuryOfEra,
182: DateTimeFieldType.weekyearOfCentury());
183:
184: fields.centuries = fields.centuryOfEra.getDurationField();
185: }
186: }
187:
188: /**
189: * Serialize ISOChronology instances using a small stub. This reduces the
190: * serialized size, and deserialized instances come from the cache.
191: */
192: private Object writeReplace() {
193: return new Stub(getZone());
194: }
195:
196: private static final class Stub implements Serializable {
197: private static final long serialVersionUID = -6212696554273812441L;
198:
199: private transient DateTimeZone iZone;
200:
201: Stub(DateTimeZone zone) {
202: iZone = zone;
203: }
204:
205: private Object readResolve() {
206: return ISOChronology.getInstance(iZone);
207: }
208:
209: private void writeObject(ObjectOutputStream out)
210: throws IOException {
211: out.writeObject(iZone);
212: }
213:
214: private void readObject(ObjectInputStream in)
215: throws IOException, ClassNotFoundException {
216: iZone = (DateTimeZone) in.readObject();
217: }
218: }
219:
220: }
|