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; |
17 | |
18 | import java.util.Locale; |
19 | |
20 | import org.joda.time.Chronology; |
21 | import org.joda.time.DateTimeConstants; |
22 | import org.joda.time.DateTimeField; |
23 | import org.joda.time.DateTimeFieldType; |
24 | import org.joda.time.DateTimeZone; |
25 | import org.joda.time.DurationField; |
26 | import org.joda.time.DurationFieldType; |
27 | import org.joda.time.field.DividedDateTimeField; |
28 | import org.joda.time.field.FieldUtils; |
29 | import org.joda.time.field.MillisDurationField; |
30 | import org.joda.time.field.OffsetDateTimeField; |
31 | import org.joda.time.field.PreciseDateTimeField; |
32 | import org.joda.time.field.PreciseDurationField; |
33 | import org.joda.time.field.RemainderDateTimeField; |
34 | import org.joda.time.field.ZeroIsMaxDateTimeField; |
35 | |
36 | /** |
37 | * Abstract implementation for calendar systems that use a typical |
38 | * day/month/year/leapYear model. |
39 | * Most of the utility methods required by subclasses are package-private, |
40 | * reflecting the intention that they be defined in the same package. |
41 | * <p> |
42 | * BasicChronology is thread-safe and immutable, and all subclasses must |
43 | * be as well. |
44 | * |
45 | * @author Stephen Colebourne |
46 | * @author Brian S O'Neill |
47 | * @author Guy Allard |
48 | * @since 1.2, renamed from BaseGJChronology |
49 | */ |
50 | abstract class BasicChronology extends AssembledChronology { |
51 | |
52 | /** Serialization lock */ |
53 | private static final long serialVersionUID = 8283225332206808863L; |
54 | |
55 | private static final DurationField cMillisField; |
56 | private static final DurationField cSecondsField; |
57 | private static final DurationField cMinutesField; |
58 | private static final DurationField cHoursField; |
59 | private static final DurationField cHalfdaysField; |
60 | private static final DurationField cDaysField; |
61 | private static final DurationField cWeeksField; |
62 | |
63 | private static final DateTimeField cMillisOfSecondField; |
64 | private static final DateTimeField cMillisOfDayField; |
65 | private static final DateTimeField cSecondOfMinuteField; |
66 | private static final DateTimeField cSecondOfDayField; |
67 | private static final DateTimeField cMinuteOfHourField; |
68 | private static final DateTimeField cMinuteOfDayField; |
69 | private static final DateTimeField cHourOfDayField; |
70 | private static final DateTimeField cHourOfHalfdayField; |
71 | private static final DateTimeField cClockhourOfDayField; |
72 | private static final DateTimeField cClockhourOfHalfdayField; |
73 | private static final DateTimeField cHalfdayOfDayField; |
74 | |
75 | static { |
76 | cMillisField = MillisDurationField.INSTANCE; |
77 | cSecondsField = new PreciseDurationField |
78 | (DurationFieldType.seconds(), DateTimeConstants.MILLIS_PER_SECOND); |
79 | cMinutesField = new PreciseDurationField |
80 | (DurationFieldType.minutes(), DateTimeConstants.MILLIS_PER_MINUTE); |
81 | cHoursField = new PreciseDurationField |
82 | (DurationFieldType.hours(), DateTimeConstants.MILLIS_PER_HOUR); |
83 | cHalfdaysField = new PreciseDurationField |
84 | (DurationFieldType.halfdays(), DateTimeConstants.MILLIS_PER_DAY / 2); |
85 | cDaysField = new PreciseDurationField |
86 | (DurationFieldType.days(), DateTimeConstants.MILLIS_PER_DAY); |
87 | cWeeksField = new PreciseDurationField |
88 | (DurationFieldType.weeks(), DateTimeConstants.MILLIS_PER_WEEK); |
89 | |
90 | cMillisOfSecondField = new PreciseDateTimeField |
91 | (DateTimeFieldType.millisOfSecond(), cMillisField, cSecondsField); |
92 | |
93 | cMillisOfDayField = new PreciseDateTimeField |
94 | (DateTimeFieldType.millisOfDay(), cMillisField, cDaysField); |
95 | |
96 | cSecondOfMinuteField = new PreciseDateTimeField |
97 | (DateTimeFieldType.secondOfMinute(), cSecondsField, cMinutesField); |
98 | |
99 | cSecondOfDayField = new PreciseDateTimeField |
100 | (DateTimeFieldType.secondOfDay(), cSecondsField, cDaysField); |
101 | |
102 | cMinuteOfHourField = new PreciseDateTimeField |
103 | (DateTimeFieldType.minuteOfHour(), cMinutesField, cHoursField); |
104 | |
105 | cMinuteOfDayField = new PreciseDateTimeField |
106 | (DateTimeFieldType.minuteOfDay(), cMinutesField, cDaysField); |
107 | |
108 | cHourOfDayField = new PreciseDateTimeField |
109 | (DateTimeFieldType.hourOfDay(), cHoursField, cDaysField); |
110 | |
111 | cHourOfHalfdayField = new PreciseDateTimeField |
112 | (DateTimeFieldType.hourOfHalfday(), cHoursField, cHalfdaysField); |
113 | |
114 | cClockhourOfDayField = new ZeroIsMaxDateTimeField |
115 | (cHourOfDayField, DateTimeFieldType.clockhourOfDay()); |
116 | |
117 | cClockhourOfHalfdayField = new ZeroIsMaxDateTimeField |
118 | (cHourOfHalfdayField, DateTimeFieldType.clockhourOfHalfday()); |
119 | |
120 | cHalfdayOfDayField = new HalfdayField(); |
121 | } |
122 | |
123 | private static final int CACHE_SIZE = 1 << 10; |
124 | private static final int CACHE_MASK = CACHE_SIZE - 1; |
125 | |
126 | private transient final YearInfo[] iYearInfoCache = new YearInfo[CACHE_SIZE]; |
127 | |
128 | private final int iMinDaysInFirstWeek; |
129 | |
130 | BasicChronology(Chronology base, Object param, int minDaysInFirstWeek) { |
131 | super(base, param); |
132 | |
133 | if (minDaysInFirstWeek < 1 || minDaysInFirstWeek > 7) { |
134 | throw new IllegalArgumentException |
135 | ("Invalid min days in first week: " + minDaysInFirstWeek); |
136 | } |
137 | |
138 | iMinDaysInFirstWeek = minDaysInFirstWeek; |
139 | } |
140 | |
141 | public DateTimeZone getZone() { |
142 | Chronology base; |
143 | if ((base = getBase()) != null) { |
144 | return base.getZone(); |
145 | } |
146 | return DateTimeZone.UTC; |
147 | } |
148 | |
149 | public long getDateTimeMillis( |
150 | int year, int monthOfYear, int dayOfMonth, int millisOfDay) |
151 | throws IllegalArgumentException { |
152 | Chronology base; |
153 | if ((base = getBase()) != null) { |
154 | return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay); |
155 | } |
156 | |
157 | FieldUtils.verifyValueBounds |
158 | (DateTimeFieldType.millisOfDay(), millisOfDay, 0, DateTimeConstants.MILLIS_PER_DAY); |
159 | return getDateMidnightMillis(year, monthOfYear, dayOfMonth) + millisOfDay; |
160 | } |
161 | |
162 | public long getDateTimeMillis( |
163 | int year, int monthOfYear, int dayOfMonth, |
164 | int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond) |
165 | throws IllegalArgumentException { |
166 | Chronology base; |
167 | if ((base = getBase()) != null) { |
168 | return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, |
169 | hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); |
170 | } |
171 | |
172 | FieldUtils.verifyValueBounds(DateTimeFieldType.hourOfDay(), hourOfDay, 0, 23); |
173 | FieldUtils.verifyValueBounds(DateTimeFieldType.minuteOfHour(), minuteOfHour, 0, 59); |
174 | FieldUtils.verifyValueBounds(DateTimeFieldType.secondOfMinute(), secondOfMinute, 0, 59); |
175 | FieldUtils.verifyValueBounds(DateTimeFieldType.millisOfSecond(), millisOfSecond, 0, 999); |
176 | |
177 | return getDateMidnightMillis(year, monthOfYear, dayOfMonth) |
178 | + hourOfDay * DateTimeConstants.MILLIS_PER_HOUR |
179 | + minuteOfHour * DateTimeConstants.MILLIS_PER_MINUTE |
180 | + secondOfMinute * DateTimeConstants.MILLIS_PER_SECOND |
181 | + millisOfSecond; |
182 | } |
183 | |
184 | public int getMinimumDaysInFirstWeek() { |
185 | return iMinDaysInFirstWeek; |
186 | } |
187 | |
188 | /** |
189 | * Checks if this chronology instance equals another. |
190 | * |
191 | * @param obj the object to compare to |
192 | * @return true if equal |
193 | * @since 1.6 |
194 | */ |
195 | public boolean equals(Object obj) { |
196 | return super.equals(obj); |
197 | } |
198 | |
199 | /** |
200 | * A suitable hash code for the chronology. |
201 | * |
202 | * @return the hash code |
203 | * @since 1.6 |
204 | */ |
205 | public int hashCode() { |
206 | return getClass().getName().hashCode() * 11 + getZone().hashCode() + getMinimumDaysInFirstWeek(); |
207 | } |
208 | |
209 | // Output |
210 | //----------------------------------------------------------------------- |
211 | /** |
212 | * Gets a debugging toString. |
213 | * |
214 | * @return a debugging string |
215 | */ |
216 | public String toString() { |
217 | StringBuffer sb = new StringBuffer(60); |
218 | String name = getClass().getName(); |
219 | int index = name.lastIndexOf('.'); |
220 | if (index >= 0) { |
221 | name = name.substring(index + 1); |
222 | } |
223 | sb.append(name); |
224 | sb.append('['); |
225 | DateTimeZone zone = getZone(); |
226 | if (zone != null) { |
227 | sb.append(zone.getID()); |
228 | } |
229 | if (getMinimumDaysInFirstWeek() != 4) { |
230 | sb.append(",mdfw="); |
231 | sb.append(getMinimumDaysInFirstWeek()); |
232 | } |
233 | sb.append(']'); |
234 | return sb.toString(); |
235 | } |
236 | |
237 | protected void assemble(Fields fields) { |
238 | // First copy fields that are the same for all Gregorian and Julian |
239 | // chronologies. |
240 | |
241 | fields.millis = cMillisField; |
242 | fields.seconds = cSecondsField; |
243 | fields.minutes = cMinutesField; |
244 | fields.hours = cHoursField; |
245 | fields.halfdays = cHalfdaysField; |
246 | fields.days = cDaysField; |
247 | fields.weeks = cWeeksField; |
248 | |
249 | fields.millisOfSecond = cMillisOfSecondField; |
250 | fields.millisOfDay = cMillisOfDayField; |
251 | fields.secondOfMinute = cSecondOfMinuteField; |
252 | fields.secondOfDay = cSecondOfDayField; |
253 | fields.minuteOfHour = cMinuteOfHourField; |
254 | fields.minuteOfDay = cMinuteOfDayField; |
255 | fields.hourOfDay = cHourOfDayField; |
256 | fields.hourOfHalfday = cHourOfHalfdayField; |
257 | fields.clockhourOfDay = cClockhourOfDayField; |
258 | fields.clockhourOfHalfday = cClockhourOfHalfdayField; |
259 | fields.halfdayOfDay = cHalfdayOfDayField; |
260 | |
261 | // Now create fields that have unique behavior for Gregorian and Julian |
262 | // chronologies. |
263 | |
264 | fields.year = new BasicYearDateTimeField(this); |
265 | fields.yearOfEra = new GJYearOfEraDateTimeField(fields.year, this); |
266 | |
267 | // Define one-based centuryOfEra and yearOfCentury. |
268 | DateTimeField field = new OffsetDateTimeField( |
269 | fields.yearOfEra, 99); |
270 | fields.centuryOfEra = new DividedDateTimeField( |
271 | field, DateTimeFieldType.centuryOfEra(), 100); |
272 | |
273 | field = new RemainderDateTimeField( |
274 | (DividedDateTimeField) fields.centuryOfEra); |
275 | fields.yearOfCentury = new OffsetDateTimeField( |
276 | field, DateTimeFieldType.yearOfCentury(), 1); |
277 | |
278 | fields.era = new GJEraDateTimeField(this); |
279 | fields.dayOfWeek = new GJDayOfWeekDateTimeField(this, fields.days); |
280 | fields.dayOfMonth = new BasicDayOfMonthDateTimeField(this, fields.days); |
281 | fields.dayOfYear = new BasicDayOfYearDateTimeField(this, fields.days); |
282 | fields.monthOfYear = new GJMonthOfYearDateTimeField(this); |
283 | fields.weekyear = new BasicWeekyearDateTimeField(this); |
284 | fields.weekOfWeekyear = new BasicWeekOfWeekyearDateTimeField(this, fields.weeks); |
285 | |
286 | field = new RemainderDateTimeField( |
287 | fields.weekyear, DateTimeFieldType.weekyearOfCentury(), 100); |
288 | fields.weekyearOfCentury = new OffsetDateTimeField( |
289 | field, DateTimeFieldType.weekyearOfCentury(), 1); |
290 | |
291 | // The remaining (imprecise) durations are available from the newly |
292 | // created datetime fields. |
293 | |
294 | fields.years = fields.year.getDurationField(); |
295 | fields.centuries = fields.centuryOfEra.getDurationField(); |
296 | fields.months = fields.monthOfYear.getDurationField(); |
297 | fields.weekyears = fields.weekyear.getDurationField(); |
298 | } |
299 | |
300 | //----------------------------------------------------------------------- |
301 | /** |
302 | * Get the number of days in the year. |
303 | * |
304 | * @return 366 |
305 | */ |
306 | int getDaysInYearMax() { |
307 | return 366; |
308 | } |
309 | |
310 | /** |
311 | * Get the number of days in the year. |
312 | * |
313 | * @param year the year to use |
314 | * @return 366 if a leap year, otherwise 365 |
315 | */ |
316 | int getDaysInYear(int year) { |
317 | return isLeapYear(year) ? 366 : 365; |
318 | } |
319 | |
320 | /** |
321 | * Get the number of weeks in the year. |
322 | * |
323 | * @param year the year to use |
324 | * @return number of weeks in the year |
325 | */ |
326 | int getWeeksInYear(int year) { |
327 | long firstWeekMillis1 = getFirstWeekOfYearMillis(year); |
328 | long firstWeekMillis2 = getFirstWeekOfYearMillis(year + 1); |
329 | return (int) ((firstWeekMillis2 - firstWeekMillis1) / DateTimeConstants.MILLIS_PER_WEEK); |
330 | } |
331 | |
332 | /** |
333 | * Get the millis for the first week of a year. |
334 | * |
335 | * @param year the year to use |
336 | * @return millis |
337 | */ |
338 | long getFirstWeekOfYearMillis(int year) { |
339 | long jan1millis = getYearMillis(year); |
340 | int jan1dayOfWeek = getDayOfWeek(jan1millis); |
341 | |
342 | if (jan1dayOfWeek > (8 - iMinDaysInFirstWeek)) { |
343 | // First week is end of previous year because it doesn't have enough days. |
344 | return jan1millis + (8 - jan1dayOfWeek) |
345 | * (long)DateTimeConstants.MILLIS_PER_DAY; |
346 | } else { |
347 | // First week is start of this year because it has enough days. |
348 | return jan1millis - (jan1dayOfWeek - 1) |
349 | * (long)DateTimeConstants.MILLIS_PER_DAY; |
350 | } |
351 | } |
352 | |
353 | /** |
354 | * Get the milliseconds for the start of a year. |
355 | * |
356 | * @param year The year to use. |
357 | * @return millis from 1970-01-01T00:00:00Z |
358 | */ |
359 | long getYearMillis(int year) { |
360 | return getYearInfo(year).iFirstDayMillis; |
361 | } |
362 | |
363 | /** |
364 | * Get the milliseconds for the start of a month. |
365 | * |
366 | * @param year The year to use. |
367 | * @param month The month to use |
368 | * @return millis from 1970-01-01T00:00:00Z |
369 | */ |
370 | long getYearMonthMillis(int year, int month) { |
371 | long millis = getYearMillis(year); |
372 | millis += getTotalMillisByYearMonth(year, month); |
373 | return millis; |
374 | } |
375 | |
376 | /** |
377 | * Get the milliseconds for a particular date. |
378 | * |
379 | * @param year The year to use. |
380 | * @param month The month to use |
381 | * @param dayOfMonth The day of the month to use |
382 | * @return millis from 1970-01-01T00:00:00Z |
383 | */ |
384 | long getYearMonthDayMillis(int year, int month, int dayOfMonth) { |
385 | long millis = getYearMillis(year); |
386 | millis += getTotalMillisByYearMonth(year, month); |
387 | return millis + (dayOfMonth - 1) * (long)DateTimeConstants.MILLIS_PER_DAY; |
388 | } |
389 | |
390 | /** |
391 | * @param instant millis from 1970-01-01T00:00:00Z |
392 | */ |
393 | int getYear(long instant) { |
394 | // Get an initial estimate of the year, and the millis value that |
395 | // represents the start of that year. Then verify estimate and fix if |
396 | // necessary. |
397 | |
398 | // Initial estimate uses values divided by two to avoid overflow. |
399 | long unitMillis = getAverageMillisPerYearDividedByTwo(); |
400 | long i2 = (instant >> 1) + getApproxMillisAtEpochDividedByTwo(); |
401 | if (i2 < 0) { |
402 | i2 = i2 - unitMillis + 1; |
403 | } |
404 | int year = (int) (i2 / unitMillis); |
405 | |
406 | long yearStart = getYearMillis(year); |
407 | long diff = instant - yearStart; |
408 | |
409 | if (diff < 0) { |
410 | year--; |
411 | } else if (diff >= DateTimeConstants.MILLIS_PER_DAY * 365L) { |
412 | // One year may need to be added to fix estimate. |
413 | long oneYear; |
414 | if (isLeapYear(year)) { |
415 | oneYear = DateTimeConstants.MILLIS_PER_DAY * 366L; |
416 | } else { |
417 | oneYear = DateTimeConstants.MILLIS_PER_DAY * 365L; |
418 | } |
419 | |
420 | yearStart += oneYear; |
421 | |
422 | if (yearStart <= instant) { |
423 | // Didn't go too far, so actually add one year. |
424 | year++; |
425 | } |
426 | } |
427 | |
428 | return year; |
429 | } |
430 | |
431 | /** |
432 | * @param millis from 1970-01-01T00:00:00Z |
433 | */ |
434 | int getMonthOfYear(long millis) { |
435 | return getMonthOfYear(millis, getYear(millis)); |
436 | } |
437 | |
438 | /** |
439 | * @param millis from 1970-01-01T00:00:00Z |
440 | * @param year precalculated year of millis |
441 | */ |
442 | abstract int getMonthOfYear(long millis, int year); |
443 | |
444 | /** |
445 | * @param millis from 1970-01-01T00:00:00Z |
446 | */ |
447 | int getDayOfMonth(long millis) { |
448 | int year = getYear(millis); |
449 | int month = getMonthOfYear(millis, year); |
450 | return getDayOfMonth(millis, year, month); |
451 | } |
452 | |
453 | /** |
454 | * @param millis from 1970-01-01T00:00:00Z |
455 | * @param year precalculated year of millis |
456 | */ |
457 | int getDayOfMonth(long millis, int year) { |
458 | int month = getMonthOfYear(millis, year); |
459 | return getDayOfMonth(millis, year, month); |
460 | } |
461 | |
462 | /** |
463 | * @param millis from 1970-01-01T00:00:00Z |
464 | * @param year precalculated year of millis |
465 | * @param month precalculated month of millis |
466 | */ |
467 | int getDayOfMonth(long millis, int year, int month) { |
468 | long dateMillis = getYearMillis(year); |
469 | dateMillis += getTotalMillisByYearMonth(year, month); |
470 | return (int) ((millis - dateMillis) / DateTimeConstants.MILLIS_PER_DAY) + 1; |
471 | } |
472 | |
473 | /** |
474 | * @param instant millis from 1970-01-01T00:00:00Z |
475 | */ |
476 | int getDayOfYear(long instant) { |
477 | return getDayOfYear(instant, getYear(instant)); |
478 | } |
479 | |
480 | /** |
481 | * @param instant millis from 1970-01-01T00:00:00Z |
482 | * @param year precalculated year of millis |
483 | */ |
484 | int getDayOfYear(long instant, int year) { |
485 | long yearStart = getYearMillis(year); |
486 | return (int) ((instant - yearStart) / DateTimeConstants.MILLIS_PER_DAY) + 1; |
487 | } |
488 | |
489 | /** |
490 | * @param instant millis from 1970-01-01T00:00:00Z |
491 | */ |
492 | int getWeekyear(long instant) { |
493 | int year = getYear(instant); |
494 | int week = getWeekOfWeekyear(instant, year); |
495 | if (week == 1) { |
496 | return getYear(instant + DateTimeConstants.MILLIS_PER_WEEK); |
497 | } else if (week > 51) { |
498 | return getYear(instant - (2 * DateTimeConstants.MILLIS_PER_WEEK)); |
499 | } else { |
500 | return year; |
501 | } |
502 | } |
503 | |
504 | /** |
505 | * @param instant millis from 1970-01-01T00:00:00Z |
506 | */ |
507 | int getWeekOfWeekyear(long instant) { |
508 | return getWeekOfWeekyear(instant, getYear(instant)); |
509 | } |
510 | |
511 | /** |
512 | * @param instant millis from 1970-01-01T00:00:00Z |
513 | * @param year precalculated year of millis |
514 | */ |
515 | int getWeekOfWeekyear(long instant, int year) { |
516 | long firstWeekMillis1 = getFirstWeekOfYearMillis(year); |
517 | if (instant < firstWeekMillis1) { |
518 | return getWeeksInYear(year - 1); |
519 | } |
520 | long firstWeekMillis2 = getFirstWeekOfYearMillis(year + 1); |
521 | if (instant >= firstWeekMillis2) { |
522 | return 1; |
523 | } |
524 | return (int) ((instant - firstWeekMillis1) / DateTimeConstants.MILLIS_PER_WEEK) + 1; |
525 | } |
526 | |
527 | /** |
528 | * @param instant millis from 1970-01-01T00:00:00Z |
529 | */ |
530 | int getDayOfWeek(long instant) { |
531 | // 1970-01-01 is day of week 4, Thursday. |
532 | |
533 | long daysSince19700101; |
534 | if (instant >= 0) { |
535 | daysSince19700101 = instant / DateTimeConstants.MILLIS_PER_DAY; |
536 | } else { |
537 | daysSince19700101 = (instant - (DateTimeConstants.MILLIS_PER_DAY - 1)) |
538 | / DateTimeConstants.MILLIS_PER_DAY; |
539 | if (daysSince19700101 < -3) { |
540 | return 7 + (int) ((daysSince19700101 + 4) % 7); |
541 | } |
542 | } |
543 | |
544 | return 1 + (int) ((daysSince19700101 + 3) % 7); |
545 | } |
546 | |
547 | /** |
548 | * @param instant millis from 1970-01-01T00:00:00Z |
549 | */ |
550 | int getMillisOfDay(long instant) { |
551 | if (instant >= 0) { |
552 | return (int) (instant % DateTimeConstants.MILLIS_PER_DAY); |
553 | } else { |
554 | return (DateTimeConstants.MILLIS_PER_DAY - 1) |
555 | + (int) ((instant + 1) % DateTimeConstants.MILLIS_PER_DAY); |
556 | } |
557 | } |
558 | |
559 | /** |
560 | * Gets the maximum number of days in any month. |
561 | * |
562 | * @return 31 |
563 | */ |
564 | int getDaysInMonthMax() { |
565 | return 31; |
566 | } |
567 | |
568 | /** |
569 | * Gets the maximum number of days in the month specified by the instant. |
570 | * |
571 | * @param instant millis from 1970-01-01T00:00:00Z |
572 | * @return the maximum number of days in the month |
573 | */ |
574 | int getDaysInMonthMax(long instant) { |
575 | int thisYear = getYear(instant); |
576 | int thisMonth = getMonthOfYear(instant, thisYear); |
577 | return getDaysInYearMonth(thisYear, thisMonth); |
578 | } |
579 | |
580 | /** |
581 | * Gets the maximum number of days in the month specified by the instant. |
582 | * The value represents what the user is trying to set, and can be |
583 | * used to optimise this method. |
584 | * |
585 | * @param instant millis from 1970-01-01T00:00:00Z |
586 | * @param value the value being set |
587 | * @return the maximum number of days in the month |
588 | */ |
589 | int getDaysInMonthMaxForSet(long instant, int value) { |
590 | return getDaysInMonthMax(instant); |
591 | } |
592 | |
593 | //----------------------------------------------------------------------- |
594 | /** |
595 | * Gets the milliseconds for a date at midnight. |
596 | * |
597 | * @param year the year |
598 | * @param monthOfYear the month |
599 | * @param dayOfMonth the day |
600 | * @return the milliseconds |
601 | */ |
602 | long getDateMidnightMillis(int year, int monthOfYear, int dayOfMonth) { |
603 | FieldUtils.verifyValueBounds(DateTimeFieldType.year(), year, getMinYear(), getMaxYear()); |
604 | FieldUtils.verifyValueBounds(DateTimeFieldType.monthOfYear(), monthOfYear, 1, getMaxMonth(year)); |
605 | FieldUtils.verifyValueBounds(DateTimeFieldType.dayOfMonth(), dayOfMonth, 1, getDaysInYearMonth(year, monthOfYear)); |
606 | return getYearMonthDayMillis(year, monthOfYear, dayOfMonth); |
607 | } |
608 | |
609 | /** |
610 | * Gets the difference between the two instants in years. |
611 | * |
612 | * @param minuendInstant the first instant |
613 | * @param subtrahendInstant the second instant |
614 | * @return the difference |
615 | */ |
616 | abstract long getYearDifference(long minuendInstant, long subtrahendInstant); |
617 | |
618 | /** |
619 | * Is the specified year a leap year? |
620 | * |
621 | * @param year the year to test |
622 | * @return true if leap |
623 | */ |
624 | abstract boolean isLeapYear(int year); |
625 | |
626 | /** |
627 | * Gets the number of days in the specified month and year. |
628 | * |
629 | * @param year the year |
630 | * @param month the month |
631 | * @return the number of days |
632 | */ |
633 | abstract int getDaysInYearMonth(int year, int month); |
634 | |
635 | /** |
636 | * Gets the maximum days in the specified month. |
637 | * |
638 | * @param month the month |
639 | * @return the max days |
640 | */ |
641 | abstract int getDaysInMonthMax(int month); |
642 | |
643 | /** |
644 | * Gets the total number of millis elapsed in this year at the start |
645 | * of the specified month, such as zero for month 1. |
646 | * |
647 | * @param year the year |
648 | * @param month the month |
649 | * @return the elapsed millis at the start of the month |
650 | */ |
651 | abstract long getTotalMillisByYearMonth(int year, int month); |
652 | |
653 | /** |
654 | * Gets the millisecond value of the first day of the year. |
655 | * |
656 | * @return the milliseconds for the first of the year |
657 | */ |
658 | abstract long calculateFirstDayOfYearMillis(int year); |
659 | |
660 | /** |
661 | * Gets the minimum supported year. |
662 | * |
663 | * @return the year |
664 | */ |
665 | abstract int getMinYear(); |
666 | |
667 | /** |
668 | * Gets the maximum supported year. |
669 | * |
670 | * @return the year |
671 | */ |
672 | abstract int getMaxYear(); |
673 | |
674 | /** |
675 | * Gets the maximum month for the specified year. |
676 | * This implementation calls getMaxMonth(). |
677 | * |
678 | * @param year the year |
679 | * @return the maximum month value |
680 | */ |
681 | int getMaxMonth(int year) { |
682 | return getMaxMonth(); |
683 | } |
684 | |
685 | /** |
686 | * Gets the maximum number of months. |
687 | * |
688 | * @return 12 |
689 | */ |
690 | int getMaxMonth() { |
691 | return 12; |
692 | } |
693 | |
694 | /** |
695 | * Gets an average value for the milliseconds per year. |
696 | * |
697 | * @return the millis per year |
698 | */ |
699 | abstract long getAverageMillisPerYear(); |
700 | |
701 | /** |
702 | * Gets an average value for the milliseconds per year, divided by two. |
703 | * |
704 | * @return the millis per year divided by two |
705 | */ |
706 | abstract long getAverageMillisPerYearDividedByTwo(); |
707 | |
708 | /** |
709 | * Gets an average value for the milliseconds per month. |
710 | * |
711 | * @return the millis per month |
712 | */ |
713 | abstract long getAverageMillisPerMonth(); |
714 | |
715 | /** |
716 | * Returns a constant representing the approximate number of milliseconds |
717 | * elapsed from year 0 of this chronology, divided by two. This constant |
718 | * <em>must</em> be defined as: |
719 | * <pre> |
720 | * (yearAtEpoch * averageMillisPerYear + millisOfYearAtEpoch) / 2 |
721 | * </pre> |
722 | * where epoch is 1970-01-01 (Gregorian). |
723 | */ |
724 | abstract long getApproxMillisAtEpochDividedByTwo(); |
725 | |
726 | /** |
727 | * Sets the year from an instant and year. |
728 | * |
729 | * @param instant millis from 1970-01-01T00:00:00Z |
730 | * @param year the year to set |
731 | * @return the updated millis |
732 | */ |
733 | abstract long setYear(long instant, int year); |
734 | |
735 | //----------------------------------------------------------------------- |
736 | // Although accessed by multiple threads, this method doesn't need to be synchronized. |
737 | private YearInfo getYearInfo(int year) { |
738 | YearInfo info = iYearInfoCache[year & CACHE_MASK]; |
739 | if (info == null || info.iYear != year) { |
740 | info = new YearInfo(year, calculateFirstDayOfYearMillis(year)); |
741 | iYearInfoCache[year & CACHE_MASK] = info; |
742 | } |
743 | return info; |
744 | } |
745 | |
746 | private static class HalfdayField extends PreciseDateTimeField { |
747 | private static final long serialVersionUID = 581601443656929254L; |
748 | |
749 | HalfdayField() { |
750 | super(DateTimeFieldType.halfdayOfDay(), cHalfdaysField, cDaysField); |
751 | } |
752 | |
753 | public String getAsText(int fieldValue, Locale locale) { |
754 | return GJLocaleSymbols.forLocale(locale).halfdayValueToText(fieldValue); |
755 | } |
756 | |
757 | public long set(long millis, String text, Locale locale) { |
758 | return set(millis, GJLocaleSymbols.forLocale(locale).halfdayTextToValue(text)); |
759 | } |
760 | |
761 | public int getMaximumTextLength(Locale locale) { |
762 | return GJLocaleSymbols.forLocale(locale).getHalfdayMaxTextLength(); |
763 | } |
764 | } |
765 | |
766 | private static class YearInfo { |
767 | public final int iYear; |
768 | public final long iFirstDayMillis; |
769 | |
770 | YearInfo(int year, long firstDayMillis) { |
771 | iYear = year; |
772 | iFirstDayMillis = firstDayMillis; |
773 | } |
774 | } |
775 | |
776 | } |