root/trunk/src/java/org/jcoderz/commons/types/Period.java

Revision 1011, 20.1 kB (checked in by amandel, 7 months ago)

Aligned svn keyword settings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.commons.types;
34
35
36import java.io.Serializable;
37import java.util.Calendar;
38
39import org.jcoderz.commons.ArgumentMalformedException;
40import org.jcoderz.commons.util.Assert;
41import org.jcoderz.commons.util.HashCodeUtil;
42
43
44
45/**
46 * A <code>Period</code> data type represents a period in time.
47 * <p>
48 * There are two types of periods. One that is day bound and the other one
49 * is milli-second bound. The day bound period has just set the hours, minutes,
50 * seconds, and milli-seconds set to zero. In other words, the first one is
51 * in day resolution whereas the second one is in milli-second resolution.
52 * <p>
53 * TODO: Refine
54 *
55 * @author Michael Rumpf
56 */
57public final class Period
58      implements Serializable
59{
60   /** The name of this type. */
61   public static final String TYPE_NAME = "Period";
62
63   /** use this serialVersionUID for serialization. */
64   static final long serialVersionUID = 128446267044986064L;
65
66   private static final String DATE_PARAMETER = "date";
67
68   /** Lazy init hash code. */
69   private transient int mHashCode = 0;
70
71   /** The start time of the period. */
72   private final Date mStartTime;
73
74   /** The end time of the period. */
75   private final Date mEndTime;
76
77   /**
78    * This is for easy creation of objects from two longs.
79    *
80    * @param start The start date.
81    * @param end The end date.
82    */
83   private Period (Date start, Date end)
84   {
85      Assert.notNull(start, "start date");
86      Assert.notNull(end, "end date");
87      if (end.compareTo(start) < 0)
88      {
89         throw new ArgumentMalformedException(TYPE_NAME,
90               "start: " + start + " end:" + end,
91               "The end date must be larger or equal to the start date!");
92      }
93      mStartTime = start;
94      mEndTime = end;
95   }
96
97   /**
98    * A factory method to create a period from two timestamps of the type
99    * {@link org.jcoderz.commons.types.Date}.
100    *
101    * @param start The start time to create the period from.
102    * @param end The end time to create the period from.
103    * @return The period created by the two timestamps.
104    * @throws ArgumentMalformedException when the end date is before the start
105    * date.
106    */
107   public static Period createPeriod (final Date start, final Date end)
108         throws ArgumentMalformedException
109   {
110      return new Period(start, end);
111   }
112
113   /**
114    * A factory method to create a period from two timestamps where the
115    * hours, minutes, seconds, and milli-seconds are stripped off.
116    *
117    * @param start The start time to create the period from.
118    * @param end The end time to create the period from.
119    * @return The period created by the two timestamps.
120    * @throws ArgumentMalformedException when the end date is before the start
121    * date.
122    */
123   public static Period createDayPeriod (Date start, Date end)
124         throws ArgumentMalformedException
125   {
126      Assert.notNull(start, "start date");
127      Assert.notNull(end, "end date");
128
129      final Calendar s = getCalendarInstance(start);
130      int year  = s.get(Calendar.YEAR);
131      int month = s.get(Calendar.MONTH);
132      int day   = s.get(Calendar.DAY_OF_MONTH);
133      s.set(year, month, day, s.getMinimum(Calendar.HOUR_OF_DAY),
134            s.getMinimum(Calendar.MINUTE), s.getMinimum(Calendar.SECOND));
135      s.set(Calendar.MILLISECOND, s.getMinimum(Calendar.MILLISECOND));
136      final Calendar e = getCalendarInstance(end);
137      year  = e.get(Calendar.YEAR);
138      month = e.get(Calendar.MONTH);
139      day   = e.get(Calendar.DAY_OF_MONTH);
140      e.set(year, month, day, e.getMaximum(Calendar.HOUR_OF_DAY),
141            e.getMaximum(Calendar.MINUTE), e.getMaximum(Calendar.SECOND));
142      e.set(Calendar.MILLISECOND, e.getMaximum(Calendar.MILLISECOND));
143      return new Period(
144            Date.fromUtilDate(s.getTime()), Date.fromUtilDate(e.getTime()));
145   }
146
147   /**
148    * A factory method to create a day period. This period starts and ends on
149    * the same day as the given <code>date</code>. The duration of this period
150    * is exactly {@link Date#MILLIS_PER_DAY} milliseconds.
151    * Example: for the date 2004-09-03T11:52:03.437Z this method will return the
152    * period 2004-09-03T00:00:00.000Z-2004-09-03T23:59:59.999Z.
153    *
154    * @param date The date that falls within the returned period.
155    * @return The period created by the two timestamps.
156    * @throws ArgumentMalformedException when the parameter <code>date</code> is
157    * null.
158    */
159   public static Period createDayPeriod (Date date)
160         throws ArgumentMalformedException
161   {
162      return Period.createDayPeriod(date, date);
163   }
164
165   /**
166    * A factory method to create a month period. This period starts and ends on
167    * the same month as the given <code>date</code>. The duration of this period
168    * is exactly the duration of the month of the <code>date</code>.
169    * Example: for the date 2004-09-03T11:52:03.437Z this method will return a
170    * period 2004-09-01T00:00:00.000Z-2004-09-30T23:59:59.999Z.
171    *
172    * @param date The date that falls within the returned period.
173    * @return The period created by the two timestamps.
174    * @throws ArgumentMalformedException when the parameter <code>date</code> is
175    * null.
176    */
177   public static Period createMonthPeriod (Date date)
178         throws ArgumentMalformedException
179   {
180      Assert.notNull(date, DATE_PARAMETER);
181
182      final Calendar c = getCalendarInstance(date);
183      c.set(Calendar.DAY_OF_MONTH, c.getMinimum(Calendar.DAY_OF_MONTH));
184      final Date s = new Date(c.getTimeInMillis());
185      c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
186      final Date e = new Date(c.getTimeInMillis());
187      return Period.createDayPeriod(s, e);
188   }
189
190   /**
191    * Returns the start date of the period.
192    *
193    * @return A Date marking the start of the period.
194    */
195   public Date getStartTime ()
196   {
197      return mStartTime;
198   }
199
200   /**
201    * Returns the end date of the period.
202    *
203    * @return A Date marking the end of the period.
204    */
205   public Date getEndTime ()
206   {
207      return mEndTime;
208   }
209
210   /**
211    * Returns the intersection of two periods.
212    *
213    * @param other The period that will be intersected with this instance.
214    * @return A new period that is the intersection between the two periods.
215    * Null is returned in case there is no intersection between the two periods.
216    */
217   public Period intersection (final Period other)
218   {
219      final Date start = Date.latest(mStartTime, other.mStartTime);
220      final Date end = Date.earliest(mEndTime, other.mEndTime);
221
222      final Period result;
223      if (start.beforeOrEqual(end))
224      {
225         result = new Period(start, end);
226      }
227      else
228      {
229         result = null;
230      }
231      return result;
232   }
233
234   /**
235    * Returns the union of two periods.
236    *
237    * @param other The period to create a union period with this instance.
238    * @return A new period that contains the intersection between the two
239    * periods or null if the two periods do not intersect.
240    */
241   public Period union (final Period other)
242   {
243      final Period result;
244      if (overlap(other))
245      {
246         final Date start = Date.earliest(mStartTime, other.mStartTime);
247         final Date end = Date.latest(mEndTime, other.mEndTime);
248         result = new Period(start, end);
249      }
250      else
251      {
252         result = null;
253      }
254      return result;
255   }
256
257   /**
258    * Checks whether a specified date falls into the period defined by the
259    * instance.
260    *
261    * @param date The date that is checked whether it lies in the period.
262    * @return true when the date falls into the period, false otherwise.
263    */
264   public boolean isIncluded (final Date date)
265   {
266      return mStartTime.beforeOrEqual(date) && mEndTime.afterOrEqual(date);
267   }
268
269   /**
270    * Checks whether a specified period falls into the period defined by the
271    * instance.
272    *
273    * @param other The period that is checked whether it lies in this period.
274    * @return true when the period falls into the period, false otherwise.
275    */
276   public boolean isIncluded (final Period other)
277   {
278      return mStartTime.beforeOrEqual(other.getStartTime())
279            && mEndTime.afterOrEqual(other.getEndTime());
280   }
281
282   /**
283    * Checks whether a specified period overlaps with this period.
284    *
285    * @param period The period to check for overlap with this period.
286    * @return true when the periods overlap, false otherwise.
287    */
288   public boolean overlap (final Period period)
289   {
290      return isIncluded(period.mStartTime) || period.isIncluded(mStartTime);
291   }
292
293   /**
294    * Returns the start time for the next period.
295    *
296    * @return the start time for the next period.
297    */
298   public Date getNextPeriodStartTime ()
299   {
300      return mEndTime.plus(1);
301   }
302
303   /**
304    * Returns the end time of the previous period.
305    *
306    * @return the end time of the previous period.
307    */
308   public Date getPrevPeriodEndTime ()
309   {
310      return mStartTime.minus(1);
311   }
312
313   /**
314    * Returns the duration of this Period in milliseconds.
315    *
316    * @return The duration of this Period in milliseconds.
317    */
318   public long duration ()
319   {
320      return mEndTime.getTime() - mStartTime.getTime();
321   }
322
323   /**
324    * Returns the next Period. The duration of the next period is exactly the
325    * same as duration of this Period. The next period starts 1 millisecond
326    * after the end time of this period.
327    *
328    * @return The next Period.
329    */
330   public Period next ()
331   {
332      return new Period(getNextPeriodStartTime(),
333            getNextPeriodStartTime().plus(duration()));
334   }
335
336   /**
337    * Returns the previous Period. The duration of the previous period is
338    * exactly the same as duration of this Period. The previous period ends 1
339    * millisecond before the start time of this period.
340    *
341    * @return The previous Period.
342    */
343   public Period previous ()
344   {
345      return new Period(getPrevPeriodEndTime().minus(duration()),
346            getPrevPeriodEndTime());
347   }
348
349   /**
350    * Returns a Period object whose start/end time lies exactly
351    * <code>offset</code> milliseconds after the start/end time of this period.
352    *
353    * @param offset The offset in milliseconds.
354    *
355    * @return The Period object whose start/end time lies exactly
356    * <code>offset</code> milliseconds after the start/end time of this period.
357    */
358   public Period plus (long offset)
359   {
360      return new Period(mStartTime.plus(offset), mEndTime.plus(offset));
361   }
362
363   /**
364    * Returns the next clock hour period, that lies after the
365    * given <code>date</code>.
366    *
367    * Example: for the date 2004-09-03T11:52:03 this method will return the
368    * period 2004-09-03T12:00:00.000Z-2004-09-03T12:59:59.999Z.
369    *
370    * @param date The date to get the next clock hour period.
371    *
372    * @return The next clock hour period, that lies after the given
373    * <code>date</code>.
374    */
375   public static Period nextHour (Date date)
376   {
377      Assert.notNull(date, DATE_PARAMETER);
378      final Calendar s = getCalendarInstance(date);
379      s.add(Calendar.HOUR, 1);
380      resetMinorFields(s);
381      return createPeriod(s, Date.MILLIS_PER_HOUR);
382   }
383
384   /**
385    * Returns the next clock hour period, that lies after this period.
386    * This method returns Period.nextHour(getEndTime()).
387    *
388    * @return the next clock hour period, that lies after this period.
389    */
390   public Period nextHour ()
391   {
392      return Period.nextHour(mEndTime);
393   }
394
395   /**
396    * Returns the previous clock hour period, that lies befor the
397    * given <code>date</code>.
398    *
399    * Example: for the date 2004-09-03T11:52:03 this method will return the
400    * period 2004-09-03T10:00:00.000Z-2004-09-03T10:59:59.999Z.
401    *
402    * @param date The date to get the previous clock hour period.
403    *
404    * @return the previous clock hour period, that lies befor the
405    * given <code>date</code>.
406    */
407   public static Period previousHour (Date date)
408   {
409      Assert.notNull(date, DATE_PARAMETER);
410      final Calendar s = getCalendarInstance(date);
411      s.add(Calendar.HOUR, -1);
412      resetMinorFields(s);
413      return createPeriod(s, Date.MILLIS_PER_HOUR);
414   }
415
416   /**
417    * Returns the next clock hour period, that lies this this period.
418    * This method returns Period.previousHour(getSrartTime()).
419    *
420    * @return the next clock hour period, that lies before this period.
421    */
422   public Period previousHour ()
423   {
424      return Period.previousHour(mStartTime);
425   }
426
427   /**
428    * Returns the day period, that lies after the given <code>date</code>.
429    *
430    * Example: for the date 2004-09-03T11:52:03 this method will return the
431    * period 2004-09-04T00:00:00.000Z-2004-09-04T23:59:59.999Z.
432    *
433    * @param date The date to get the next day period.
434    *
435    * @return The day period, that lies after the given <code>date</code>.
436    */
437   public static Period nextDay (Date date)
438   {
439      Assert.notNull(date, DATE_PARAMETER);
440      final Date nextDay = date.plus(Date.MILLIS_PER_DAY);
441      return Period.createDayPeriod(nextDay, nextDay);
442   }
443
444   /**
445    * Returns the day period, that lies after this period.
446    * This method returns Period.nextDay(getEndTime()).
447    * @see #nextDay(Date)
448    *
449    * @return The day period, that lies after the given <code>date</code>.
450    */
451   public Period nextDay ()
452   {
453      return Period.nextDay(mEndTime);
454   }
455
456   /**
457    * Returns the day period, that lies before the given <code>date</code>.
458    *
459    * Example: for the date 2004-09-03T11:52:03 this method will return the
460    * period 2004-09-02T00:00:00.000Z-2004-09-02T23:59:59.999Z.
461    *
462    * @param date The date to get the previous day period.
463    *
464    * @return The day period, that lies before the given <code>date</code>.
465    */
466   public static Period previousDay (Date date)
467   {
468      Assert.notNull(date, DATE_PARAMETER);
469      final Date prevDay = date.minus(Date.MILLIS_PER_DAY);
470      return Period.createDayPeriod(prevDay, prevDay);
471   }
472
473   /**
474    * Returns the day period, that lies before this period.
475    * This method returns Period.previousDay(getStartTime()).
476    * @see #previousDay(Date)
477    *
478    * @return The the day period, that lies before this period.
479    */
480   public Period previousDay ()
481   {
482      return Period.previousDay(mStartTime);
483   }
484
485   /**
486    * Returns the month period, that lies after the given <code>date</code>.
487    *
488    * Example: for the date 2004-09-03T11:52:03 this method will return the
489    * period 2004-10-01T00:00:00.000Z-2004-10-31T23:59:59.999Z.
490    *
491    * @param date The date to get the next month period.
492    *
493    * @return The the month period, that lies after the given <code>date</code>.
494    */
495   public static Period nextMonth (Date date)
496   {
497      Assert.notNull(date, DATE_PARAMETER);
498      final Calendar c = getCalendarInstance(date);
499      c.add(Calendar.MONTH, 1);
500      return Period.createMonthPeriod(new Date(c.getTimeInMillis()));
501   }
502
503   /**
504    * Returns the month period, that lies after this period.
505    * This method returns Period.nextMonth(getEndTime()).
506    * @see #nextMonth(Date)
507    *
508    * @return The the month period, that lies after this period.
509    */
510   public Period nextMonth ()
511   {
512      return Period.nextMonth(mEndTime);
513   }
514
515   /**
516    * Returns the month period, that lies before the given <code>date</code>.
517    *
518    * Example: for the date 2004-09-03T11:52:03 this method will return the
519    * period 2004-08-01T00:00:00.000Z-2004-08-31T23:59:59.999Z.
520    *
521    * @param date The date to get the previous month period.
522    *
523    * @return The the month period, that lies before the given
524    * <code>date</code>.
525    */
526   public static Period previousMonth (Date date)
527   {
528      Assert.notNull(date, DATE_PARAMETER);
529      final Calendar c = getCalendarInstance(date);
530      c.add(Calendar.MONTH, -1);
531      return Period.createMonthPeriod(new Date(c.getTimeInMillis()));
532   }
533
534   /**
535    * Returns the month period, that lies before this period.
536    * This method returns Period.previousMonth(getStartTime()).
537    * @see #previousMonth(Date)
538    *
539    * @return The the month period, that lies before this period.
540    */
541   public Period previousMonth ()
542   {
543      return Period.previousMonth(mStartTime);
544   }
545
546   /**
547    * Returns a Period object whose start/end time lies exactly
548    * <code>offset</code> milliseconds before the start/end time of this period.
549    *
550    * @param offset The offset in milliseconds.
551    *
552    * @return The Period object whose start/end time lies exactly
553    * <code>offset</code> milliseconds before the start/end time of this period.
554    */
555   public Period minus (long offset)
556   {
557      return new Period(mStartTime.minus(offset), mEndTime.minus(offset));
558   }
559
560   /**
561    * Checks if this period is before the given period.
562    * @param other the period to compare with.
563    * @return true, if this period is before the given period.
564    */
565   public boolean before (final Period other)
566   {
567      Assert.notNull(other, "other");
568
569      return mEndTime.before(other.getStartTime());
570   }
571
572   /**
573    * Checks if this period is after the given period.
574    * @param other the period to compare with.
575    * @return true, if this period is after the given period.
576    */
577   public boolean after (final Period other)
578   {
579      Assert.notNull(other, "other");
580
581      return mStartTime.after(other.getEndTime());
582   }
583
584   /** {@inheritDoc} */
585   public boolean equals (Object o)
586   {
587      final boolean result;
588      if (o instanceof Period)
589      {
590         final Period other = (Period) o;
591         result = mStartTime.equals(other.mStartTime)
592               && mEndTime.equals(other.mEndTime);
593      }
594      else
595      {
596         result = false;
597      }
598      return result;
599   }
600
601   /** {@inheritDoc} */
602   public int hashCode ()
603   {
604      if (mHashCode == 0)
605      {
606         mHashCode = HashCodeUtil.SEED;
607         mHashCode = HashCodeUtil.hash(mHashCode, mStartTime);
608         mHashCode = HashCodeUtil.hash(mHashCode, mEndTime);
609      }
610      return mHashCode;
611   }
612
613   /** {@inheritDoc} */
614   public String toString ()
615   {
616      return mStartTime.toString() + "-" + mEndTime.toString();
617   }
618
619   private static void resetMinorFields (final Calendar s)
620   {
621      s.set(Calendar.MILLISECOND, s.getMinimum(Calendar.MILLISECOND));
622      s.set(Calendar.SECOND, s.getMinimum(Calendar.SECOND));
623      s.set(Calendar.MINUTE, s.getMinimum(Calendar.MINUTE));
624   }
625
626   private static Period createPeriod (final Calendar s,
627         final long periodDuration)
628   {
629      final Date start = new Date(s.getTimeInMillis());
630      return new Period(start, start.plus(periodDuration - 1));
631   }
632
633   static Calendar getCalendarInstance (final Date date)
634   {
635      final Calendar s = Calendar.getInstance(Date.TIME_ZONE);
636      s.setLenient(false);
637      s.clear();
638      s.setTimeInMillis(date.getTime());
639      return s;
640   }
641}
Note: See TracBrowser for help on using the browser.