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;
017    
018    import java.io.Serializable;
019    
020    import org.joda.time.Chronology;
021    import org.joda.time.DateTimeField;
022    import org.joda.time.DateTimeFieldType;
023    import org.joda.time.DateTimeZone;
024    import org.joda.time.DurationField;
025    import org.joda.time.DurationFieldType;
026    import org.joda.time.IllegalFieldValueException;
027    import org.joda.time.ReadablePartial;
028    import org.joda.time.ReadablePeriod;
029    import org.joda.time.field.FieldUtils;
030    import org.joda.time.field.UnsupportedDateTimeField;
031    import org.joda.time.field.UnsupportedDurationField;
032    
033    /**
034     * BaseChronology provides a skeleton implementation for chronology
035     * classes. Many utility methods are defined, but all fields are unsupported.
036     * <p>
037     * BaseChronology is thread-safe and immutable, and all subclasses must be
038     * as well.
039     *
040     * @author Brian S O'Neill
041     * @since 1.0
042     */
043    public abstract class BaseChronology
044            extends Chronology
045            implements Serializable {
046    
047        /** Serialization version. */
048        private static final long serialVersionUID = -7310865996721419676L;
049    
050        /**
051         * Restricted constructor.
052         */
053        protected BaseChronology() {
054            super();
055        }
056    
057        /**
058         * Returns the DateTimeZone that this Chronology operates in, or null if
059         * unspecified.
060         *
061         * @return DateTimeZone null if unspecified
062         */
063        public abstract DateTimeZone getZone();
064    
065        /**
066         * Returns an instance of this Chronology that operates in the UTC time
067         * zone. Chronologies that do not operate in a time zone or are already
068         * UTC must return themself.
069         *
070         * @return a version of this chronology that ignores time zones
071         */
072        public abstract Chronology withUTC();
073        
074        /**
075         * Returns an instance of this Chronology that operates in any time zone.
076         *
077         * @return a version of this chronology with a specific time zone
078         * @param zone to use, or default if null
079         * @see org.joda.time.chrono.ZonedChronology
080         */
081        public abstract Chronology withZone(DateTimeZone zone);
082    
083        /**
084         * Returns a datetime millisecond instant, formed from the given year,
085         * month, day, and millisecond values. The set of given values must refer
086         * to a valid datetime, or else an IllegalArgumentException is thrown.
087         * <p>
088         * The default implementation calls upon separate DateTimeFields to
089         * determine the result. Subclasses are encouraged to provide a more
090         * efficient implementation.
091         *
092         * @param year year to use
093         * @param monthOfYear month to use
094         * @param dayOfMonth day of month to use
095         * @param millisOfDay millisecond to use
096         * @return millisecond instant from 1970-01-01T00:00:00Z
097         */
098        public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
099                                      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    }