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.field;
017    
018    import org.joda.time.DateTimeFieldType;
019    import org.joda.time.DurationField;
020    
021    /**
022     * Precise datetime field, which has a precise unit duration field.
023     * <p>
024     * PreciseDurationDateTimeField is thread-safe and immutable, and its
025     * subclasses must be as well.
026     *
027     * @author Brian S O'Neill
028     * @since 1.0
029     */
030    public abstract class PreciseDurationDateTimeField extends BaseDateTimeField {
031    
032        private static final long serialVersionUID = 5004523158306266035L;
033    
034        /** The fractional unit in millis */
035        final long iUnitMillis;
036    
037        private final DurationField iUnitField;
038    
039        /**
040         * Constructor.
041         * 
042         * @param type  the field type
043         * @param unit  precise unit duration, like "days()".
044         * @throws IllegalArgumentException if duration field is imprecise
045         * @throws IllegalArgumentException if unit milliseconds is less than one
046         */
047        public PreciseDurationDateTimeField(DateTimeFieldType type, DurationField unit) {
048            super(type);
049    
050            if (!unit.isPrecise()) {
051                throw new IllegalArgumentException("Unit duration field must be precise");
052            }
053    
054            iUnitMillis = unit.getUnitMillis();
055            if (iUnitMillis < 1) {
056                throw new IllegalArgumentException("The unit milliseconds must be at least 1");
057            }
058    
059            iUnitField = unit;
060        }
061    
062        /**
063         * Returns false by default.
064         */
065        public boolean isLenient() {
066            return false;
067        }
068    
069        /**
070         * Set the specified amount of units to the specified time instant.
071         * 
072         * @param instant  the milliseconds from 1970-01-01T00:00:00Z to set in
073         * @param value  value of units to set.
074         * @return the updated time instant.
075         * @throws IllegalArgumentException if value is too large or too small.
076         */
077        public long set(long instant, int value) {
078            FieldUtils.verifyValueBounds(this, value, getMinimumValue(),
079                                         getMaximumValueForSet(instant, value));
080            return instant + (value - get(instant)) * iUnitMillis;
081        }
082    
083        /**
084         * This method assumes that this field is properly rounded on
085         * 1970-01-01T00:00:00. If the rounding alignment differs, override this
086         * method as follows:
087         * <pre>
088         * return super.roundFloor(instant + ALIGNMENT_MILLIS) - ALIGNMENT_MILLIS;
089         * </pre>
090         */
091        public long roundFloor(long instant) {
092            if (instant >= 0) {
093                return instant - instant % iUnitMillis;
094            } else {
095                instant += 1;
096                return instant - instant % iUnitMillis - iUnitMillis;
097            }
098        }
099    
100        /**
101         * This method assumes that this field is properly rounded on
102         * 1970-01-01T00:00:00. If the rounding alignment differs, override this
103         * method as follows:
104         * <pre>
105         * return super.roundCeiling(instant + ALIGNMENT_MILLIS) - ALIGNMENT_MILLIS;
106         * </pre>
107         */
108        public long roundCeiling(long instant) {
109            if (instant > 0) {
110                instant -= 1;
111                return instant - instant % iUnitMillis + iUnitMillis;
112            } else {
113                return instant - instant % iUnitMillis;
114            }
115        }
116    
117        /**
118         * This method assumes that this field is properly rounded on
119         * 1970-01-01T00:00:00. If the rounding alignment differs, override this
120         * method as follows:
121         * <pre>
122         * return super.remainder(instant + ALIGNMENT_MILLIS);
123         * </pre>
124         */
125        public long remainder(long instant) {
126            if (instant >= 0) {
127                return instant % iUnitMillis;
128            } else {
129                return (instant + 1) % iUnitMillis + iUnitMillis - 1;
130            }
131        }
132    
133        /**
134         * Returns the duration per unit value of this field. For example, if this
135         * field represents "minute of hour", then the duration field is minutes.
136         *
137         * @return the duration of this field, or UnsupportedDurationField if field
138         * has no duration
139         */
140        public DurationField getDurationField() {
141            return iUnitField;
142        }
143    
144        /**
145         * Get the minimum value for the field.
146         * 
147         * @return the minimum value
148         */
149        public int getMinimumValue() {
150            return 0;
151        }
152    
153        public final long getUnitMillis() {
154            return iUnitMillis;
155        }
156    
157        /**
158         * Called by the set method to get the maximum allowed value. By default,
159         * returns getMaximumValue(instant). Override to provide a faster
160         * implementation.
161         */
162        protected int getMaximumValueForSet(long instant, int value) {
163            return getMaximumValue(instant);
164        }
165    
166    }