View Javadoc

1   /*
2    *  Copyright 2001-2005 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      private static final long serialVersionUID = 7190739608550251860L;
44  
45      final long iUnitMillis;
46      private final DurationField iDurationField;
47  
48      /**
49       * Constructor.
50       * 
51       * @param type  the field type
52       * @param unitMillis  the average duration unit milliseconds
53       */
54      public ImpreciseDateTimeField(DateTimeFieldType type, long unitMillis) {
55          super(type);
56          iUnitMillis = unitMillis;
57          iDurationField = new LinkedDurationField(type.getDurationType());
58      }
59  
60      public abstract int get(long instant);
61  
62      public abstract long set(long instant, int value);
63  
64      public abstract long add(long instant, int value);
65  
66      public abstract long add(long instant, long value);
67  
68      /**
69       * Computes the difference between two instants, as measured in the units
70       * of this field. Any fractional units are dropped from the result. Calling
71       * getDifference reverses the effect of calling add. In the following code:
72       *
73       * <pre>
74       * long instant = ...
75       * int v = ...
76       * int age = getDifference(add(instant, v), instant);
77       * </pre>
78       *
79       * The value 'age' is the same as the value 'v'.
80       * <p>
81       * The default implementation call getDifferenceAsLong and converts the
82       * return value to an int.
83       *
84       * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
85       * subtract from
86       * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
87       * subtract off the minuend
88       * @return the difference in the units of this field
89       */
90      public int getDifference(long minuendInstant, long subtrahendInstant) {
91          return FieldUtils.safeToInt(getDifferenceAsLong(minuendInstant, subtrahendInstant));
92      }
93  
94      /**
95       * Computes the difference between two instants, as measured in the units
96       * of this field. Any fractional units are dropped from the result. Calling
97       * getDifference reverses the effect of calling add. In the following code:
98       *
99       * <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 }