1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.oness.common.model.temporal;
17
18 import java.util.Arrays;
19 import java.util.Calendar;
20
21 import net.sf.oness.common.all.BaseObject;
22
23 /***
24 * Range of dates
25 *
26 * @author Carlos Sanchez
27 * @version $Revision: 1.5 $
28 */
29 public class DateRange extends BaseObject {
30
31 private Date start, end;
32
33 public static DateRange EMPTY = new DateRange(new Date(1980,
34 Calendar.JANUARY, 1, 0, 0, 0), new Date(1980, Calendar.JANUARY, 1,
35 0, 0, 0));
36
37 public DateRange() {
38 }
39
40 /***
41 * Create a date range from start to end
42 *
43 * @param start
44 * start date
45 * @param end
46 * end date
47 */
48 public DateRange(Date start, Date end) {
49 setStart(start);
50 setEnd(end);
51 }
52
53 /***
54 * Set the start date
55 *
56 * @param start
57 * start date
58 */
59 public void setStart(Date start) {
60 this.start = start;
61 }
62
63 public Date getStart() {
64 return start;
65 }
66
67 /***
68 * Set the end date
69 *
70 * @param end
71 * end date
72 */
73 public void setEnd(Date end) {
74 this.end = end;
75 }
76
77 public Date getEnd() {
78 return end;
79 }
80
81 public boolean isEmpty() {
82 return getStart().equals(getEnd()) || getStart().after(getEnd());
83 }
84
85 public boolean isOpen() {
86 return (getStart().equals(Date.MIN_VALUE))
87 || (getEnd().equals(Date.MAX_VALUE));
88 }
89
90 public boolean includes(Date arg) {
91 return !arg.before(getStart()) && !arg.after(getEnd());
92 }
93
94 public void endNow() {
95 setEnd(Date.now());
96 }
97
98 public static DateRange upTo(Date end) {
99 return new DateRange(Date.MIN_VALUE, end);
100 }
101
102 public static DateRange startingOn(Date start) {
103 return new DateRange(start, Date.MAX_VALUE);
104 }
105
106 public static DateRange startingNow() {
107 return new DateRange(Date.now(), Date.MAX_VALUE);
108 }
109
110 public static DateRange startingAndEndingNow() {
111 Date d = Date.now();
112 return new DateRange(d, d);
113 }
114
115 public boolean overlaps(DateRange arg) {
116 return arg.includes(getStart()) || arg.includes(getEnd())
117 || this.includes(arg);
118 }
119
120 public boolean includes(DateRange arg) {
121 return this.includes(arg.getStart()) && this.includes(arg.getEnd());
122 }
123
124 public DateRange gap(DateRange arg) {
125 if (this.overlaps(arg))
126 return DateRange.EMPTY;
127 DateRange lower, higher;
128 if (this.compareTo(arg) < 0) {
129 lower = this;
130 higher = arg;
131 } else {
132 lower = arg;
133 higher = this;
134 }
135 Date start = (Date) lower.getEnd().clone();
136 Date end = (Date) higher.getStart().clone();
137 start.addDays(1);
138 end.addDays(-1);
139 return new DateRange(start, end);
140 }
141
142 public int compareTo(Object arg) {
143 DateRange other = (DateRange) arg;
144 if (!getStart().equals(other.getStart()))
145 return getStart().compareTo(other.getStart());
146 return getEnd().compareTo(other.getEnd());
147 }
148
149 public boolean abuts(DateRange arg) {
150 return !this.overlaps(arg) && this.gap(arg).isEmpty();
151 }
152
153 public boolean partitionedBy(DateRange[] args) {
154 if (!isContiguous(args))
155 return false;
156 return this.equals(DateRange.combination(args));
157 }
158
159 public static DateRange combination(DateRange[] args) {
160 Arrays.sort(args);
161 if (!isContiguous(args))
162 throw new IllegalArgumentException("Unable to combine date ranges");
163 return new DateRange(args[0].getStart(), args[args.length - 1].getEnd());
164 }
165
166 public static boolean isContiguous(DateRange[] args) {
167 Arrays.sort(args);
168 for (int i = 0; i < args.length - 1; i++) {
169 if (!args[i].abuts(args[i + 1]))
170 return false;
171 }
172 return true;
173 }
174
175 /***
176 * Calculates the length of this range in days.
177 *
178 * @return The number of days in this range. Zero is returned if the dates
179 * are the same. Long.MAX_VALUE is returned if this is an open
180 * range.
181 */
182 public long lengthInDays() {
183 if (isOpen())
184 return Long.MAX_VALUE;
185 Calendar start = getStart().getCalendar();
186 Calendar end = getEnd().getCalendar();
187 int days = end.get(Calendar.DAY_OF_YEAR)
188 - start.get(Calendar.DAY_OF_YEAR);
189 int y2 = end.get(Calendar.YEAR);
190 if (start.get(Calendar.YEAR) != y2) {
191 start = (Calendar) start.clone();
192 do {
193 days += start.getActualMaximum(Calendar.DAY_OF_YEAR);
194 start.add(Calendar.YEAR, 1);
195 } while (start.get(Calendar.YEAR) != y2);
196 }
197 return days;
198 }
199
200 /***
201 * Calculates the length of this range in minutes.
202 *
203 * @return The number of minutes in this range. Zero is returned if the
204 * dates are the same. Long.MAX_VALUE is returned if this is an open
205 * range.
206 */
207 public long lengthInMinutes() {
208 return length(Calendar.MINUTE);
209 }
210
211 /***
212 * Calculate length in milliseconds, seconds, minutes or hours, based in the
213 * field parameter.
214 *
215 * @param field
216 * One of Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE
217 * or Calendar.HOUR
218 * @return the length. Long.MAX_VALUE is returned if this is an open range.
219 */
220 private long length(int field) {
221 if (isOpen())
222 return Long.MAX_VALUE;
223
224 long milliseconds = getEnd().getCalendar().getTimeInMillis()
225 - getStart().getCalendar().getTimeInMillis();
226 if (field == Calendar.MILLISECOND)
227 return milliseconds;
228
229 long seconds = milliseconds / 1000;
230 if (field == Calendar.SECOND)
231 return seconds;
232
233 long minutes = seconds / 60;
234 if (field == Calendar.MINUTE)
235 return minutes;
236
237 long hours = minutes / 60;
238 if (field == Calendar.HOUR)
239 return hours;
240
241 throw new IllegalArgumentException();
242 }
243
244 public String toString() {
245 return getStart() + " - " + getEnd();
246 }
247
248 /***
249 * @see java.lang.Object#clone()
250 */
251 public Object clone() {
252 Date s = (start == null) ? null : (Date) start.clone();
253 Date e = (end == null) ? null : (Date) end.clone();
254 return new DateRange(s, e);
255 }
256
257 }