View Javadoc

1   /*
2    * Copyright 2004 Carlos Sanchez.
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 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 }