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    import org.joda.time.DurationFieldType;
021    
022    /**
023     * Abstract datetime field class that defines its own DurationField, which
024     * delegates back into this ImpreciseDateTimeField.
025     * <p>
026     * This DateTimeField is useful for defining DateTimeFields that are composed
027     * of imprecise durations. If both duration fields are precise, then a
028     * {@link PreciseDateTimeField} should be used instead.
029     * <p>
030     * When defining imprecise DateTimeFields where a matching DurationField is
031     * already available, just extend BaseDateTimeField directly so as not to
032     * create redundant DurationField instances.
033     * <p>
034     * ImpreciseDateTimeField is thread-safe and immutable, and its subclasses must
035     * be as well.
036     *
037     * @author Brian S O'Neill
038     * @see PreciseDateTimeField
039     * @since 1.0
040     */
041    public abstract class ImpreciseDateTimeField extends BaseDateTimeField {
042    
043        private static final long serialVersionUID = 7190739608550251860L;
044    
045        final long iUnitMillis;
046        private final DurationField iDurationField;
047    
048        /**
049         * Constructor.
050         * 
051         * @param type  the field type
052         * @param unitMillis  the average duration unit milliseconds
053         */
054        public ImpreciseDateTimeField(DateTimeFieldType type, long unitMillis) {
055            super(type);
056            iUnitMillis = unitMillis;
057            iDurationField = new LinkedDurationField(type.getDurationType());
058        }
059    
060        public abstract int get(long instant);
061    
062        public abstract long set(long instant, int value);
063    
064        public abstract long add(long instant, int value);
065    
066        public abstract long add(long instant, long value);
067    
068        /**
069         * Computes the difference between two instants, as measured in the units
070         * of this field. Any fractional units are dropped from the result. Calling
071         * getDifference reverses the effect of calling add. In the following code:
072         *
073         * <pre>
074         * long instant = ...
075         * int v = ...
076         * int age = getDifference(add(instant, v), instant);
077         * </pre>
078         *
079         * The value 'age' is the same as the value 'v'.
080         * <p>
081         * The default implementation call getDifferenceAsLong and converts the
082         * return value to an int.
083         *
084         * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
085         * subtract from
086         * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
087         * subtract off the minuend
088         * @return the difference in the units of this field
089         */
090        public int getDifference(long minuendInstant, long subtrahendInstant) {
091            return FieldUtils.safeToInt(getDifferenceAsLong(minuendInstant, subtrahendInstant));
092        }
093    
094        /**
095         * Computes the difference between two instants, as measured in the units
096         * of this field. Any fractional units are dropped from the result. Calling
097         * getDifference reverses the effect of calling add. In the following code:
098         *
099         * <pre>
100         * long instant = ...
101         * long v = ...
102         * long age = getDifferenceAsLong(add(instant, v), instant);
103         * </pre>
104         *
105         * The value 'age' is the same as the value 'v'.
106         * <p>
107         * The default implementation performs a guess-and-check algorithm using
108         * getDurationField().getUnitMillis() and the add() method. Subclasses are
109         * encouraged to provide a more efficient implementation.
110         *
111         * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
112         * subtract from
113         * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
114         * subtract off the minuend
115         * @return the difference in the units of this field
116         */
117        public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
118            if (minuendInstant < subtrahendInstant) {
119                return -getDifferenceAsLong(subtrahendInstant, minuendInstant);
120            }
121            
122            long difference = (minuendInstant - subtrahendInstant) / iUnitMillis;
123            if (add(subtrahendInstant, difference) < minuendInstant) {
124                do {
125                    difference++;
126                } while (add(subtrahendInstant, difference) <= minuendInstant);
127                difference--;
128            } else if (add(subtrahendInstant, difference) > minuendInstant) {
129                do {
130                    difference--;
131                } while (add(subtrahendInstant, difference) > minuendInstant);
132            }
133            return difference;
134        }
135    
136        public final DurationField getDurationField() {
137            return iDurationField;
138        }
139    
140        public abstract DurationField getRangeDurationField();
141    
142        public abstract long roundFloor(long instant);
143    
144        protected final long getDurationUnitMillis() {
145            return iUnitMillis;
146        }
147    
148        private final class LinkedDurationField extends BaseDurationField {
149            private static final long serialVersionUID = -203813474600094134L;
150    
151            LinkedDurationField(DurationFieldType type) {
152                super(type);
153            }
154        
155            public boolean isPrecise() {
156                return false;
157            }
158        
159            public long getUnitMillis() {
160                return iUnitMillis;
161            }
162    
163            public int getValue(long duration, long instant) {
164                return ImpreciseDateTimeField.this
165                    .getDifference(instant + duration, instant);
166            }
167    
168            public long getValueAsLong(long duration, long instant) {
169                return ImpreciseDateTimeField.this
170                    .getDifferenceAsLong(instant + duration, instant);
171            }
172            
173            public long getMillis(int value, long instant) {
174                return ImpreciseDateTimeField.this.add(instant, value) - instant;
175            }
176    
177            public long getMillis(long value, long instant) {
178                return ImpreciseDateTimeField.this.add(instant, value) - instant;
179            }
180    
181            public long add(long instant, int value) {
182                return ImpreciseDateTimeField.this.add(instant, value);
183            }
184            
185            public long add(long instant, long value) {
186                return ImpreciseDateTimeField.this.add(instant, value);
187            }
188            
189            public int getDifference(long minuendInstant, long subtrahendInstant) {
190                return ImpreciseDateTimeField.this
191                    .getDifference(minuendInstant, subtrahendInstant);
192            }
193            
194            public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
195                return ImpreciseDateTimeField.this
196                    .getDifferenceAsLong(minuendInstant, subtrahendInstant);
197            }
198        }
199    
200    }