1 /*
2  *  Copyright 2001-2013 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.field;
17
18 import org.joda.time.DateTimeFieldType;
19 import org.joda.time.DurationField;
20 import org.joda.time.DurationFieldType;
21
22 /**
23  * Abstract datetime field class that defines its own DurationField, which
24  * delegates back into this ImpreciseDateTimeField.
25  * <p>
26  * This DateTimeField is useful for defining DateTimeFields that are composed
27  * of imprecise durations. If both duration fields are precise, then a
28  * {@link PreciseDateTimeField} should be used instead.
29  * <p>
30  * When defining imprecise DateTimeFields where a matching DurationField is
31  * already available, just extend BaseDateTimeField directly so as not to
32  * create redundant DurationField instances.
33  * <p>
34  * ImpreciseDateTimeField is thread-safe and immutable, and its subclasses must
35  * be as well.
36  *
37  * @author Brian S O'Neill
38  * @see PreciseDateTimeField
39  * @since 1.0
40  */

41 public abstract class ImpreciseDateTimeField extends BaseDateTimeField {
42
43     @SuppressWarnings("unused")
44     private static final long serialVersionUID = 7190739608550251860L;
45
46     final long iUnitMillis;
47     private final DurationField iDurationField;
48
49     /**
50      * Constructor.
51      * 
52      * @param type  the field type
53      * @param unitMillis  the average duration unit milliseconds
54      */

55     public ImpreciseDateTimeField(DateTimeFieldType type, long unitMillis) {
56         super(type);
57         iUnitMillis = unitMillis;
58         iDurationField = new LinkedDurationField(type.getDurationType());
59     }
60
61     public abstract int get(long instant);
62
63     public abstract long set(long instant, int value);
64
65     public abstract long add(long instant, int value);
66
67     public abstract long add(long instant, long value);
68
69     /**
70      * Computes the difference between two instants, as measured in the units
71      * of this field. Any fractional units are dropped from the result. Calling
72      * getDifference reverses the effect of calling add. In the following code:
73      *
74      * <pre>
75      * long instant = ...
76      * int v = ...
77      * int age = getDifference(add(instant, v), instant);
78      * </pre>
79      *
80      * The value 'age' is the same as the value 'v'.
81      * <p>
82      * The default implementation call getDifferenceAsLong and converts the
83      * return value to an int.
84      *
85      * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
86      * subtract from
87      * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
88      * subtract off the minuend
89      * @return the difference in the units of this field
90      */

91     public int getDifference(long minuendInstant, long subtrahendInstant) {
92         return FieldUtils.safeToInt(getDifferenceAsLong(minuendInstant, subtrahendInstant));
93     }
94
95     /**
96      * Computes the difference between two instants, as measured in the units
97      * of this field. Any fractional units are dropped from the result. Calling
98      * getDifference reverses the effect of calling add. In the following code:
99      *
100      * <pre>
101      * long instant = ...
102      * long v = ...
103      * long age = getDifferenceAsLong(add(instant, v), instant);
104      * </pre>
105      *
106      * The value 'age' is the same as the value 'v'.
107      * <p>
108      * The default implementation performs a guess-and-check algorithm using
109      * getDurationField().getUnitMillis() and the add() method. Subclasses are
110      * encouraged to provide a more efficient implementation.
111      *
112      * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
113      * subtract from
114      * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
115      * subtract off the minuend
116      * @return the difference in the units of this field
117      */

118     public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
119         if (minuendInstant < subtrahendInstant) {
120             return -getDifferenceAsLong(subtrahendInstant, minuendInstant);
121         }
122         
123         long difference = (minuendInstant - subtrahendInstant) / iUnitMillis;
124         if (add(subtrahendInstant, difference) < minuendInstant) {
125             do {
126                 difference++;
127             } while (add(subtrahendInstant, difference) <= minuendInstant);
128             difference--;
129         } else if (add(subtrahendInstant, difference) > minuendInstant) {
130             do {
131                 difference--;
132             } while (add(subtrahendInstant, difference) > minuendInstant);
133         }
134         return difference;
135     }
136
137     public final DurationField getDurationField() {
138         return iDurationField;
139     }
140
141     public abstract DurationField getRangeDurationField();
142
143     public abstract long roundFloor(long instant);
144
145     protected final long getDurationUnitMillis() {
146         return iUnitMillis;
147     }
148
149     private final class LinkedDurationField extends BaseDurationField {
150         private static final long serialVersionUID = -203813474600094134L;
151
152         LinkedDurationField(DurationFieldType type) {
153             super(type);
154         }
155     
156         public boolean isPrecise() {
157             return false;
158         }
159     
160         public long getUnitMillis() {
161             return iUnitMillis;
162         }
163
164         public int getValue(long duration, long instant) {
165             return ImpreciseDateTimeField.this
166                 .getDifference(instant + duration, instant);
167         }
168
169         public long getValueAsLong(long duration, long instant) {
170             return ImpreciseDateTimeField.this
171                 .getDifferenceAsLong(instant + duration, instant);
172         }
173         
174         public long getMillis(int value, long instant) {
175             return ImpreciseDateTimeField.this.add(instant, value) - instant;
176         }
177
178         public long getMillis(long value, long instant) {
179             return ImpreciseDateTimeField.this.add(instant, value) - instant;
180         }
181
182         public long add(long instant, int value) {
183             return ImpreciseDateTimeField.this.add(instant, value);
184         }
185         
186         public long add(long instant, long value) {
187             return ImpreciseDateTimeField.this.add(instant, value);
188         }
189         
190         public int getDifference(long minuendInstant, long subtrahendInstant) {
191             return ImpreciseDateTimeField.this
192                 .getDifference(minuendInstant, subtrahendInstant);
193         }
194         
195         public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
196             return ImpreciseDateTimeField.this
197                 .getDifferenceAsLong(minuendInstant, subtrahendInstant);
198         }
199     }
200
201 }
202