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; |
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 cCache = new HashMap(); |
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 = (BuddhistChronology) 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 | } |