001 /* 002 * Copyright 2001-2009 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.util.HashMap; 019 import java.util.Map; 020 021 import org.joda.time.Chronology; 022 import org.joda.time.DateTime; 023 import org.joda.time.DateTimeConstants; 024 import org.joda.time.DateTimeField; 025 import org.joda.time.DateTimeFieldType; 026 import org.joda.time.DateTimeZone; 027 import org.joda.time.field.DelegatedDateTimeField; 028 import org.joda.time.field.DividedDateTimeField; 029 import org.joda.time.field.OffsetDateTimeField; 030 import org.joda.time.field.RemainderDateTimeField; 031 import org.joda.time.field.SkipUndoDateTimeField; 032 033 /** 034 * A chronology that matches the BuddhistCalendar class supplied by Sun. 035 * <p> 036 * The chronology is identical to the Gregorian/Julian, except that the 037 * year is offset by +543 and the era is named 'BE' for Buddhist Era. 038 * <p> 039 * This class was intended by Sun to model the calendar used in Thailand. 040 * However, the actual rules for Thailand are much more involved than 041 * this class covers. (This class is accurate after 1941-01-01 ISO). 042 * <p> 043 * This chronlogy is being retained for those who want a same effect 044 * replacement for the Sun class. It is hoped that community support will 045 * enable a more accurate chronology for Thailand, to be developed. 046 * <p> 047 * BuddhistChronology is thread-safe and immutable. 048 * 049 * @author Stephen Colebourne 050 * @author Brian S O'Neill 051 * @since 1.0 052 */ 053 public final class BuddhistChronology extends AssembledChronology { 054 055 /** Serialization lock */ 056 private static final long serialVersionUID = -3474595157769370126L; 057 058 /** 059 * Constant value for 'Buddhist Era', equivalent to the value returned 060 * for AD/CE. Note that this differs from the constant in BuddhistCalendar. 061 */ 062 public static final int BE = DateTimeConstants.CE; 063 064 /** A singleton era field. */ 065 private static final DateTimeField ERA_FIELD = new BasicSingleEraDateTimeField("BE"); 066 067 /** Number of years difference in calendars. */ 068 private static final int BUDDHIST_OFFSET = 543; 069 070 /** Cache of zone to chronology */ 071 private static final Map<DateTimeZone, BuddhistChronology> cCache = new HashMap<DateTimeZone, BuddhistChronology>(); 072 073 /** UTC instance of the chronology */ 074 private static final BuddhistChronology INSTANCE_UTC = getInstance(DateTimeZone.UTC); 075 076 /** 077 * Standard instance of a Buddhist Chronology, that matches 078 * Sun's BuddhistCalendar class. This means that it follows the 079 * GregorianJulian calendar rules with a cutover date. 080 * <p> 081 * The time zone of the returned instance is UTC. 082 */ 083 public static BuddhistChronology getInstanceUTC() { 084 return INSTANCE_UTC; 085 } 086 087 /** 088 * Standard instance of a Buddhist Chronology, that matches 089 * Sun's BuddhistCalendar class. This means that it follows the 090 * GregorianJulian calendar rules with a cutover date. 091 */ 092 public static BuddhistChronology getInstance() { 093 return getInstance(DateTimeZone.getDefault()); 094 } 095 096 /** 097 * Standard instance of a Buddhist Chronology, that matches 098 * Sun's BuddhistCalendar class. This means that it follows the 099 * 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 }