1 /*
2 * TimeSpan.java
3 *
4 * Created on January 28, 2003, 11:09 AM
5 * ====================================================================
6 *
7 * The JavaRanch Software License, Version 1.0
8 *
9 * Copyright (c) 2003 JavaRanch. All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
12 * following conditions are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
15 * disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided with the distribution.
19 *
20 * 3. The name JavaRanch must not be used to endorse or promote products derived from this software without prior written
21 * permission.
22 *
23 * 4. Products derived from this software may not be called "JavaRanch" nor may "JavaRanch" appear in their names without
24 * prior written permission of JavaRanch.
25 *
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAVARANCH OR ITS
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE. ====================================================================
33 *
34 */
35
36 package com.melloware.jukes.util;
37
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41
42 /**
43 * The value of an instance of TimeSpan represents a period of time.
44 *
45 * TimeSpan can be used in several ways.
46 *
47 * To calculate the difference in time between two dates:
48 * <PRE>TimeSpan timespan = TimeSpan.subtract(date1, date2);</PRE>
49 *
50 * To add five days to a TimeSpan:
51 * <PRE>timspan.add(TimeSpanUnit.DAYS, 5);</PRE>
52 *
53 * To subtract another TimeSpan object from this one:
54 * <PRE>timspan.subtract(timespan2);</PRE>
55 *
56 * @author Thomas Paul
57 */
58 public class TimeSpan
59 implements Comparable,
60 java.io.Serializable,
61 Cloneable {
62
63 private static final Log LOG = LogFactory.getLog(TimeSpan.class);
64
65 /** Represents the Maximum TimeSpan value */
66 public static final TimeSpan MAX_VALUE = new TimeSpan(Long.MAX_VALUE);
67
68 /** Represents the Minimum TimeSpan value */
69 public static final TimeSpan MIN_VALUE = new TimeSpan(Long.MIN_VALUE);
70
71 /** Represents the TimeSpan with a value of zero */
72 public static final TimeSpan ZERO = new TimeSpan(0L);
73
74 private long time = 0;
75
76 /** Creates a new instance of TimeSpan based on the number of milliseconds
77 * entered.
78 *
79 * @param time the number of milliseconds for this TimeSpan.
80 *
81 */
82 public TimeSpan(long time) {
83 this.time = time;
84 }
85
86 /**
87 * Creates a new TimeSpan object based on the unit and value entered.
88 *
89 * @param unit the type of unit to use to create a TimeSpan instance.
90 * @param value the number of units to use to create a TimeSpan instance.
91 */
92 public TimeSpan(TimeSpanUnit unit, long value) {
93 this.time = TimeSpan.toMilliseconds(unit, value);
94 }
95
96 /** Compares two TimeSpan objects.
97 *
98 * @param first first TimeSpan to use in the compare.
99 * @param second second TimeSpan to use in the compare.
100 *
101 * @return a negative integer, zero, or a positive integer as the first
102 * TimeSpan is less than, equal to, or greater than the
103 * second TimeSpan.
104 *
105 */
106 public static int compare(TimeSpan first, TimeSpan second) {
107 if (first.time == second.time) {
108 return 0;
109 }
110 if (first.time > second.time) {
111 return +1;
112 }
113 return -1;
114 }
115
116 /** Subtracts two Date objects creating a new TimeSpan object.
117 *
118 * @param date1 Date to use as the base value.
119 * @param date2 Date to subtract from the base value.
120 *
121 * @return a TimeSpan object representing the difference bewteen the
122 * two Date objects.
123 *
124 */
125 public static TimeSpan subtract(java.util.Date date1, java.util.Date date2) {
126 return new TimeSpan(date1.getTime() - date2.getTime());
127 }
128
129 /** Gets the number of days (truncated).
130 *
131 * @return the number of days.
132 */
133 public long getDays() {
134 return (((this.time / 1000) / 60) / 60) / 24;
135 }
136
137 /** Gets the number of hours (truncated).
138 *
139 * @return the number of hours.
140 */
141 public long getHours() {
142 return ((this.time / 1000) / 60) / 60;
143 }
144
145 /** Gets the number of milliseconds.
146 *
147 * @return the number of milliseconds.
148 */
149 public long getMilliseconds() {
150 return this.time;
151 }
152
153 /** Gets the number of minutes (truncated).
154 *
155 * @return the number of minutes.
156 */
157 public long getMinutes() {
158 return (this.time / 1000) / 60;
159 }
160
161 /**
162 * Returns a string for music formatting for album length. So it would
163 * look like 48:03 or 74:15.
164 * @return the string value of the music duration
165 */
166 public String getMusicDuration() {
167 StringBuffer sb = new StringBuffer();
168 long millis = this.time;
169 if (millis < 0) {
170 sb.append('-');
171 millis = -millis;
172 }
173
174 long day = millis / TimeSpanUnit.TimeSpanConstants.DAYS;
175
176 if (day != 0) {
177 sb.append(day);
178 sb.append("d.");
179 millis = millis % TimeSpanUnit.TimeSpanConstants.DAYS;
180 }
181
182 long hours = millis / TimeSpanUnit.TimeSpanConstants.HOURS;
183
184 if (hours != 0) {
185 sb.append(hours);
186 millis = millis % TimeSpanUnit.TimeSpanConstants.HOURS;
187 sb.append(':');
188 }
189
190 sb.append(StringUtils.leftPad(Long.toString(millis / TimeSpanUnit.TimeSpanConstants.MINUTES), 2, "0"));
191 millis = millis % TimeSpanUnit.TimeSpanConstants.MINUTES;
192 sb.append(':');
193 sb.append(StringUtils.leftPad(Long.toString(millis / TimeSpanUnit.TimeSpanConstants.SECONDS), 2, "0"));
194 return sb.toString();
195 }
196
197 /** Gets the number of seconds (truncated).
198 *
199 * @return the number of seconds.
200 */
201 public long getSeconds() {
202 return this.time / 1000;
203 }
204
205 /** Gets the number of days including fractional days.
206 *
207 * @return the number of days.
208 */
209 public double getTotalDays() {
210 return (((this.time / 1000.0d) / 60.0d) / 60.0d) / 24.0d;
211 }
212
213 /** Gets the number of hours including fractional hours.
214 *
215 * @return the number of hours.
216 */
217 public double getTotalHours() {
218 return ((this.time / 1000.0d) / 60.0d) / 60.0d;
219 }
220
221 /** Gets the number of minutes including fractional minutes.
222 *
223 * @return the number of minutes.
224 */
225 public double getTotalMinutes() {
226 return (this.time / 1000.0d) / 60.0d;
227 }
228
229 /** Gets the number of seconds including fractional seconds.
230 *
231 * @return the number of seconds.
232 */
233 public double getTotalSeconds() {
234 return this.time / 1000.0d;
235 }
236
237 /** Indicates whether the value of the TimeSpan is negative.
238 *
239 * @return <code>true</code> if the value of the TimeSpan is less
240 * than zero.
241 * <code>false</code> otherwise.
242 *
243 */
244 public boolean isNegative() {
245 return (this.compareTo(TimeSpan.ZERO) < 0) ? true : false;
246 }
247
248 /** Indicates whether the value of the TimeSpan is positive.
249 *
250 * @return <code>true</code> if the value of the TimeSpan is greater
251 * than zero.
252 * <code>false</code> otherwise.
253 *
254 */
255 public boolean isPositive() {
256 return (this.compareTo(TimeSpan.ZERO) > 0) ? true : false;
257 }
258
259 /** Indicates whether the value of the TimeSpan is zero.
260 *
261 * @return <code>true</code> if the value of the TimeSpan is equal to zero.
262 * <code>false</code> otherwise.
263 *
264 */
265 public boolean isZero() {
266 return this.equals(TimeSpan.ZERO);
267 }
268
269 /** Adds a TimeSpan to this TimeSpan.
270 *
271 * @param timespan the TimeSpan to add to this TimeSpan.
272 */
273 public void add(TimeSpan timespan) {
274 add(TimeSpanUnit.MILLISECONDS, timespan.time);
275 }
276
277 /**
278 * Adds a number of units to this TimeSpan.
279 *
280 * @param unit the type of unit to add to this TimeSpan.
281 * @param value the number of units to add to this TimeSpan.
282 */
283 public void add(TimeSpanUnit unit, long value) {
284 this.time += TimeSpan.toMilliseconds(unit, value);
285 }
286
287 /** Returns a clone of this TimeSpan.
288 *
289 * @return a clone of this TimeSpan.
290 */
291 public Object clone() {
292 try {
293 return super.clone();
294 } catch (CloneNotSupportedException ex) {
295 LOG.error("CloneNotSupportedException", ex);
296 return null;
297 }
298 }
299
300 /** Compares this object with the specified object for order. Returns a
301 * negative integer, zero, or a positive integer as this object is less
302 * than, equal to, or greater than the specified object. Comparison is
303 * based on the number of milliseconds in this TimeSpan.
304 *
305 * @param o the Object to be compared.
306 *
307 * @return a negative integer, zero, or a positive integer as this object
308 * is less than, equal to, or greater than the specified object.
309 *
310 * @throws ClassCastException if the specified object's type prevents it
311 * from being compared to this Object.
312 *
313 */
314 public int compareTo(Object o) {
315 TimeSpan compare = (TimeSpan)o;
316 if (this.time == compare.time) {
317 return 0;
318 }
319 if (this.time > compare.time) {
320 return +1;
321 }
322 return -1;
323 }
324
325 /** Returns a TimeSpan whose value is the absolute value of this TimeSpan.
326 *
327 * @return a TimeSpan whose value is the absolute value of this TimeSpan.
328 */
329 public TimeSpan duration() {
330 return new TimeSpan(Math.abs(this.time));
331 }
332
333 /** Indicates whether some other object is "equal to" this one.
334 * Comparison is based on the number of milliseconds in this TimeSpan.
335 *
336 * @param obj the reference object with which to compare.
337 *
338 * @return <code>true</code> if the obj argument is a TimeSpan object
339 * with the exact same number of milliseconds.
340 * <code>false</code> otherwise.
341 *
342 */
343 public boolean equals(Object obj) {
344 if (obj instanceof TimeSpan) {
345 TimeSpan compare = (TimeSpan)obj;
346 if (this.time == compare.time) {
347 return true;
348 }
349 }
350 return false;
351 }
352
353 /** Returns a hash code value for the object. This method is
354 * supported for the benefit of hashtables such as those provided by
355 * <code>java.util.Hashtable</code>. The method uses the same
356 * algorithm as found in the Long class.
357 *
358 * @return a hash code value for this object.
359 *
360 * @see java.lang.Object#equals(java.lang.Object)
361 * @see java.util.Hashtable
362 *
363 */
364 public int hashCode() {
365 return Long.valueOf(this.time).hashCode();
366 }
367
368 /** Returns a TimeSpan whose value is the negated value of this TimeSpan.
369 *
370 * @return a TimeSpan whose value is the negated value of this TimeSpan.
371 */
372 public TimeSpan negate() {
373 return new TimeSpan(-this.time);
374 }
375
376 /** Subtracts a TimeSpan from this TimeSpan.
377 *
378 * @param timespan the TimeSpan to subtract from this TimeSpan.
379 */
380 public void subtract(TimeSpan timespan) {
381 subtract(TimeSpanUnit.MILLISECONDS, timespan.time);
382 }
383
384 /**
385 * Subtracts a number of units from this TimeSpan.
386 *
387 * @param unit the type of unit to subtract from this TimeSpan.
388 * @param value the number of units to subtract from this TimeSpan.
389 */
390 public void subtract(TimeSpanUnit unit, long value) {
391 add(unit, -value);
392 }
393
394 /** Returns a string representation of the object in the format
395 * "[-]d.hh:mm:ss.ff" where "-" is an optional sign for negative TimeSpan
396 * values, the "d" component is days, "hh" is hours, "mm" is minutes,
397 * "ss" is seconds, and "ff" is milliseconds
398 *
399 * @return a string containing the number of milliseconds.
400 *
401 */
402 public String toString() {
403 StringBuffer sb = new StringBuffer();
404 long millis = this.time;
405 if (millis < 0) {
406 sb.append('-');
407 millis = -millis;
408 }
409
410 long day = millis / TimeSpanUnit.TimeSpanConstants.DAYS;
411
412 if (day != 0) {
413 sb.append(day);
414 sb.append("d.");
415 millis = millis % TimeSpanUnit.TimeSpanConstants.DAYS;
416 }
417
418 sb.append(millis / TimeSpanUnit.TimeSpanConstants.HOURS);
419 millis = millis % TimeSpanUnit.TimeSpanConstants.HOURS;
420 sb.append("h:");
421 sb.append(StringUtils.leftPad(Long.toString(millis / TimeSpanUnit.TimeSpanConstants.MINUTES), 2, "0"));
422 millis = millis % TimeSpanUnit.TimeSpanConstants.MINUTES;
423 sb.append("m:");
424 sb.append(StringUtils.leftPad(Long.toString(millis / TimeSpanUnit.TimeSpanConstants.SECONDS), 2, "0"));
425 sb.append('s');
426 millis = millis % TimeSpanUnit.TimeSpanConstants.SECONDS;
427 if (millis != 0) {
428 sb.append('.');
429 sb.append(millis);
430 sb.append("ms");
431 }
432 return sb.toString();
433 }
434
435 private static long toMilliseconds(TimeSpanUnit unit, long value) {
436 return value * unit.getValue();
437 }
438
439 /**
440 * Gets the time.
441 * <p>
442 * @return Returns the time.
443 */
444 public long getTime() {
445 return this.time;
446 }
447
448 /**
449 * Sets the time.
450 * <p>
451 * @param aTime The time to set.
452 */
453 public void setTime(long aTime) {
454 this.time = aTime;
455 }
456 }