001: /*
002: * Copyright 2001-2005 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.gj;
017:
018: import org.joda.time.Chronology;
019: import org.joda.time.DateTimeField;
020: import org.joda.time.DateTimeZone;
021: import org.joda.time.DurationField;
022: import org.joda.time.chrono.BaseChronology;
023:
024: /**
025: * A reference Gregorian/Julian chronology implementation, intended for testing
026: * purposes only. Correctness is favored over performance. The key functions
027: * for date calculations are based on ones provided in "Calendrical
028: * Calculations", ISBN 0-521-77752-6.
029: *
030: * <p>In theory, this class can be used to test any other Gregorian/Julian
031: * chronology as long as almost all datetime fields are implemented differently
032: * between the two. Fields that would most likely be implemented the same are
033: * not supported by this class.
034: *
035: * <p>Unsupported features
036: * <ul>
037: * <li>time zones
038: * <li>time of day
039: * <li>year of era
040: * <li>year of century
041: * <li>century of era
042: * <li>era
043: * </ul>
044: *
045: * @author Brian S O'Neill
046: */
047: abstract class TestGJChronology extends BaseChronology {
048: static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
049:
050: /**
051: * Divide with round-negative behavior.
052: *
053: * @param divisor must be positive
054: */
055: static long div(long dividend, long divisor) {
056: if (divisor < 0) {
057: throw new IllegalArgumentException(
058: "divisor must be positive: " + divisor);
059: }
060: if (dividend >= 0) {
061: return dividend / divisor;
062: } else {
063: return (dividend + 1) / divisor - 1;
064: }
065: }
066:
067: /**
068: * Modulus with round-negative behavior, result is always positive.
069: *
070: * @param divisor must be positive
071: */
072: static long mod(long dividend, long divisor) {
073: if (divisor < 0) {
074: throw new IllegalArgumentException(
075: "divisor must be positive: " + divisor);
076: }
077: if (dividend >= 0) {
078: return dividend % divisor;
079: } else {
080: return (dividend + 1) % divisor - 1 + divisor;
081: }
082: }
083:
084: static long amod(long dividend, long divisor) {
085: long mod = mod(dividend, divisor);
086: return (mod == 0) ? divisor : mod;
087: }
088:
089: /** Milliseconds from 0001-01-01 to the epoch. */
090: private final long iEpochMillis;
091:
092: public TestGJChronology(int epochYear, int epochMonth, int epochDay) {
093: iEpochMillis = fixedFromGJ(epochYear, epochMonth, epochDay)
094: * MILLIS_PER_DAY;
095: }
096:
097: public DateTimeZone getZone() {
098: return null;
099: }
100:
101: public Chronology withUTC() {
102: return this ;
103: }
104:
105: /**
106: * Unsupported.
107: */
108: public Chronology withZone(DateTimeZone zone) {
109: throw new UnsupportedOperationException();
110: }
111:
112: long getTimeOnlyMillis(long millis) {
113: return mod(millis, MILLIS_PER_DAY);
114: }
115:
116: long getDateOnlyMillis(long millis) {
117: return millis - mod(millis, MILLIS_PER_DAY);
118: }
119:
120: public DurationField days() {
121: return dayOfWeek().getDurationField();
122: }
123:
124: public DateTimeField dayOfWeek() {
125: return new TestGJDayOfWeekField(this );
126: }
127:
128: public DateTimeField dayOfMonth() {
129: return new TestGJDayOfMonthField(this );
130: }
131:
132: public DateTimeField dayOfYear() {
133: return new TestGJDayOfYearField(this );
134: }
135:
136: public DurationField weeks() {
137: return weekOfWeekyear().getDurationField();
138: }
139:
140: public DateTimeField weekOfWeekyear() {
141: return new TestGJWeekOfWeekyearField(this );
142: }
143:
144: public DurationField weekyears() {
145: return weekyear().getDurationField();
146: }
147:
148: public DateTimeField weekyear() {
149: return new TestGJWeekyearField(this );
150: }
151:
152: public DurationField months() {
153: return monthOfYear().getDurationField();
154: }
155:
156: public DateTimeField monthOfYear() {
157: return new TestGJMonthOfYearField(this );
158: }
159:
160: public DurationField years() {
161: return year().getDurationField();
162: }
163:
164: public DateTimeField year() {
165: return new TestGJYearField(this );
166: }
167:
168: abstract long millisPerYear();
169:
170: abstract long millisPerMonth();
171:
172: abstract boolean isLeapYear(int year);
173:
174: /**
175: * @return days from 0001-01-01
176: */
177: abstract long fixedFromGJ(int year, int monthOfYear, int dayOfMonth);
178:
179: /**
180: * @param date days from 0001-01-01
181: * @return gj year
182: */
183: abstract int gjYearFromFixed(long date);
184:
185: /**
186: * @param date days from 0001-01-01
187: * @return gj year, monthOfYear, dayOfMonth
188: */
189: abstract int[] gjFromFixed(long date);
190:
191: abstract long fixedFromISO(int weekyear, int weekOfWeekyear,
192: int dayOfWeek);
193:
194: /**
195: * @param date days from 0001-01-01
196: * @return iso weekyear, weekOfWeekyear, dayOfWeek (1=Monday to 7)
197: */
198: abstract int[] isoFromFixed(long date);
199:
200: /**
201: * @param millis milliseconds from epoch
202: * @return days from 0001-01-01
203: */
204: long fixedFromMillis(long millis) {
205: return div(millis + iEpochMillis, MILLIS_PER_DAY);
206: }
207:
208: /**
209: * @param fixed days from 0001-01-01
210: * @return milliseconds from epoch
211: */
212: long millisFromFixed(long fixed) {
213: return fixed * MILLIS_PER_DAY - iEpochMillis;
214: }
215:
216: /**
217: * @return milliseconds from epoch
218: */
219: long millisFromGJ(int year, int monthOfYear, int dayOfMonth) {
220: return millisFromFixed(fixedFromGJ(year, monthOfYear,
221: dayOfMonth));
222: }
223:
224: /**
225: * @param millis milliseconds from epoch
226: * @return gj year
227: */
228: int gjYearFromMillis(long millis) {
229: return gjYearFromFixed(fixedFromMillis(millis));
230: }
231:
232: /**
233: * @param millis milliseconds from epoch
234: * @return gj year, monthOfYear, dayOfMonth
235: */
236: int[] gjFromMillis(long millis) {
237: return gjFromFixed(fixedFromMillis(millis));
238: }
239:
240: /**
241: * @return milliseconds from epoch
242: */
243: long millisFromISO(int weekyear, int weekOfWeekyear, int dayOfWeek) {
244: return millisFromFixed(fixedFromISO(weekyear, weekOfWeekyear,
245: dayOfWeek));
246: }
247:
248: /**
249: * @param millis milliseconds from epoch
250: * @return iso weekyear, weekOfWeekyear, dayOfWeek (1=Monday to 7)
251: */
252: int[] isoFromMillis(long millis) {
253: return isoFromFixed(fixedFromMillis(millis));
254: }
255:
256: /**
257: * @param date days from 0001-01-01
258: * @param weekday 0=Sunday, 1=Monday, 2=Tuesday ... 6=Saturday, 7=Sunday
259: * @param date days from 0001-01-01, on or before weekday
260: */
261: long weekdayOnOrBefore(long date, int weekday) {
262: return date - mod(date - mod(weekday, 7), 7);
263: }
264:
265: long weekdayOnOrAfter(long date, int weekday) {
266: return weekdayOnOrBefore(date + 6, weekday);
267: }
268:
269: long weekdayNearest(long date, int weekday) {
270: return weekdayOnOrBefore(date + 3, weekday);
271: }
272:
273: long weekdayBefore(long date, int weekday) {
274: return weekdayOnOrBefore(date - 1, weekday);
275: }
276:
277: long weekdayAfter(long date, int weekday) {
278: return weekdayOnOrBefore(date + 7, weekday);
279: }
280:
281: long nthWeekday(int n, int weekday, int year, int monthOfYear,
282: int dayOfMonth) {
283: if (n > 0) {
284: return 7
285: * n
286: + weekdayBefore(fixedFromGJ(year, monthOfYear,
287: dayOfMonth), weekday);
288: } else {
289: return 7
290: * n
291: + weekdayAfter(fixedFromGJ(year, monthOfYear,
292: dayOfMonth), weekday);
293: }
294: }
295:
296: long firstWeekday(int weekday, int year, int monthOfYear,
297: int dayOfMonth) {
298: return nthWeekday(1, weekday, year, monthOfYear, dayOfMonth);
299: }
300:
301: long lastWeekday(int weekday, int year, int monthOfYear,
302: int dayOfMonth) {
303: return nthWeekday(-1, weekday, year, monthOfYear, dayOfMonth);
304: }
305: }
|