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.util.HashMap;
19  import java.util.Map;
20  
21  import org.joda.time.Chronology;
22  import org.joda.time.DateTime;
23  import org.joda.time.DateTimeConstants;
24  import org.joda.time.DateTimeField;
25  import org.joda.time.DateTimeFieldType;
26  import org.joda.time.DateTimeZone;
27  import org.joda.time.field.DelegatedDateTimeField;
28  import org.joda.time.field.DividedDateTimeField;
29  import org.joda.time.field.OffsetDateTimeField;
30  import org.joda.time.field.RemainderDateTimeField;
31  import org.joda.time.field.SkipUndoDateTimeField;
32  
33  /**
34   * A chronology that matches the BuddhistCalendar class supplied by Sun.
35   * <p>
36   * The chronology is identical to the Gregorian/Julian, except that the
37   * year is offset by +543 and the era is named 'BE' for Buddhist Era.
38   * <p>
39   * This class was intended by Sun to model the calendar used in Thailand.
40   * However, the actual rules for Thailand are much more involved than
41   * this class covers. (This class is accurate after 1941-01-01 ISO).
42   * <p>
43   * This chronlogy is being retained for those who want a same effect
44   * replacement for the Sun class. It is hoped that community support will
45   * enable a more accurate chronology for Thailand, to be developed.
46   * <p>
47   * BuddhistChronology is thread-safe and immutable.
48   *
49   * @author Stephen Colebourne
50   * @author Brian S O'Neill
51   * @since 1.0
52   */
53  public final class BuddhistChronology extends AssembledChronology {
54      
55      /** Serialization lock */
56      private static final long serialVersionUID = -3474595157769370126L;
57  
58      /**
59       * Constant value for 'Buddhist Era', equivalent to the value returned
60       * for AD/CE. Note that this differs from the constant in BuddhistCalendar.
61       */
62      public static final int BE = DateTimeConstants.CE;
63  
64      /** A singleton era field. */
65      private static final DateTimeField ERA_FIELD = new BasicSingleEraDateTimeField("BE");
66  
67      /** Number of years difference in calendars. */
68      private static final int BUDDHIST_OFFSET = 543;
69  
70      /** Cache of zone to chronology */
71      private static final Map<DateTimeZone, BuddhistChronology> cCache = new HashMap<DateTimeZone, BuddhistChronology>();
72  
73      /** UTC instance of the chronology */
74      private static final BuddhistChronology INSTANCE_UTC = getInstance(DateTimeZone.UTC);
75  
76      /**
77       * Standard instance of a Buddhist Chronology, that matches
78       * Sun's BuddhistCalendar class. This means that it follows the
79       * GregorianJulian calendar rules with a cutover date.
80       * <p>
81       * The time zone of the returned instance is UTC.
82       */
83      public static BuddhistChronology getInstanceUTC() {
84          return INSTANCE_UTC;
85      }
86  
87      /**
88       * Standard instance of a Buddhist Chronology, that matches
89       * Sun's BuddhistCalendar class. This means that it follows the
90       * GregorianJulian calendar rules with a cutover date.
91       */
92      public static BuddhistChronology getInstance() {
93          return getInstance(DateTimeZone.getDefault());
94      }
95  
96      /**
97       * Standard instance of a Buddhist Chronology, that matches
98       * Sun's BuddhistCalendar class. This means that it follows the
99       * GregorianJulian calendar rules with a cutover date.
100      *
101      * @param zone  the time zone to use, null is default
102      */
103     public static synchronized BuddhistChronology getInstance(DateTimeZone zone) {
104         if (zone == null) {
105             zone = DateTimeZone.getDefault();
106         }
107         BuddhistChronology chrono = cCache.get(zone);
108         if (chrono == null) {
109             // First create without a lower limit.
110             chrono = new BuddhistChronology(GJChronology.getInstance(zone, null), null);
111             // Impose lower limit and make another BuddhistChronology.
112             DateTime lowerLimit = new DateTime(1, 1, 1, 0, 0, 0, 0, chrono);
113             chrono = new BuddhistChronology(LimitChronology.getInstance(chrono, lowerLimit, null), "");
114             cCache.put(zone, chrono);
115         }
116         return chrono;
117     }
118 
119     // Constructors and instance variables
120     //-----------------------------------------------------------------------
121     
122     /**
123      * Restricted constructor.
124      *
125      * @param param if non-null, then don't change the field set
126      */
127     private BuddhistChronology(Chronology base, Object param) {
128         super(base, param);
129     }
130 
131     /**
132      * Serialization singleton
133      */
134     private Object readResolve() {
135         Chronology base = getBase();
136         return base == null ? getInstanceUTC() : getInstance(base.getZone());
137     }
138 
139     // Conversion
140     //-----------------------------------------------------------------------
141     /**
142      * Gets the Chronology in the UTC time zone.
143      * 
144      * @return the chronology in UTC
145      */
146     public Chronology withUTC() {
147         return INSTANCE_UTC;
148     }
149 
150     /**
151      * Gets the Chronology in a specific time zone.
152      * 
153      * @param zone  the zone to get the chronology in, null is default
154      * @return the chronology
155      */
156     public Chronology withZone(DateTimeZone zone) {
157         if (zone == null) {
158             zone = DateTimeZone.getDefault();
159         }
160         if (zone == getZone()) {
161             return this;
162         }
163         return getInstance(zone);
164     }
165 
166     /**
167      * Checks if this chronology instance equals another.
168      * 
169      * @param obj  the object to compare to
170      * @return true if equal
171      * @since 1.6
172      */
173     public boolean equals(Object obj) {
174         return super.equals(obj);
175     }
176 
177     /**
178      * A suitable hash code for the chronology.
179      * 
180      * @return the hash code
181      * @since 1.6
182      */
183     public int hashCode() {
184         return "Buddhist".hashCode() * 11 + getZone().hashCode();
185     }
186 
187     // Output
188     //-----------------------------------------------------------------------
189     /**
190      * Gets a debugging toString.
191      * 
192      * @return a debugging string
193      */
194     public String toString() {
195         String str = "BuddhistChronology";
196         DateTimeZone zone = getZone();
197         if (zone != null) {
198             str = str + '[' + zone.getID() + ']';
199         }
200         return str;
201     }
202 
203     protected void assemble(Fields fields) {
204         if (getParam() == null) {
205             // julian chrono removed zero, but we need to put it back
206             DateTimeField field = fields.year;
207             fields.year = new OffsetDateTimeField(
208                     new SkipUndoDateTimeField(this, field), BUDDHIST_OFFSET);
209             
210             // one era, so yearOfEra is the same
211             field = fields.yearOfEra;
212             fields.yearOfEra = new DelegatedDateTimeField(
213                 fields.year, DateTimeFieldType.yearOfEra());
214             
215             // julian chrono removed zero, but we need to put it back
216             field = fields.weekyear;
217             fields.weekyear = new OffsetDateTimeField(
218                     new SkipUndoDateTimeField(this, field), BUDDHIST_OFFSET);
219             
220             field = new OffsetDateTimeField(fields.yearOfEra, 99);
221             fields.centuryOfEra = new DividedDateTimeField(
222                 field, DateTimeFieldType.centuryOfEra(), 100);
223             
224             field = new RemainderDateTimeField(
225                 (DividedDateTimeField) fields.centuryOfEra);
226             fields.yearOfCentury = new OffsetDateTimeField(
227                 field, DateTimeFieldType.yearOfCentury(), 1);
228             
229             field = new RemainderDateTimeField(
230                 fields.weekyear, DateTimeFieldType.weekyearOfCentury(), 100);
231             fields.weekyearOfCentury = new OffsetDateTimeField(
232                 field, DateTimeFieldType.weekyearOfCentury(), 1);
233             
234             fields.era = ERA_FIELD;
235         }
236     }
237    
238 }