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.DateTimeZone; 024 025 /** 026 * Implements a pure proleptic Gregorian calendar system, which defines every 027 * fourth year as leap, unless the year is divisible by 100 and not by 400. 028 * This improves upon the Julian calendar leap year rule. 029 * <p> 030 * Although the Gregorian calendar did not exist before 1582 CE, this 031 * chronology assumes it did, thus it is proleptic. This implementation also 032 * fixes the start of the year at January 1, and defines the year zero. 033 * <p> 034 * GregorianChronology is thread-safe and immutable. 035 * 036 * @see <a href="http://en.wikipedia.org/wiki/Gregorian_calendar">Wikipedia</a> 037 * @see JulianChronology 038 * @see GJChronology 039 * 040 * @author Guy Allard 041 * @author Stephen Colebourne 042 * @author Brian S O'Neill 043 * @since 1.0 044 */ 045 public final class GregorianChronology extends BasicGJChronology { 046 047 /** Serialization lock */ 048 private static final long serialVersionUID = -861407383323710522L; 049 050 private static final long MILLIS_PER_YEAR = 051 (long) (365.2425 * DateTimeConstants.MILLIS_PER_DAY); 052 053 private static final long MILLIS_PER_MONTH = 054 (long) (365.2425 * DateTimeConstants.MILLIS_PER_DAY / 12); 055 056 private static final int DAYS_0000_TO_1970 = 719527; 057 058 /** The lowest year that can be fully supported. */ 059 private static final int MIN_YEAR = -292275054; 060 061 /** The highest year that can be fully supported. */ 062 private static final int MAX_YEAR = 292278993; 063 064 /** Singleton instance of a UTC GregorianChronology */ 065 private static final GregorianChronology INSTANCE_UTC; 066 067 /** Cache of zone to chronology arrays */ 068 private static final Map<DateTimeZone, GregorianChronology[]> cCache = new HashMap<DateTimeZone, GregorianChronology[]>(); 069 070 static { 071 INSTANCE_UTC = getInstance(DateTimeZone.UTC); 072 } 073 074 /** 075 * Gets an instance of the GregorianChronology. 076 * The time zone of the returned instance is UTC. 077 * 078 * @return a singleton UTC instance of the chronology 079 */ 080 public static GregorianChronology getInstanceUTC() { 081 return INSTANCE_UTC; 082 } 083 084 /** 085 * Gets an instance of the GregorianChronology in the default time zone. 086 * 087 * @return a chronology in the default time zone 088 */ 089 public static GregorianChronology getInstance() { 090 return getInstance(DateTimeZone.getDefault(), 4); 091 } 092 093 /** 094 * Gets an instance of the GregorianChronology in the given time zone. 095 * 096 * @param zone the time zone to get the chronology in, null is default 097 * @return a chronology in the specified time zone 098 */ 099 public static GregorianChronology getInstance(DateTimeZone zone) { 100 return getInstance(zone, 4); 101 } 102 103 /** 104 * Gets an instance of the GregorianChronology in the given time zone. 105 * 106 * @param zone the time zone to get the chronology in, null is default 107 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4 108 * @return a chronology in the specified time zone 109 */ 110 public static GregorianChronology getInstance(DateTimeZone zone, int minDaysInFirstWeek) { 111 if (zone == null) { 112 zone = DateTimeZone.getDefault(); 113 } 114 GregorianChronology chrono; 115 synchronized (cCache) { 116 GregorianChronology[] chronos = cCache.get(zone); 117 if (chronos == null) { 118 chronos = new GregorianChronology[7]; 119 cCache.put(zone, chronos); 120 } 121 try { 122 chrono = chronos[minDaysInFirstWeek - 1]; 123 } catch (ArrayIndexOutOfBoundsException e) { 124 throw new IllegalArgumentException 125 ("Invalid min days in first week: " + minDaysInFirstWeek); 126 } 127 if (chrono == null) { 128 if (zone == DateTimeZone.UTC) { 129 chrono = new GregorianChronology(null, null, minDaysInFirstWeek); 130 } else { 131 chrono = getInstance(DateTimeZone.UTC, minDaysInFirstWeek); 132 chrono = new GregorianChronology 133 (ZonedChronology.getInstance(chrono, zone), null, minDaysInFirstWeek); 134 } 135 chronos[minDaysInFirstWeek - 1] = chrono; 136 } 137 } 138 return chrono; 139 } 140 141 // Constructors and instance variables 142 //----------------------------------------------------------------------- 143 144 /** 145 * Restricted constructor 146 */ 147 private GregorianChronology(Chronology base, Object param, int minDaysInFirstWeek) { 148 super(base, param, minDaysInFirstWeek); 149 } 150 151 /** 152 * Serialization singleton 153 */ 154 private Object readResolve() { 155 Chronology base = getBase(); 156 int minDays = getMinimumDaysInFirstWeek(); 157 minDays = (minDays == 0 ? 4 : minDays); // handle rename of BaseGJChronology 158 return base == null ? 159 getInstance(DateTimeZone.UTC, minDays) : 160 getInstance(base.getZone(), minDays); 161 } 162 163 // Conversion 164 //----------------------------------------------------------------------- 165 /** 166 * Gets the Chronology in the UTC time zone. 167 * 168 * @return the chronology in UTC 169 */ 170 public Chronology withUTC() { 171 return INSTANCE_UTC; 172 } 173 174 /** 175 * Gets the Chronology in a specific time zone. 176 * 177 * @param zone the zone to get the chronology in, null is default 178 * @return the chronology 179 */ 180 public Chronology withZone(DateTimeZone zone) { 181 if (zone == null) { 182 zone = DateTimeZone.getDefault(); 183 } 184 if (zone == getZone()) { 185 return this; 186 } 187 return getInstance(zone); 188 } 189 190 protected void assemble(Fields fields) { 191 if (getBase() == null) { 192 super.assemble(fields); 193 } 194 } 195 196 boolean isLeapYear(int year) { 197 return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0); 198 } 199 200 long calculateFirstDayOfYearMillis(int year) { 201 // Initial value is just temporary. 202 int leapYears = year / 100; 203 if (year < 0) { 204 // Add 3 before shifting right since /4 and >>2 behave differently 205 // on negative numbers. When the expression is written as 206 // (year / 4) - (year / 100) + (year / 400), 207 // it works for both positive and negative values, except this optimization 208 // eliminates two divisions. 209 leapYears = ((year + 3) >> 2) - leapYears + ((leapYears + 3) >> 2) - 1; 210 } else { 211 leapYears = (year >> 2) - leapYears + (leapYears >> 2); 212 if (isLeapYear(year)) { 213 leapYears--; 214 } 215 } 216 217 return (year * 365L + (leapYears - DAYS_0000_TO_1970)) * DateTimeConstants.MILLIS_PER_DAY; 218 } 219 220 int getMinYear() { 221 return MIN_YEAR; 222 } 223 224 int getMaxYear() { 225 return MAX_YEAR; 226 } 227 228 long getAverageMillisPerYear() { 229 return MILLIS_PER_YEAR; 230 } 231 232 long getAverageMillisPerYearDividedByTwo() { 233 return MILLIS_PER_YEAR / 2; 234 } 235 236 long getAverageMillisPerMonth() { 237 return MILLIS_PER_MONTH; 238 } 239 240 long getApproxMillisAtEpochDividedByTwo() { 241 return (1970L * MILLIS_PER_YEAR) / 2; 242 } 243 244 }