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.DateTimeConstants; 023 import org.joda.time.DateTimeFieldType; 024 import org.joda.time.DateTimeZone; 025 import org.joda.time.IllegalFieldValueException; 026 import org.joda.time.field.SkipDateTimeField; 027 028 /** 029 * Implements a pure proleptic Julian calendar system, which defines every 030 * fourth year as leap. This implementation follows the leap year rule 031 * strictly, even for dates before 8 CE, where leap years were actually 032 * irregular. In the Julian calendar, year zero does not exist: 1 BCE is 033 * followed by 1 CE. 034 * <p> 035 * Although the Julian calendar did not exist before 45 BCE, this chronology 036 * assumes it did, thus it is proleptic. This implementation also fixes the 037 * start of the year at January 1. 038 * <p> 039 * JulianChronology is thread-safe and immutable. 040 * 041 * @see <a href="http://en.wikipedia.org/wiki/Julian_calendar">Wikipedia</a> 042 * @see GregorianChronology 043 * @see GJChronology 044 * 045 * @author Guy Allard 046 * @author Brian S O'Neill 047 * @author Stephen Colebourne 048 * @since 1.0 049 */ 050 public final class JulianChronology extends BasicGJChronology { 051 052 /** Serialization lock */ 053 private static final long serialVersionUID = -8731039522547897247L; 054 055 private static final long MILLIS_PER_YEAR = 056 (long) (365.25 * DateTimeConstants.MILLIS_PER_DAY); 057 058 private static final long MILLIS_PER_MONTH = 059 (long) (365.25 * DateTimeConstants.MILLIS_PER_DAY / 12); 060 061 /** The lowest year that can be fully supported. */ 062 private static final int MIN_YEAR = -292269054; 063 064 /** The highest year that can be fully supported. */ 065 private static final int MAX_YEAR = 292272992; 066 067 /** Singleton instance of a UTC JulianChronology */ 068 private static final JulianChronology INSTANCE_UTC; 069 070 /** Cache of zone to chronology arrays */ 071 private static final Map<DateTimeZone, JulianChronology[]> cCache = new HashMap<DateTimeZone, JulianChronology[]>(); 072 073 static { 074 INSTANCE_UTC = getInstance(DateTimeZone.UTC); 075 } 076 077 static int adjustYearForSet(int year) { 078 if (year <= 0) { 079 if (year == 0) { 080 throw new IllegalFieldValueException 081 (DateTimeFieldType.year(), Integer.valueOf(year), null, null); 082 } 083 year++; 084 } 085 return year; 086 } 087 088 /** 089 * Gets an instance of the JulianChronology. 090 * The time zone of the returned instance is UTC. 091 * 092 * @return a singleton UTC instance of the chronology 093 */ 094 public static JulianChronology getInstanceUTC() { 095 return INSTANCE_UTC; 096 } 097 098 /** 099 * Gets an instance of the JulianChronology in the default time zone. 100 * 101 * @return a chronology in the default time zone 102 */ 103 public static JulianChronology getInstance() { 104 return getInstance(DateTimeZone.getDefault(), 4); 105 } 106 107 /** 108 * Gets an instance of the JulianChronology in the given time zone. 109 * 110 * @param zone the time zone to get the chronology in, null is default 111 * @return a chronology in the specified time zone 112 */ 113 public static JulianChronology getInstance(DateTimeZone zone) { 114 return getInstance(zone, 4); 115 } 116 117 /** 118 * Gets an instance of the JulianChronology in the given time zone. 119 * 120 * @param zone the time zone to get the chronology in, null is default 121 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4 122 * @return a chronology in the specified time zone 123 */ 124 public static JulianChronology getInstance(DateTimeZone zone, int minDaysInFirstWeek) { 125 if (zone == null) { 126 zone = DateTimeZone.getDefault(); 127 } 128 JulianChronology chrono; 129 synchronized (cCache) { 130 JulianChronology[] chronos = cCache.get(zone); 131 if (chronos == null) { 132 chronos = new JulianChronology[7]; 133 cCache.put(zone, chronos); 134 } 135 try { 136 chrono = chronos[minDaysInFirstWeek - 1]; 137 } catch (ArrayIndexOutOfBoundsException e) { 138 throw new IllegalArgumentException 139 ("Invalid min days in first week: " + minDaysInFirstWeek); 140 } 141 if (chrono == null) { 142 if (zone == DateTimeZone.UTC) { 143 chrono = new JulianChronology(null, null, minDaysInFirstWeek); 144 } else { 145 chrono = getInstance(DateTimeZone.UTC, minDaysInFirstWeek); 146 chrono = new JulianChronology 147 (ZonedChronology.getInstance(chrono, zone), null, minDaysInFirstWeek); 148 } 149 chronos[minDaysInFirstWeek - 1] = chrono; 150 } 151 } 152 return chrono; 153 } 154 155 // Constructors and instance variables 156 //----------------------------------------------------------------------- 157 158 /** 159 * Restricted constructor 160 */ 161 JulianChronology(Chronology base, Object param, int minDaysInFirstWeek) { 162 super(base, param, minDaysInFirstWeek); 163 } 164 165 /** 166 * Serialization singleton 167 */ 168 private Object readResolve() { 169 Chronology base = getBase(); 170 int minDays = getMinimumDaysInFirstWeek(); 171 minDays = (minDays == 0 ? 4 : minDays); // handle rename of BaseGJChronology 172 return base == null ? 173 getInstance(DateTimeZone.UTC, minDays) : 174 getInstance(base.getZone(), minDays); 175 } 176 177 // Conversion 178 //----------------------------------------------------------------------- 179 /** 180 * Gets the Chronology in the UTC time zone. 181 * 182 * @return the chronology in UTC 183 */ 184 public Chronology withUTC() { 185 return INSTANCE_UTC; 186 } 187 188 /** 189 * Gets the Chronology in a specific time zone. 190 * 191 * @param zone the zone to get the chronology in, null is default 192 * @return the chronology 193 */ 194 public Chronology withZone(DateTimeZone zone) { 195 if (zone == null) { 196 zone = DateTimeZone.getDefault(); 197 } 198 if (zone == getZone()) { 199 return this; 200 } 201 return getInstance(zone); 202 } 203 204 long getDateMidnightMillis(int year, int monthOfYear, int dayOfMonth) 205 throws IllegalArgumentException 206 { 207 return super.getDateMidnightMillis(adjustYearForSet(year), monthOfYear, dayOfMonth); 208 } 209 210 boolean isLeapYear(int year) { 211 return (year & 3) == 0; 212 } 213 214 long calculateFirstDayOfYearMillis(int year) { 215 // Java epoch is 1970-01-01 Gregorian which is 1969-12-19 Julian. 216 // Calculate relative to the nearest leap year and account for the 217 // difference later. 218 219 int relativeYear = year - 1968; 220 int leapYears; 221 if (relativeYear <= 0) { 222 // Add 3 before shifting right since /4 and >>2 behave differently 223 // on negative numbers. 224 leapYears = (relativeYear + 3) >> 2; 225 } else { 226 leapYears = relativeYear >> 2; 227 // For post 1968 an adjustment is needed as jan1st is before leap day 228 if (!isLeapYear(year)) { 229 leapYears++; 230 } 231 } 232 233 long millis = (relativeYear * 365L + leapYears) * (long)DateTimeConstants.MILLIS_PER_DAY; 234 235 // Adjust to account for difference between 1968-01-01 and 1969-12-19. 236 237 return millis - (366L + 352) * DateTimeConstants.MILLIS_PER_DAY; 238 } 239 240 int getMinYear() { 241 return MIN_YEAR; 242 } 243 244 int getMaxYear() { 245 return MAX_YEAR; 246 } 247 248 long getAverageMillisPerYear() { 249 return MILLIS_PER_YEAR; 250 } 251 252 long getAverageMillisPerYearDividedByTwo() { 253 return MILLIS_PER_YEAR / 2; 254 } 255 256 long getAverageMillisPerMonth() { 257 return MILLIS_PER_MONTH; 258 } 259 260 long getApproxMillisAtEpochDividedByTwo() { 261 return (1969L * MILLIS_PER_YEAR + 352L * DateTimeConstants.MILLIS_PER_DAY) / 2; 262 } 263 264 protected void assemble(Fields fields) { 265 if (getBase() == null) { 266 super.assemble(fields); 267 // Julian chronology has no year zero. 268 fields.year = new SkipDateTimeField(this, fields.year); 269 fields.weekyear = new SkipDateTimeField(this, fields.weekyear); 270 } 271 } 272 273 }