View Javadoc

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.gj;
17  
18  import org.joda.time.Chronology;
19  import org.joda.time.DateTimeField;
20  import org.joda.time.DateTimeZone;
21  import org.joda.time.DurationField;
22  import org.joda.time.chrono.BaseChronology;
23  
24  /**
25   * A reference Gregorian/Julian chronology implementation, intended for testing
26   * purposes only. Correctness is favored over performance. The key functions
27   * for date calculations are based on ones provided in "Calendrical
28   * Calculations", ISBN 0-521-77752-6.
29   *
30   * <p>In theory, this class can be used to test any other Gregorian/Julian
31   * chronology as long as almost all datetime fields are implemented differently
32   * between the two. Fields that would most likely be implemented the same are
33   * not supported by this class.
34   *
35   * <p>Unsupported features
36   * <ul>
37   * <li>time zones
38   * <li>time of day
39   * <li>year of era
40   * <li>year of century
41   * <li>century of era
42   * <li>era
43   * </ul>
44   *
45   * @author Brian S O'Neill
46   */
47  abstract class TestGJChronology extends BaseChronology {
48      static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
49  
50      /**
51       * Divide with round-negative behavior.
52       *
53       * @param divisor must be positive
54       */
55      static long div(long dividend, long divisor) {
56          if (divisor < 0) {
57              throw new IllegalArgumentException("divisor must be positive: " + divisor);
58          }
59          if (dividend >= 0) {
60              return dividend / divisor;
61          } else {
62              return (dividend + 1) / divisor - 1;
63          }
64      }
65  
66      /**
67       * Modulus with round-negative behavior, result is always positive.
68       *
69       * @param divisor must be positive
70       */
71      static long mod(long dividend, long divisor) {
72          if (divisor < 0) {
73              throw new IllegalArgumentException("divisor must be positive: " + divisor);
74          }
75          if (dividend >= 0) {
76              return dividend % divisor;
77          } else {
78              return (dividend + 1) % divisor - 1 + divisor;
79          }
80      }
81  
82      static long amod(long dividend, long divisor) {
83          long mod = mod(dividend, divisor);
84          return (mod == 0) ? divisor : mod;
85      }
86  
87      /** Milliseconds from 0001-01-01 to the epoch. */
88      private final long iEpochMillis;
89  
90      public TestGJChronology(int epochYear, int epochMonth, int epochDay) {
91          iEpochMillis = fixedFromGJ(epochYear, epochMonth, epochDay) * MILLIS_PER_DAY;
92      }
93  
94      public DateTimeZone getZone() {
95          return null;
96      }
97  
98      public Chronology withUTC() {
99          return this;
100     }
101 
102     /**
103      * Unsupported.
104      */
105     public Chronology withZone(DateTimeZone zone) {
106         throw new UnsupportedOperationException();
107     }
108 
109     long getTimeOnlyMillis(long millis) {
110         return mod(millis, MILLIS_PER_DAY);
111     }
112 
113     long getDateOnlyMillis(long millis) {
114         return millis - mod(millis, MILLIS_PER_DAY);
115     }
116 
117     public DurationField days() {
118         return dayOfWeek().getDurationField();
119     }
120 
121     public DateTimeField dayOfWeek() {
122         return new TestGJDayOfWeekField(this);
123     }
124 
125     public DateTimeField dayOfMonth() {
126         return new TestGJDayOfMonthField(this); 
127     }
128 
129     public DateTimeField dayOfYear() {
130         return new TestGJDayOfYearField(this);
131     }
132 
133     public DurationField weeks() {
134         return weekOfWeekyear().getDurationField();
135     }
136 
137     public DateTimeField weekOfWeekyear() {
138         return new TestGJWeekOfWeekyearField(this);
139     }
140 
141     public DurationField weekyears() {
142         return weekyear().getDurationField();
143     }
144 
145     public DateTimeField weekyear() {
146         return new TestGJWeekyearField(this);
147     }
148 
149     public DurationField months() {
150         return monthOfYear().getDurationField();
151     }
152 
153     public DateTimeField monthOfYear() {
154         return new TestGJMonthOfYearField(this);
155     }
156 
157     public DurationField years() {
158         return year().getDurationField();
159     }
160 
161     public DateTimeField year() {
162         return new TestGJYearField(this);
163     }
164 
165     abstract long millisPerYear();
166 
167     abstract long millisPerMonth();
168 
169     abstract boolean isLeapYear(int year);
170 
171     /**
172      * @return days from 0001-01-01
173      */
174     abstract long fixedFromGJ(int year, int monthOfYear, int dayOfMonth);
175 
176     /**
177      * @param date days from 0001-01-01
178      * @return gj year
179      */
180     abstract int gjYearFromFixed(long date);
181 
182     /**
183      * @param date days from 0001-01-01
184      * @return gj year, monthOfYear, dayOfMonth
185      */
186     abstract int[] gjFromFixed(long date);
187 
188     abstract long fixedFromISO(int weekyear, int weekOfWeekyear, int dayOfWeek);
189 
190     /**
191      * @param date days from 0001-01-01
192      * @return iso weekyear, weekOfWeekyear, dayOfWeek (1=Monday to 7)
193      */
194     abstract int[] isoFromFixed(long date);
195 
196     /**
197      * @param millis milliseconds from epoch
198      * @return days from 0001-01-01
199      */
200     long fixedFromMillis(long millis) {
201         return div(millis + iEpochMillis, MILLIS_PER_DAY);
202     }
203 
204     /**
205      * @param fixed days from 0001-01-01
206      * @return milliseconds from epoch
207      */
208     long millisFromFixed(long fixed) {
209         return fixed * MILLIS_PER_DAY - iEpochMillis;
210     }
211 
212     /**
213      * @return milliseconds from epoch
214      */
215     long millisFromGJ(int year, int monthOfYear, int dayOfMonth) {
216         return millisFromFixed(fixedFromGJ(year, monthOfYear, dayOfMonth));
217     }
218 
219     /**
220      * @param millis milliseconds from epoch
221      * @return gj year
222      */
223     int gjYearFromMillis(long millis) {
224         return gjYearFromFixed(fixedFromMillis(millis));
225     }
226 
227     /**
228      * @param millis milliseconds from epoch
229      * @return gj year, monthOfYear, dayOfMonth
230      */
231     int[] gjFromMillis(long millis) {
232         return gjFromFixed(fixedFromMillis(millis));
233     }
234 
235     /**
236      * @return milliseconds from epoch
237      */
238     long millisFromISO(int weekyear, int weekOfWeekyear, int dayOfWeek) {
239         return millisFromFixed(fixedFromISO(weekyear, weekOfWeekyear, dayOfWeek));
240     }
241 
242     /**
243      * @param millis milliseconds from epoch
244      * @return iso weekyear, weekOfWeekyear, dayOfWeek (1=Monday to 7)
245      */
246     int[] isoFromMillis(long millis) {
247         return isoFromFixed(fixedFromMillis(millis));
248     }
249 
250     /**
251      * @param date days from 0001-01-01
252      * @param weekday 0=Sunday, 1=Monday, 2=Tuesday ... 6=Saturday, 7=Sunday
253      * @param date days from 0001-01-01, on or before weekday
254      */
255     long weekdayOnOrBefore(long date, int weekday) {
256         return date - mod(date - mod(weekday, 7), 7);
257     }
258 
259     long weekdayOnOrAfter(long date, int weekday) {
260         return weekdayOnOrBefore(date + 6, weekday);
261     }
262 
263     long weekdayNearest(long date, int weekday) {
264         return weekdayOnOrBefore(date + 3, weekday);
265     }
266 
267     long weekdayBefore(long date, int weekday) {
268         return weekdayOnOrBefore(date - 1, weekday);
269     }
270 
271     long weekdayAfter(long date, int weekday) {
272         return weekdayOnOrBefore(date + 7, weekday);
273     }
274 
275     long nthWeekday(int n, int weekday,
276                     int year, int monthOfYear, int dayOfMonth)
277     {
278         if (n > 0) {
279             return 7 * n + weekdayBefore
280                 (fixedFromGJ(year, monthOfYear, dayOfMonth), weekday);
281         } else {
282             return 7 * n + weekdayAfter
283                 (fixedFromGJ(year, monthOfYear, dayOfMonth), weekday);
284         }
285     }
286 
287     long firstWeekday(int weekday, int year, int monthOfYear, int dayOfMonth) {
288         return nthWeekday(1, weekday, year, monthOfYear, dayOfMonth);
289     }
290 
291     long lastWeekday(int weekday, int year, int monthOfYear, int dayOfMonth) {
292         return nthWeekday(-1, weekday, year, monthOfYear, dayOfMonth);
293     }
294 }