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