/* * Copyright 2001-2013 Stephen Colebourne * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.joda.time.chrono; import java.io.Serializable; import org.joda.time.Chronology; import org.joda.time.DateTimeField; import org.joda.time.DateTimeFieldType; import org.joda.time.DateTimeZone; import org.joda.time.DurationField; import org.joda.time.DurationFieldType; import org.joda.time.IllegalFieldValueException; import org.joda.time.ReadablePartial; import org.joda.time.ReadablePeriod; import org.joda.time.field.FieldUtils; import org.joda.time.field.UnsupportedDateTimeField; import org.joda.time.field.UnsupportedDurationField; /** * BaseChronology provides a skeleton implementation for chronology * classes. Many utility methods are defined, but all fields are unsupported. * <p> * BaseChronology is thread-safe and immutable, and all subclasses must be * as well. * * @author Brian S O'Neill * @since 1.0 */ public abstract class BaseChronology extends Chronology implements Serializable { /** Serialization version. */ private static final long serialVersionUID = -7310865996721419676L; /** * Restricted constructor. */ protected BaseChronology() { super(); } /** * Returns the DateTimeZone that this Chronology operates in, or null if * unspecified. * * @return DateTimeZone null if unspecified */ public abstract DateTimeZone getZone(); /** * Returns an instance of this Chronology that operates in the UTC time * zone. Chronologies that do not operate in a time zone or are already * UTC must return themselves. * * @return a version of this chronology that ignores time zones */ public abstract Chronology withUTC(); /** * Returns an instance of this Chronology that operates in any time zone. * * @return a version of this chronology with a specific time zone * @param zone to use, or default if null * @see org.joda.time.chrono.ZonedChronology */ public abstract Chronology withZone(DateTimeZone zone); /** * Returns a datetime millisecond instant, formed from the given year, * month, day, and millisecond values. The set of given values must refer * to a valid datetime, or else an IllegalArgumentException is thrown. * <p> * The default implementation calls upon separate DateTimeFields to * determine the result. Subclasses are encouraged to provide a more * efficient implementation. * * @param year year to use * @param monthOfYear month to use * @param dayOfMonth day of month to use * @param millisOfDay millisecond to use * @return millisecond instant from 1970-01-01T00:00:00Z */ public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth, int millisOfDay) throws IllegalArgumentException { long instant = year().set(0, year); instant = monthOfYear().set(instant, monthOfYear); instant = dayOfMonth().set(instant, dayOfMonth); return millisOfDay().set(instant, millisOfDay); } /** * Returns a datetime millisecond instant, formed from the given year, * month, day, hour, minute, second, and millisecond values. The set of * given values must refer to a valid datetime, or else an * IllegalArgumentException is thrown. * <p> * The default implementation calls upon separate DateTimeFields to * determine the result. Subclasses are encouraged to provide a more * efficient implementation. * * @param year year to use * @param monthOfYear month to use * @param dayOfMonth day of month to use * @param hourOfDay hour to use * @param minuteOfHour minute to use * @param secondOfMinute second to use * @param millisOfSecond millisecond to use * @return millisecond instant from 1970-01-01T00:00:00Z */ public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond) throws IllegalArgumentException { long instant = year().set(0, year); instant = monthOfYear().set(instant, monthOfYear); instant = dayOfMonth().set(instant, dayOfMonth); instant = hourOfDay().set(instant, hourOfDay); instant = minuteOfHour().set(instant, minuteOfHour); instant = secondOfMinute().set(instant, secondOfMinute); return millisOfSecond().set(instant, millisOfSecond); } /** * Returns a datetime millisecond instant, from from the given instant, * hour, minute, second, and millisecond values. The set of given values * must refer to a valid datetime, or else an IllegalArgumentException is * thrown. * <p> * The default implementation calls upon separate DateTimeFields to * determine the result. Subclasses are encouraged to provide a more * efficient implementation. * * @param instant instant to start from * @param hourOfDay hour to use * @param minuteOfHour minute to use * @param secondOfMinute second to use * @param millisOfSecond millisecond to use * @return millisecond instant from 1970-01-01T00:00:00Z */ public long getDateTimeMillis(long instant, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond) throws IllegalArgumentException { instant = hourOfDay().set(instant, hourOfDay); instant = minuteOfHour().set(instant, minuteOfHour); instant = secondOfMinute().set(instant, secondOfMinute); return millisOfSecond().set(instant, millisOfSecond); } //----------------------------------------------------------------------- /** * Validates whether the fields stored in a partial instant are valid. * <p> * This implementation uses {@link DateTimeField#getMinimumValue(ReadablePartial, int[])} * and {@link DateTimeField#getMaximumValue(ReadablePartial, int[])}. * * @param partial the partial instant to validate * @param values the values to validate, not null unless the partial is empty * @throws IllegalArgumentException if the instant is invalid */ public void validate(ReadablePartial partial, int[] values) { // check values in standard range, catching really stupid cases like -1 // this means that the second check will not hit trouble int size = partial.size(); for (int i = 0; i < size; i++) { int value = values[i]; DateTimeField field = partial.getField(i); if (value < field.getMinimumValue()) { throw new IllegalFieldValueException (field.getType(), Integer.valueOf(value), Integer.valueOf(field.getMinimumValue()), null); } if (value > field.getMaximumValue()) { throw new IllegalFieldValueException (field.getType(), Integer.valueOf(value), null, Integer.valueOf(field.getMaximumValue())); } } // check values in specific range, catching really odd cases like 30th Feb for (int i = 0; i < size; i++) { int value = values[i]; DateTimeField field = partial.getField(i); if (value < field.getMinimumValue(partial, values)) { throw new IllegalFieldValueException (field.getType(), Integer.valueOf(value), Integer.valueOf(field.getMinimumValue(partial, values)), null); } if (value > field.getMaximumValue(partial, values)) { throw new IllegalFieldValueException (field.getType(), Integer.valueOf(value), null, Integer.valueOf(field.getMaximumValue(partial, values))); } } } /** * Gets the values of a partial from an instant. * * @param partial the partial instant to use * @param instant the instant to query * @return the values of the partial extracted from the instant */ public int[] get(ReadablePartial partial, long instant) { int size = partial.size(); int[] values = new int[size]; for (int i = 0; i < size; i++) { values[i] = partial.getFieldType(i).getField(this).get(instant); } return values; } /** * Sets the partial into the instant. * * @param partial the partial instant to use * @param instant the instant to update * @return the updated instant */ public long set(ReadablePartial partial, long instant) { for (int i = 0, isize = partial.size(); i < isize; i++) { instant = partial.getFieldType(i).getField(this).set(instant, partial.getValue(i)); } return instant; } //----------------------------------------------------------------------- /** * Gets the values of a period from an interval. * * @param period the period instant to use * @param startInstant the start instant of an interval to query * @param endInstant the start instant of an interval to query * @return the values of the period extracted from the interval */ public int[] get(ReadablePeriod period, long startInstant, long endInstant) { int size = period.size(); int[] values = new int[size]; if (startInstant != endInstant) { for (int i = 0; i < size; i++) { DurationField field = period.getFieldType(i).getField(this); int value = field.getDifference(endInstant, startInstant); if (value != 0) { startInstant = field.add(startInstant, value); } values[i] = value; } } return values; } /** * Gets the values of a period from an interval. * * @param period the period instant to use * @param duration the duration to query * @return the values of the period extracted from the duration */ public int[] get(ReadablePeriod period, long duration) { int size = period.size(); int[] values = new int[size]; if (duration != 0) { long current = 0; for (int i = 0; i < size; i++) { DurationField field = period.getFieldType(i).getField(this); if (field.isPrecise()) { int value = field.getDifference(duration, current); current = field.add(current, value); values[i] = value; } } } return values; } /** * Adds the period to the instant, specifying the number of times to add. * * @param period the period to add, null means add nothing * @param instant the instant to add to * @param scalar the number of times to add * @return the updated instant */ public long add(ReadablePeriod period, long instant, int scalar) { if (scalar != 0 && period != null) { for (int i = 0, isize = period.size(); i < isize; i++) { long value = period.getValue(i); // use long to allow for multiplication (fits OK) if (value != 0) { instant = period.getFieldType(i).getField(this).add(instant, value * scalar); } } } return instant; } //----------------------------------------------------------------------- /** * Adds the duration to the instant, specifying the number of times to add. * * @param instant the instant to add to * @param duration the duration to add * @param scalar the number of times to add * @return the updated instant */ public long add(long instant, long duration, int scalar) { if (duration == 0 || scalar == 0) { return instant; } long add = FieldUtils.safeMultiply(duration, scalar); return FieldUtils.safeAdd(instant, add); } // Millis //----------------------------------------------------------------------- /** * Get the millis duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField millis() { return UnsupportedDurationField.getInstance(DurationFieldType.millis()); } /** * Get the millis of second field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField millisOfSecond() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.millisOfSecond(), millis()); } /** * Get the millis of day field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField millisOfDay() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.millisOfDay(), millis()); } // Second //----------------------------------------------------------------------- /** * Get the seconds duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField seconds() { return UnsupportedDurationField.getInstance(DurationFieldType.seconds()); } /** * Get the second of minute field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField secondOfMinute() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.secondOfMinute(), seconds()); } /** * Get the second of day field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField secondOfDay() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.secondOfDay(), seconds()); } // Minute //----------------------------------------------------------------------- /** * Get the minutes duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField minutes() { return UnsupportedDurationField.getInstance(DurationFieldType.minutes()); } /** * Get the minute of hour field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField minuteOfHour() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.minuteOfHour(), minutes()); } /** * Get the minute of day field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField minuteOfDay() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.minuteOfDay(), minutes()); } // Hour //----------------------------------------------------------------------- /** * Get the hours duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField hours() { return UnsupportedDurationField.getInstance(DurationFieldType.hours()); } /** * Get the hour of day (0-23) field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField hourOfDay() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.hourOfDay(), hours()); } /** * Get the hour of day (offset to 1-24) field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField clockhourOfDay() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.clockhourOfDay(), hours()); } // Halfday //----------------------------------------------------------------------- /** * Get the halfdays duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField halfdays() { return UnsupportedDurationField.getInstance(DurationFieldType.halfdays()); } /** * Get the hour of am/pm (0-11) field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField hourOfHalfday() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.hourOfHalfday(), hours()); } /** * Get the hour of am/pm (offset to 1-12) field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField clockhourOfHalfday() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.clockhourOfHalfday(), hours()); } /** * Get the AM(0) PM(1) field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField halfdayOfDay() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.halfdayOfDay(), halfdays()); } // Day //----------------------------------------------------------------------- /** * Get the days duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField days() { return UnsupportedDurationField.getInstance(DurationFieldType.days()); } /** * Get the day of week field for this chronology. * * <p>DayOfWeek values are defined in * {@link org.joda.time.DateTimeConstants DateTimeConstants}. * They use the ISO definitions, where 1 is Monday and 7 is Sunday. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField dayOfWeek() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.dayOfWeek(), days()); } /** * Get the day of month field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField dayOfMonth() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.dayOfMonth(), days()); } /** * Get the day of year field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField dayOfYear() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.dayOfYear(), days()); } // Week //----------------------------------------------------------------------- /** * Get the weeks duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField weeks() { return UnsupportedDurationField.getInstance(DurationFieldType.weeks()); } /** * Get the week of a week based year field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField weekOfWeekyear() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.weekOfWeekyear(), weeks()); } // Weekyear //----------------------------------------------------------------------- /** * Get the weekyears duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField weekyears() { return UnsupportedDurationField.getInstance(DurationFieldType.weekyears()); } /** * Get the year of a week based year field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField weekyear() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.weekyear(), weekyears()); } /** * Get the year of a week based year in a century field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField weekyearOfCentury() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.weekyearOfCentury(), weekyears()); } // Month //----------------------------------------------------------------------- /** * Get the months duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField months() { return UnsupportedDurationField.getInstance(DurationFieldType.months()); } /** * Get the month of year field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField monthOfYear() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.monthOfYear(), months()); } // Year //----------------------------------------------------------------------- /** * Get the years duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField years() { return UnsupportedDurationField.getInstance(DurationFieldType.years()); } /** * Get the year field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField year() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.year(), years()); } /** * Get the year of era field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField yearOfEra() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.yearOfEra(), years()); } /** * Get the year of century field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField yearOfCentury() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.yearOfCentury(), years()); } // Century //----------------------------------------------------------------------- /** * Get the centuries duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField centuries() { return UnsupportedDurationField.getInstance(DurationFieldType.centuries()); } /** * Get the century of era field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField centuryOfEra() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.centuryOfEra(), centuries()); } // Era //----------------------------------------------------------------------- /** * Get the eras duration field for this chronology. * * @return DurationField or UnsupportedDurationField if unsupported */ public DurationField eras() { return UnsupportedDurationField.getInstance(DurationFieldType.eras()); } /** * Get the era field for this chronology. * * @return DateTimeField or UnsupportedDateTimeField if unsupported */ public DateTimeField era() { return UnsupportedDateTimeField.getInstance(DateTimeFieldType.era(), eras()); } //----------------------------------------------------------------------- /** * Gets a debugging toString. * * @return a debugging string */ public abstract String toString(); }