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 | } |