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;
17  
18  import java.io.Serializable;
19  
20  import org.joda.time.Chronology;
21  import org.joda.time.DateTimeField;
22  import org.joda.time.DateTimeFieldType;
23  import org.joda.time.DateTimeZone;
24  import org.joda.time.DurationField;
25  import org.joda.time.DurationFieldType;
26  import org.joda.time.IllegalFieldValueException;
27  import org.joda.time.ReadablePartial;
28  import org.joda.time.ReadablePeriod;
29  import org.joda.time.field.FieldUtils;
30  import org.joda.time.field.UnsupportedDateTimeField;
31  import org.joda.time.field.UnsupportedDurationField;
32  
33  /**
34   * BaseChronology provides a skeleton implementation for chronology
35   * classes. Many utility methods are defined, but all fields are unsupported.
36   * <p>
37   * BaseChronology is thread-safe and immutable, and all subclasses must be
38   * as well.
39   *
40   * @author Brian S O'Neill
41   * @since 1.0
42   */
43  public abstract class BaseChronology
44          extends Chronology
45          implements Serializable {
46  
47      /** Serialization version. */
48      private static final long serialVersionUID = -7310865996721419676L;
49  
50      /**
51       * Restricted constructor.
52       */
53      protected BaseChronology() {
54          super();
55      }
56  
57      /**
58       * Returns the DateTimeZone that this Chronology operates in, or null if
59       * unspecified.
60       *
61       * @return DateTimeZone null if unspecified
62       */
63      public abstract DateTimeZone getZone();
64  
65      /**
66       * Returns an instance of this Chronology that operates in the UTC time
67       * zone. Chronologies that do not operate in a time zone or are already
68       * UTC must return themself.
69       *
70       * @return a version of this chronology that ignores time zones
71       */
72      public abstract Chronology withUTC();
73      
74      /**
75       * Returns an instance of this Chronology that operates in any time zone.
76       *
77       * @return a version of this chronology with a specific time zone
78       * @param zone to use, or default if null
79       * @see org.joda.time.chrono.ZonedChronology
80       */
81      public abstract Chronology withZone(DateTimeZone zone);
82  
83      /**
84       * Returns a datetime millisecond instant, formed from the given year,
85       * month, day, and millisecond values. The set of given values must refer
86       * to a valid datetime, or else an IllegalArgumentException is thrown.
87       * <p>
88       * The default implementation calls upon separate DateTimeFields to
89       * determine the result. Subclasses are encouraged to provide a more
90       * efficient implementation.
91       *
92       * @param year year to use
93       * @param monthOfYear month to use
94       * @param dayOfMonth day of month to use
95       * @param millisOfDay millisecond to use
96       * @return millisecond instant from 1970-01-01T00:00:00Z
97       */
98      public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
99                                    int millisOfDay)
100         throws IllegalArgumentException
101     {
102         long instant = year().set(0, year);
103         instant = monthOfYear().set(instant, monthOfYear);
104         instant = dayOfMonth().set(instant, dayOfMonth);
105         return millisOfDay().set(instant, millisOfDay);
106     }
107 
108     /**
109      * Returns a datetime millisecond instant, formed from the given year,
110      * month, day, hour, minute, second, and millisecond values. The set of
111      * given values must refer to a valid datetime, or else an
112      * IllegalArgumentException is thrown.
113      * <p>
114      * The default implementation calls upon separate DateTimeFields to
115      * determine the result. Subclasses are encouraged to provide a more
116      * efficient implementation.
117      *
118      * @param year year to use
119      * @param monthOfYear month to use
120      * @param dayOfMonth day of month to use
121      * @param hourOfDay hour to use
122      * @param minuteOfHour minute to use
123      * @param secondOfMinute second to use
124      * @param millisOfSecond millisecond to use
125      * @return millisecond instant from 1970-01-01T00:00:00Z
126      */
127     public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
128                                   int hourOfDay, int minuteOfHour,
129                                   int secondOfMinute, int millisOfSecond)
130         throws IllegalArgumentException
131     {
132         long instant = year().set(0, year);
133         instant = monthOfYear().set(instant, monthOfYear);
134         instant = dayOfMonth().set(instant, dayOfMonth);
135         instant = hourOfDay().set(instant, hourOfDay);
136         instant = minuteOfHour().set(instant, minuteOfHour);
137         instant = secondOfMinute().set(instant, secondOfMinute);
138         return millisOfSecond().set(instant, millisOfSecond);
139     }
140 
141     /**
142      * Returns a datetime millisecond instant, from from the given instant,
143      * hour, minute, second, and millisecond values. The set of given values
144      * must refer to a valid datetime, or else an IllegalArgumentException is
145      * thrown.
146      * <p>
147      * The default implementation calls upon separate DateTimeFields to
148      * determine the result. Subclasses are encouraged to provide a more
149      * efficient implementation.
150      *
151      * @param instant instant to start from
152      * @param hourOfDay hour to use
153      * @param minuteOfHour minute to use
154      * @param secondOfMinute second to use
155      * @param millisOfSecond millisecond to use
156      * @return millisecond instant from 1970-01-01T00:00:00Z
157      */
158     public long getDateTimeMillis(long instant,
159                                   int hourOfDay, int minuteOfHour,
160                                   int secondOfMinute, int millisOfSecond)
161         throws IllegalArgumentException
162     {
163         instant = hourOfDay().set(instant, hourOfDay);
164         instant = minuteOfHour().set(instant, minuteOfHour);
165         instant = secondOfMinute().set(instant, secondOfMinute);
166         return millisOfSecond().set(instant, millisOfSecond);
167     }
168 
169     //-----------------------------------------------------------------------
170     /**
171      * Validates whether the fields stored in a partial instant are valid.
172      * <p>
173      * This implementation uses {@link DateTimeField#getMinimumValue(ReadablePartial, int[])}
174      * and {@link DateTimeField#getMaximumValue(ReadablePartial, int[])}.
175      *
176      * @param partial  the partial instant to validate
177      * @param values  the values to validate, not null unless the partial is empty
178      * @throws IllegalArgumentException if the instant is invalid
179      */
180     public void validate(ReadablePartial partial, int[] values) {
181         // check values in standard range, catching really stupid cases like -1
182         // this means that the second check will not hit trouble
183         int size = partial.size();
184         for (int i = 0; i < size; i++) {
185             int value = values[i];
186             DateTimeField field = partial.getField(i);
187             if (value < field.getMinimumValue()) {
188                 throw new IllegalFieldValueException
189                     (field.getType(), Integer.valueOf(value),
190                      Integer.valueOf(field.getMinimumValue()), null);
191             }
192             if (value > field.getMaximumValue()) {
193                 throw new IllegalFieldValueException
194                     (field.getType(), Integer.valueOf(value),
195                      null, Integer.valueOf(field.getMaximumValue()));
196             }
197         }
198         // check values in specific range, catching really odd cases like 30th Feb
199         for (int i = 0; i < size; i++) {
200             int value = values[i];
201             DateTimeField field = partial.getField(i);
202             if (value < field.getMinimumValue(partial, values)) {
203                 throw new IllegalFieldValueException
204                     (field.getType(), Integer.valueOf(value),
205                      Integer.valueOf(field.getMinimumValue(partial, values)), null);
206             }
207             if (value > field.getMaximumValue(partial, values)) {
208                 throw new IllegalFieldValueException
209                     (field.getType(), Integer.valueOf(value),
210                      null, Integer.valueOf(field.getMaximumValue(partial, values)));
211             }
212         }
213     }
214 
215     /**
216      * Gets the values of a partial from an instant.
217      *
218      * @param partial  the partial instant to use
219      * @param instant  the instant to query
220      * @return the values of the partial extracted from the instant
221      */
222     public int[] get(ReadablePartial partial, long instant) {
223         int size = partial.size();
224         int[] values = new int[size];
225         for (int i = 0; i < size; i++) {
226             values[i] = partial.getFieldType(i).getField(this).get(instant);
227         }
228         return values;
229     }
230 
231     /**
232      * Sets the partial into the instant.
233      *
234      * @param partial  the partial instant to use
235      * @param instant  the instant to update
236      * @return the updated instant
237      */
238     public long set(ReadablePartial partial, long instant) {
239         for (int i = 0, isize = partial.size(); i < isize; i++) {
240             instant = partial.getFieldType(i).getField(this).set(instant, partial.getValue(i));
241         }
242         return instant;
243     }
244 
245     //-----------------------------------------------------------------------
246     /**
247      * Gets the values of a period from an interval.
248      *
249      * @param period  the period instant to use
250      * @param startInstant  the start instant of an interval to query
251      * @param endInstant  the start instant of an interval to query
252      * @return the values of the period extracted from the interval
253      */
254     public int[] get(ReadablePeriod period, long startInstant, long endInstant) {
255         int size = period.size();
256         int[] values = new int[size];
257         if (startInstant != endInstant) {
258             for (int i = 0; i < size; i++) {
259                 DurationField field = period.getFieldType(i).getField(this);
260                 int value = field.getDifference(endInstant, startInstant);
261                 startInstant = field.add(startInstant, value);
262                 values[i] = value;
263             }
264         }
265         return values;
266     }
267 
268     /**
269      * Gets the values of a period from an interval.
270      *
271      * @param period  the period instant to use
272      * @param duration  the duration to query
273      * @return the values of the period extracted from the duration
274      */
275     public int[] get(ReadablePeriod period, long duration) {
276         int size = period.size();
277         int[] values = new int[size];
278         if (duration != 0) {
279             long current = 0;
280             for (int i = 0; i < size; i++) {
281                 DurationField field = period.getFieldType(i).getField(this);
282                 if (field.isPrecise()) {
283                     int value = field.getDifference(duration, current);
284                     current = field.add(current, value);
285                     values[i] = value;
286                 }
287             }
288         }
289         return values;
290     }
291 
292     /**
293      * Adds the period to the instant, specifying the number of times to add.
294      *
295      * @param period  the period to add, null means add nothing
296      * @param instant  the instant to add to
297      * @param scalar  the number of times to add
298      * @return the updated instant
299      */
300     public long add(ReadablePeriod period, long instant, int scalar) {
301         if (scalar != 0 && period != null) {
302             for (int i = 0, isize = period.size(); i < isize; i++) {
303                 long value = period.getValue(i); // use long to allow for multiplication (fits OK)
304                 if (value != 0) {
305                     instant = period.getFieldType(i).getField(this).add(instant, value * scalar);
306                 }
307             }
308         }
309         return instant;
310     }
311 
312     //-----------------------------------------------------------------------
313     /**
314      * Adds the duration to the instant, specifying the number of times to add.
315      *
316      * @param instant  the instant to add to
317      * @param duration  the duration to add
318      * @param scalar  the number of times to add
319      * @return the updated instant
320      */
321     public long add(long instant, long duration, int scalar) {
322         if (duration == 0 || scalar == 0) {
323             return instant;
324         }
325         long add = FieldUtils.safeMultiply(duration, scalar);
326         return FieldUtils.safeAdd(instant, add);
327     }
328 
329     // Millis
330     //-----------------------------------------------------------------------
331     /**
332      * Get the millis duration field for this chronology.
333      * 
334      * @return DurationField or UnsupportedDurationField if unsupported
335      */
336     public DurationField millis() {
337         return UnsupportedDurationField.getInstance(DurationFieldType.millis());
338     }
339 
340     /**
341      * Get the millis of second field for this chronology.
342      * 
343      * @return DateTimeField or UnsupportedDateTimeField if unsupported
344      */
345     public DateTimeField millisOfSecond() {
346         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.millisOfSecond(), millis());
347     }
348 
349     /**
350      * Get the millis of day field for this chronology.
351      * 
352      * @return DateTimeField or UnsupportedDateTimeField if unsupported
353      */
354     public DateTimeField millisOfDay() {
355         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.millisOfDay(), millis());
356     }
357 
358     // Second
359     //-----------------------------------------------------------------------
360     /**
361      * Get the seconds duration field for this chronology.
362      * 
363      * @return DurationField or UnsupportedDurationField if unsupported
364      */
365     public DurationField seconds() {
366         return UnsupportedDurationField.getInstance(DurationFieldType.seconds());
367     }
368 
369     /**
370      * Get the second of minute field for this chronology.
371      * 
372      * @return DateTimeField or UnsupportedDateTimeField if unsupported
373      */
374     public DateTimeField secondOfMinute() {
375         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.secondOfMinute(), seconds());
376     }
377 
378     /**
379      * Get the second of day field for this chronology.
380      * 
381      * @return DateTimeField or UnsupportedDateTimeField if unsupported
382      */
383     public DateTimeField secondOfDay() {
384         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.secondOfDay(), seconds());
385     }
386 
387     // Minute
388     //-----------------------------------------------------------------------
389     /**
390      * Get the minutes duration field for this chronology.
391      * 
392      * @return DurationField or UnsupportedDurationField if unsupported
393      */
394     public DurationField minutes() {
395         return UnsupportedDurationField.getInstance(DurationFieldType.minutes());
396     }
397 
398     /**
399      * Get the minute of hour field for this chronology.
400      * 
401      * @return DateTimeField or UnsupportedDateTimeField if unsupported
402      */
403     public DateTimeField minuteOfHour() {
404         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.minuteOfHour(), minutes());
405     }
406 
407     /**
408      * Get the minute of day field for this chronology.
409      * 
410      * @return DateTimeField or UnsupportedDateTimeField if unsupported
411      */
412     public DateTimeField minuteOfDay() {
413         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.minuteOfDay(), minutes());
414     }
415 
416     // Hour
417     //-----------------------------------------------------------------------
418     /**
419      * Get the hours duration field for this chronology.
420      * 
421      * @return DurationField or UnsupportedDurationField if unsupported
422      */
423     public DurationField hours() {
424         return UnsupportedDurationField.getInstance(DurationFieldType.hours());
425     }
426 
427     /**
428      * Get the hour of day (0-23) field for this chronology.
429      * 
430      * @return DateTimeField or UnsupportedDateTimeField if unsupported
431      */
432     public DateTimeField hourOfDay() {
433         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.hourOfDay(), hours());
434     }
435 
436     /**
437      * Get the hour of day (offset to 1-24) field for this chronology.
438      * 
439      * @return DateTimeField or UnsupportedDateTimeField if unsupported
440      */
441     public DateTimeField clockhourOfDay() {
442         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.clockhourOfDay(), hours());
443     }
444 
445     // Halfday
446     //-----------------------------------------------------------------------
447     /**
448      * Get the halfdays duration field for this chronology.
449      * 
450      * @return DurationField or UnsupportedDurationField if unsupported
451      */
452     public DurationField halfdays() {
453         return UnsupportedDurationField.getInstance(DurationFieldType.halfdays());
454     }
455 
456     /**
457      * Get the hour of am/pm (0-11) field for this chronology.
458      * 
459      * @return DateTimeField or UnsupportedDateTimeField if unsupported
460      */
461     public DateTimeField hourOfHalfday() {
462         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.hourOfHalfday(), hours());
463     }
464 
465     /**
466      * Get the hour of am/pm (offset to 1-12) field for this chronology.
467      * 
468      * @return DateTimeField or UnsupportedDateTimeField if unsupported
469      */
470     public DateTimeField clockhourOfHalfday() {
471         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.clockhourOfHalfday(), hours());
472     }
473 
474     /**
475      * Get the AM(0) PM(1) field for this chronology.
476      * 
477      * @return DateTimeField or UnsupportedDateTimeField if unsupported
478      */
479     public DateTimeField halfdayOfDay() {
480         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.halfdayOfDay(), halfdays());
481     }
482 
483     // Day
484     //-----------------------------------------------------------------------
485     /**
486      * Get the days duration field for this chronology.
487      * 
488      * @return DurationField or UnsupportedDurationField if unsupported
489      */
490     public DurationField days() {
491         return UnsupportedDurationField.getInstance(DurationFieldType.days());
492     }
493 
494     /**
495      * Get the day of week field for this chronology.
496      *
497      * <p>DayOfWeek values are defined in
498      * {@link org.joda.time.DateTimeConstants DateTimeConstants}.
499      * They use the ISO definitions, where 1 is Monday and 7 is Sunday.
500      * 
501      * @return DateTimeField or UnsupportedDateTimeField if unsupported
502      */
503     public DateTimeField dayOfWeek() {
504         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.dayOfWeek(), days());
505     }
506 
507     /**
508      * Get the day of month field for this chronology.
509      * 
510      * @return DateTimeField or UnsupportedDateTimeField if unsupported
511      */
512     public DateTimeField dayOfMonth() {
513         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.dayOfMonth(), days());
514     }
515 
516     /**
517      * Get the day of year field for this chronology.
518      * 
519      * @return DateTimeField or UnsupportedDateTimeField if unsupported
520      */
521     public DateTimeField dayOfYear() {
522         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.dayOfYear(), days());
523     }
524 
525     // Week
526     //-----------------------------------------------------------------------
527     /**
528      * Get the weeks duration field for this chronology.
529      * 
530      * @return DurationField or UnsupportedDurationField if unsupported
531      */
532     public DurationField weeks() {
533         return UnsupportedDurationField.getInstance(DurationFieldType.weeks());
534     }
535 
536     /**
537      * Get the week of a week based year field for this chronology.
538      * 
539      * @return DateTimeField or UnsupportedDateTimeField if unsupported
540      */
541     public DateTimeField weekOfWeekyear() {
542         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.weekOfWeekyear(), weeks());
543     }
544 
545     // Weekyear
546     //-----------------------------------------------------------------------
547     /**
548      * Get the weekyears duration field for this chronology.
549      * 
550      * @return DurationField or UnsupportedDurationField if unsupported
551      */
552     public DurationField weekyears() {
553         return UnsupportedDurationField.getInstance(DurationFieldType.weekyears());
554     }
555 
556     /**
557      * Get the year of a week based year field for this chronology.
558      * 
559      * @return DateTimeField or UnsupportedDateTimeField if unsupported
560      */
561     public DateTimeField weekyear() {
562         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.weekyear(), weekyears());
563     }
564 
565     /**
566      * Get the year of a week based year in a century field for this chronology.
567      * 
568      * @return DateTimeField or UnsupportedDateTimeField if unsupported
569      */
570     public DateTimeField weekyearOfCentury() {
571         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.weekyearOfCentury(), weekyears());
572     }
573 
574     // Month
575     //-----------------------------------------------------------------------
576     /**
577      * Get the months duration field for this chronology.
578      * 
579      * @return DurationField or UnsupportedDurationField if unsupported
580      */
581     public DurationField months() {
582         return UnsupportedDurationField.getInstance(DurationFieldType.months());
583     }
584 
585     /**
586      * Get the month of year field for this chronology.
587      * 
588      * @return DateTimeField or UnsupportedDateTimeField if unsupported
589      */
590     public DateTimeField monthOfYear() {
591         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.monthOfYear(), months());
592     }
593 
594     // Year
595     //-----------------------------------------------------------------------
596     /**
597      * Get the years duration field for this chronology.
598      * 
599      * @return DurationField or UnsupportedDurationField if unsupported
600      */
601     public DurationField years() {
602         return UnsupportedDurationField.getInstance(DurationFieldType.years());
603     }
604 
605     /**
606      * Get the year field for this chronology.
607      * 
608      * @return DateTimeField or UnsupportedDateTimeField if unsupported
609      */
610     public DateTimeField year() {
611         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.year(), years());
612     }
613 
614     /**
615      * Get the year of era field for this chronology.
616      * 
617      * @return DateTimeField or UnsupportedDateTimeField if unsupported
618      */
619     public DateTimeField yearOfEra() {
620         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.yearOfEra(), years());
621     }
622 
623     /**
624      * Get the year of century field for this chronology.
625      * 
626      * @return DateTimeField or UnsupportedDateTimeField if unsupported
627      */
628     public DateTimeField yearOfCentury() {
629         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.yearOfCentury(), years());
630     }
631 
632     // Century
633     //-----------------------------------------------------------------------
634     /**
635      * Get the centuries duration field for this chronology.
636      * 
637      * @return DurationField or UnsupportedDurationField if unsupported
638      */
639     public DurationField centuries() {
640         return UnsupportedDurationField.getInstance(DurationFieldType.centuries());
641     }
642 
643     /**
644      * Get the century of era field for this chronology.
645      * 
646      * @return DateTimeField or UnsupportedDateTimeField if unsupported
647      */
648     public DateTimeField centuryOfEra() {
649         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.centuryOfEra(), centuries());
650     }
651 
652     // Era
653     //-----------------------------------------------------------------------
654     /**
655      * Get the eras duration field for this chronology.
656      * 
657      * @return DurationField or UnsupportedDurationField if unsupported
658      */
659     public DurationField eras() {
660         return UnsupportedDurationField.getInstance(DurationFieldType.eras());
661     }
662 
663     /**
664      * Get the era field for this chronology.
665      * 
666      * @return DateTimeField or UnsupportedDateTimeField if unsupported
667      */
668     public DateTimeField era() {
669         return UnsupportedDateTimeField.getInstance(DateTimeFieldType.era(), eras());
670     }
671 
672     //-----------------------------------------------------------------------
673     /**
674      * Gets a debugging toString.
675      * 
676      * @return a debugging string
677      */
678     public abstract String toString();
679 
680 }