001/*
002 * The MIT License
003 * Copyright (c) 2012 Microsoft Corporation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a copy
006 * of this software and associated documentation files (the "Software"), to deal
007 * in the Software without restriction, including without limitation the rights
008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009 * copies of the Software, and to permit persons to whom the Software is
010 * furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021 * THE SOFTWARE.
022 */
023
024package microsoft.exchange.webservices.data.misc;
025
026import microsoft.exchange.webservices.data.core.exception.misc.FormatException;
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030/**
031 * The Class TimeSpan.
032 */
033public class TimeSpan implements Comparable<TimeSpan>, java.io.Serializable, Cloneable {
034
035  private static final Log LOG = LogFactory.getLog(TimeSpan.class);
036
037  /**
038   * Constant serialized ID used for compatibility.
039   */
040  private static final long serialVersionUID = 1L;
041
042  /**
043   * The time.
044   */
045  private long time = 0;
046
047  /**
048   * Constant for milliseconds unit and conversion.
049   */
050  public static final int MILLISECONDS = 1;
051
052  /**
053   * Constant for seconds unit and conversion.
054   */
055  public static final int SECONDS = MILLISECONDS * 1000;
056
057  /**
058   * Constant for minutes unit and conversion.
059   */
060  public static final int MINUTES = SECONDS * 60;
061
062  /**
063   * Constant for hours unit and conversion.
064   */
065  public static final int HOURS = MINUTES * 60;
066
067  /**
068   * Constant for days unit and conversion.
069   */
070  public static final int DAYS = HOURS * 24;
071
072  /**
073   * Represents the Maximum TimeSpan value.
074   */
075  public static final TimeSpan MAX_VALUE = new TimeSpan(Long.MAX_VALUE);
076
077  /**
078   * Represents the Minimum TimeSpan value.
079   */
080  public static final TimeSpan MIN_VALUE = new TimeSpan(Long.MIN_VALUE);
081
082  /**
083   * Represents the TimeSpan with a value of zero.
084   */
085  public static final TimeSpan ZERO = new TimeSpan(0L);
086
087  /**
088   * Creates a new instance of TimeSpan based on the number of milliseconds
089   * entered.
090   *
091   * @param time the number of milliseconds for this TimeSpan.
092   */
093  public TimeSpan(long time) {
094    this.time = time;
095  }
096
097  /**
098   * Creates a new TimeSpan object based on the unit and value entered.
099   *
100   * @param units the type of unit to use to create a TimeSpan instance.
101   * @param value the number of units to use to create a TimeSpan instance.
102   */
103  public TimeSpan(int units, long value) {
104    this.time = TimeSpan.toMilliseconds(units, value);
105  }
106
107        /*
108         * public static TimeSpan fromMinutes(int value) { int l = value*60*100;
109         * return l; }
110         */
111
112  /**
113   * Subtracts two Date objects creating a new TimeSpan object.
114   *
115   * @param date1 Date to use as the base value.
116   * @param date2 Date to subtract from the base value.
117   * @return a TimeSpan object representing the difference bewteen the two
118   * Date objects.
119   */
120  public static TimeSpan subtract(java.util.Date date1,
121      java.util.Date date2) {
122    return new TimeSpan(date1.getTime() - date2.getTime());
123  }
124
125  /**
126   * Compares this object with the specified object for order. Returns a
127   * negative integer, zero, or a positive integer as this object is less
128   * than, equal to, or greater than the specified object. Comparison is based
129   * on the number of milliseconds in this TimeSpan.
130   *
131   * @param o the Object to be compared.
132   * @return a negative integer, zero, or a positive integer as this object is
133   * less than, equal to, or greater than the specified object.
134   */
135  public int compareTo(TimeSpan o) {
136    TimeSpan compare = (TimeSpan) o;
137    if (this.time == compare.time) {
138      return 0;
139    }
140    if (this.time > compare.time) {
141      return +1;
142    }
143    return -1;
144  }
145
146  /**
147   * Indicates whether some other object is "equal to" this one. Comparison is
148   * based on the number of milliseconds in this TimeSpan.
149   *
150   * @param obj the reference object with which to compare.
151   * @return if the obj argument is a TimeSpan object with the exact same
152   * number of milliseconds. otherwise.
153   */
154  public boolean equals(Object obj) {
155    if (obj instanceof TimeSpan) {
156      TimeSpan compare = (TimeSpan) obj;
157      if (this.time == compare.time) {
158        return true;
159      }
160    }
161    return false;
162  }
163
164  /**
165   * Returns a hash code value for the object. This method is supported for
166   * the benefit of hashtables such as those provided by
167   * <code>java.util.Hashtable</code>. The method uses the same algorithm as
168   * found in the Long class.
169   *
170   * @return a hash code value for this object.
171   * @see Object#equals(Object)
172   * @see java.util.Hashtable
173   */
174  public int hashCode() {
175    return Long.valueOf(this.time).hashCode();
176  }
177
178  /**
179   * Returns a string representation of the object in the format.
180   * "[-]d.hh:mm:ss.ff" where "-" is an optional sign for negative TimeSpan
181   * values, the "d" component is days, "hh" is hours, "mm" is minutes, "ss"
182   * is seconds, and "ff" is milliseconds
183   *
184   * @return a string containing the number of milliseconds.
185   */
186  public String toString() {
187    StringBuffer sb = new StringBuffer();
188    long millis = this.time;
189    if (millis < 0) {
190      sb.append("-");
191      millis = -millis;
192    }
193
194    long day = millis / TimeSpan.DAYS;
195
196    if (day != 0) {
197      sb.append(day);
198      sb.append("d.");
199      millis = millis % TimeSpan.DAYS;
200    }
201
202    sb.append(millis / TimeSpan.HOURS);
203    millis = millis % TimeSpan.HOURS;
204    sb.append("h:");
205    sb.append(millis / TimeSpan.MINUTES);
206    millis = millis % TimeSpan.MINUTES;
207    sb.append("m:");
208    sb.append(millis / TimeSpan.SECONDS);
209    sb.append("s");
210    millis = millis % TimeSpan.SECONDS;
211    if (millis != 0) {
212      sb.append(".");
213      sb.append(millis);
214      sb.append("ms");
215    }
216    return sb.toString();
217  }
218
219  /**
220   * Returns a clone of this TimeSpan.
221   *
222   * @return a clone of this TimeSpan.
223   */
224  public Object clone() {
225    try {
226      return super.clone();
227    } catch (CloneNotSupportedException e) {
228      LOG.error(e);
229      throw new InternalError();
230    }
231  }
232
233  /**
234   * Indicates whether the value of the TimeSpan is positive.
235   *
236   * @return if the value of the TimeSpan is greater than
237   * zero.  otherwise.
238   */
239  public boolean isPositive() {
240    return this.compareTo(TimeSpan.ZERO) > 0 ? true : false;
241  }
242
243  /**
244   * Indicates whether the value of the TimeSpan is negative.
245   *
246   * @return if the value of the TimeSpan is less than zero.
247   * otherwise.
248   */
249  public boolean isNegative() {
250    return this.compareTo(TimeSpan.ZERO) < 0 ? true : false;
251  }
252
253  /**
254   * Indicates whether the value of the TimeSpan is zero.
255   *
256   * @return if the value of the TimeSpan is equal to zero.
257   * otherwise.
258   */
259  public boolean isZero() {
260    return this.equals(TimeSpan.ZERO);
261  }
262
263  /**
264   * Gets the number of milliseconds.
265   *
266   * @return the number of milliseconds.
267   */
268  public long getMilliseconds() {
269    return (((this.time % TimeSpan.HOURS) % TimeSpan.MINUTES) % TimeSpan.MILLISECONDS)
270        / TimeSpan.MILLISECONDS;
271  }
272
273  /**
274   * Gets the number of milliseconds.
275   *
276   * @return the number of milliseconds.
277   */
278  public long getTotalMilliseconds() {
279    return this.time;
280  }
281
282  /**
283   * Gets the number of seconds (truncated).
284   *
285   * @return the number of seconds.
286   */
287  public long getSeconds() {
288    return ((this.time % TimeSpan.HOURS) % TimeSpan.MINUTES) / TimeSpan.SECONDS;
289  }
290
291  /**
292   * Gets the number of seconds including fractional seconds.
293   *
294   * @return the number of seconds.
295   */
296  public double getTotalSeconds() {
297    return this.time / 1000.0d;
298  }
299
300  /**
301   * Gets the number of minutes (truncated).
302   *
303   * @return the number of minutes.
304   */
305  public long getMinutes() {
306    return (this.time % TimeSpan.HOURS) / TimeSpan.MINUTES;// (this.time/1000)/60;
307  }
308
309  /**
310   * Gets the number of minutes including fractional minutes.
311   *
312   * @return the number of minutes.
313   */
314  public double getTotalMinutes() {
315    return (this.time / 1000.0d) / 60.0d;
316  }
317
318  /**
319   * Gets the number of hours (truncated).
320   *
321   * @return the number of hours.
322   */
323  public long getHours() {
324    return ((this.time / 1000) / 60) / 60;
325  }
326
327  /**
328   * Gets the number of hours including fractional hours.
329   *
330   * @return the number of hours.
331   */
332  public double getTotalHours() {
333    return ((this.time / 1000.0d) / 60.0d) / 60.0d;
334  }
335
336  /**
337   * Gets the number of days (truncated).
338   *
339   * @return the number of days.
340   */
341  public long getDays() {
342    return (((this.time / 1000) / 60) / 60) / 24;
343  }
344
345  /**
346   * Gets the number of days including fractional days.
347   *
348   * @return the number of days.
349   */
350  public double getTotalDays() {
351    return (((this.time / 1000.0d) / 60.0d) / 60.0d) / 24.0d;
352  }
353
354  /**
355   * Adds a TimeSpan to this TimeSpan.
356   *
357   * @param timespan the TimeSpan to add to this TimeSpan.
358   */
359  public void add(TimeSpan timespan) {
360    add(TimeSpan.MILLISECONDS, timespan.time);
361  }
362
363  /**
364   * Adds a number of units to this TimeSpan.
365   *
366   * @param units the type of unit to add to this TimeSpan.
367   * @param value the number of units to add to this TimeSpan.
368   */
369  public void add(int units, long value) {
370    this.time += TimeSpan.toMilliseconds(units, value);
371  }
372
373  /**
374   * Compares two TimeSpan objects.
375   *
376   * @param first  first TimeSpan to use in the compare.
377   * @param second second TimeSpan to use in the compare.
378   * @return a negative integer, zero, or a positive integer as the first
379   * TimeSpan is less than, equal to, or greater than the second
380   * TimeSpan.
381   */
382  public static int compare(TimeSpan first, TimeSpan second) {
383    if (first.time == second.time) {
384      return 0;
385    }
386    if (first.time > second.time) {
387      return +1;
388    }
389    return -1;
390  }
391
392  /**
393   * Returns a TimeSpan whose value is the absolute value of this TimeSpan.
394   *
395   * @return a TimeSpan whose value is the absolute value of this TimeSpan.
396   */
397  public TimeSpan duration() {
398    return new TimeSpan(Math.abs(this.time));
399  }
400
401  /**
402   * Returns a TimeSpan whose value is the negated value of this TimeSpan.
403   *
404   * @return a TimeSpan whose value is the negated value of this TimeSpan.
405   */
406  public TimeSpan negate() {
407    return new TimeSpan(-this.time);
408  }
409
410  /**
411   * Subtracts a TimeSpan from this TimeSpan.
412   *
413   * @param timespan the TimeSpan to subtract from this TimeSpan.
414   */
415  public void subtract(TimeSpan timespan) {
416    subtract(TimeSpan.MILLISECONDS, timespan.time);
417  }
418
419  /**
420   * Subtracts a number of units from this TimeSpan.
421   *
422   * @param units the type of unit to subtract from this TimeSpan.
423   * @param value the number of units to subtract from this TimeSpan.
424   */
425  public void subtract(int units, long value) {
426    add(units, -value);
427  }
428
429  /**
430   * To milliseconds.
431   *
432   * @param units the units
433   * @param value the value
434   * @return the long
435   */
436  private static long toMilliseconds(int units, long value) {
437    long millis;
438    switch (units) {
439      case TimeSpan.MILLISECONDS:
440      case TimeSpan.SECONDS:
441      case TimeSpan.MINUTES:
442      case TimeSpan.HOURS:
443      case TimeSpan.DAYS:
444        millis = value * units;
445        break;
446      default:
447        throw new IllegalArgumentException("Unrecognized units: " + units);
448    }
449    return millis;
450  }
451
452  public static TimeSpan parse(String s) throws Exception {
453    String str = s.trim();
454    String[] st1 = str.split("\\.");
455    int days = 0, millsec = 0, totMillSec = 0;
456    String data = str;
457    switch (st1.length) {
458      case 1:
459        data = str;
460        break;
461      case 2:
462        if (st1[0].split(":").length > 1) {
463          millsec = Integer.parseInt(st1[1]);
464          data = st1[0];
465        } else {
466          days = Integer.parseInt(st1[0]);
467          data = st1[1];
468        }
469        break;
470      case 3:
471        days = Integer.parseInt(st1[0]);
472        data = st1[1];
473        millsec = Integer.parseInt(st1[2]);
474        break;
475      default:
476        throw new FormatException("Bad Format");
477
478    }
479    String[] st = data.split(":");
480    switch (st.length) {
481      case 1:
482        totMillSec = Integer.parseInt(str) * 24 * 60 * 60 * 1000;
483        break;
484      case 2:
485        totMillSec = (Integer.parseInt(st[0]) * 60 * 60 * 1000) + (Integer.parseInt(st[1]) * 60 * 1000);
486        break;
487      case 3:
488        totMillSec = (Integer.parseInt(st[0]) * 60 * 60 * 1000) + (Integer.parseInt(st[1]) * 60 * 1000) + (
489            Integer.parseInt(st[2]) * 1000);
490        break;
491      case 4:
492        totMillSec =
493            (Integer.parseInt(st[0]) * 24 * 60 * 60 * 1000) + (Integer.parseInt(st[1]) * 60 * 60 * 1000) + (
494                Integer.parseInt(st[2]) * 60 * 1000) + (Integer.parseInt(st[3]) * 1000);
495        break;
496      default:
497        throw new FormatException("Bad Format/Overflow");
498    }
499    totMillSec += (days * 24 * 60 * 60 * 1000) + millsec;
500    return new TimeSpan(totMillSec);
501  }
502
503}