View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.common.math;
25  
26  import java.math.BigDecimal;
27  import java.text.DecimalFormat;
28  import java.util.concurrent.TimeUnit;
29  import net.jcip.annotations.Immutable;
30  
31  /**
32   * A number representing an immutable duration of time. This is intended to be used in the same manner as other {@link Number}
33   * subclasses.
34   */
35  @Immutable
36  public class Duration extends Number implements Comparable<Duration> {
37  
38      private static final long serialVersionUID = 1L;
39  
40      private final long durationInNanos;
41      private Components components;
42  
43      /**
44       * Create a duration given the number of nanoseconds.
45       * 
46       * @param nanos the number of nanoseconds in the duration
47       */
48      public Duration( long nanos ) {
49          this(nanos, TimeUnit.NANOSECONDS);
50      }
51  
52      /**
53       * Create a duration and the time unit.
54       * 
55       * @param duration the duration in the supplied time units
56       * @param unit the time unit
57       */
58      public Duration( long duration,
59                       TimeUnit unit ) {
60          this.durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit);
61      }
62  
63      /**
64       * {@inheritDoc}
65       */
66      @Override
67      public double doubleValue() {
68          return this.durationInNanos;
69      }
70  
71      /**
72       * {@inheritDoc}
73       */
74      @Override
75      public float floatValue() {
76          return this.durationInNanos;
77      }
78  
79      /**
80       * {@inheritDoc}
81       */
82      @Override
83      public int intValue() {
84          return (int)this.durationInNanos;
85      }
86  
87      /**
88       * {@inheritDoc}
89       */
90      @Override
91      public long longValue() {
92          return this.durationInNanos;
93      }
94  
95      public BigDecimal toBigDecimal() {
96          return new BigDecimal(this.durationInNanos);
97      }
98  
99      /**
100      * Add the supplied duration to this duration, and return the result.
101      * 
102      * @param duration the duration to add to this object
103      * @param unit the unit of the duration being added; may not be null
104      * @return the total duration
105      */
106     public Duration add( long duration,
107                          TimeUnit unit ) {
108         long durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit);
109         return new Duration(this.durationInNanos + durationInNanos);
110     }
111 
112     /**
113      * Subtract the supplied duration from this duration, and return the result.
114      * 
115      * @param duration the duration to subtract from this object
116      * @param unit the unit of the duration being subtracted; may not be null
117      * @return the total duration
118      */
119     public Duration subtract( long duration,
120                               TimeUnit unit ) {
121         long durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit);
122         return new Duration(this.durationInNanos - durationInNanos);
123     }
124 
125     /**
126      * Add the supplied duration to this duration, and return the result. A null value is treated as a duration of 0 nanoseconds.
127      * 
128      * @param duration the duration to add to this object
129      * @return the total duration
130      */
131     public Duration add( Duration duration ) {
132         return new Duration(this.durationInNanos + (duration == null ? 0l : duration.longValue()));
133     }
134 
135     /**
136      * Subtract the supplied duration from this duration, and return the result. A null value is treated as a duration of 0
137      * nanoseconds.
138      * 
139      * @param duration the duration to subtract from this object
140      * @return the resulting duration
141      */
142     public Duration subtract( Duration duration ) {
143         return new Duration(this.durationInNanos - (duration == null ? 0l : duration.longValue()));
144     }
145 
146     /**
147      * Multiply the duration by the supplied scale factor, and return the result.
148      * 
149      * @param scale the factor by which the duration is to be scaled.
150      * @return the scaled duration
151      */
152     public Duration multiply( long scale ) {
153         return new Duration(this.durationInNanos * scale);
154     }
155 
156     /**
157      * Divide the duration by the supplied number, and return the result.
158      * 
159      * @param denominator the factor by which the duration is to be divided.
160      * @return the resulting duration
161      */
162     public Duration divide( long denominator ) {
163         return new Duration(this.durationInNanos / denominator);
164     }
165 
166     /**
167      * Divide the duration by another duration to calculate the ratio.
168      * 
169      * @param duration the duration that this duration is to be divided by; may not be null
170      * @return the resulting duration
171      */
172     public double divide( Duration duration ) {
173         return this.toBigDecimal().divide(duration.toBigDecimal()).doubleValue();
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     public int compareTo( Duration that ) {
180         if (that == null) return 1;
181         return this.durationInNanos < that.durationInNanos ? -1 : this.durationInNanos > that.durationInNanos ? 1 : 0;
182     }
183 
184     /**
185      * Return the total duration in nanoseconds.
186      * 
187      * @return the total duration in nanoseconds
188      */
189     public long getDuratinInNanoseconds() {
190         return this.durationInNanos;
191     }
192 
193     /**
194      * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component.
195      * 
196      * @return the total duration in microseconds
197      */
198     public BigDecimal getDurationInMicroseconds() {
199         return this.toBigDecimal().divide(new BigDecimal(1000));
200     }
201 
202     /**
203      * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component.
204      * 
205      * @return the total duration in microseconds
206      */
207     public BigDecimal getDurationInMilliseconds() {
208         return this.toBigDecimal().divide(new BigDecimal(1000000));
209     }
210 
211     /**
212      * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component.
213      * 
214      * @return the total duration in microseconds
215      */
216     public BigDecimal getDurationInSeconds() {
217         return this.toBigDecimal().divide(new BigDecimal(1000000000));
218     }
219 
220     /**
221      * Return the duration components.
222      * 
223      * @return the individual time components of this duration
224      */
225     public Components getComponents() {
226         if (this.components == null) {
227             // This is idempotent, so no need to synchronize ...
228 
229             // Calculate how many seconds, and don't lose any information ...
230             BigDecimal bigSeconds = new BigDecimal(this.durationInNanos).divide(new BigDecimal(1000000000));
231             // Calculate the minutes, and round to lose the seconds
232             int minutes = bigSeconds.intValue() / 60;
233             // Remove the minutes from the seconds, to just have the remainder of seconds
234             double dMinutes = minutes;
235             double seconds = bigSeconds.doubleValue() - dMinutes * 60;
236             // Now compute the number of full hours, and change 'minutes' to hold the remainding minutes
237             int hours = minutes / 60;
238             minutes = minutes - (hours * 60);
239             this.components = new Components(hours, minutes, seconds);
240         }
241         return this.components;
242     }
243 
244     /**
245      * Get the duration value in the supplied unit of time.
246      * 
247      * @param unit the unit of time for the returned value; may not be null
248      * @return the value of this duration in the supplied unit of time
249      */
250     public long getDuration( TimeUnit unit ) {
251         if (unit == null) throw new IllegalArgumentException();
252         return unit.convert(durationInNanos, TimeUnit.NANOSECONDS);
253     }
254 
255     /**
256      * Writes the duration in a form containing hours, minutes, and seconds, including the fractional part of the seconds. The
257      * format is essentially <code>HHH:MM:SS.mmm,mmm</code>, where
258      * <dl>
259      * <dt>HHH</dt>
260      * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
261      * <dt>MM</dt>
262      * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
263      * <dt>SS</dt>
264      * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
265      * <dt>mmm,mmm</dt>
266      * <dd>is the fractional part of seconds, written in at least millisecond precision and up to microsecond precision. The comma
267      * appears if more than 3 digits are used.
268      * </dl>
269      * 
270      * @return a string representation of the duration
271      */
272     @Override
273     public String toString() {
274         // Insert a comma after the milliseconds, if there are enough digits ..
275         return this.getComponents().toString().replaceAll("(\\d{2}).(\\d{3})(\\d{1,3})", "$1.$2,$3");
276     }
277 
278     /**
279      * The atomic components of this duration, broken down into whole hours, minutes and (fractional) seconds.
280      */
281     public class Components {
282 
283         private final int hours;
284         private final int minutes;
285         private final double seconds;
286 
287         protected Components( int hours,
288                               int minutes,
289                               double seconds ) {
290             this.hours = hours;
291             this.minutes = minutes;
292             this.seconds = seconds;
293         }
294 
295         /**
296          * Get the whole hours in this duration.
297          * 
298          * @return the hours
299          */
300         public int getHours() {
301             return hours;
302         }
303 
304         /**
305          * Get the whole minutes in this duration.
306          * 
307          * @return the minutes, from 0 to 59.
308          */
309         public int getMinutes() {
310             return minutes;
311         }
312 
313         /**
314          * Get the duration's seconds component.
315          * 
316          * @return the number of seconds, including fractional part.
317          */
318         public double getSeconds() {
319             return seconds;
320         }
321 
322         /**
323          * Return the duration as a string in a form containing hours, minutes, and seconds, including the fractional part of the
324          * seconds. The format is essentially <code>HHH:MM:SS.mmm</code>, where
325          * <dl>
326          * <dt>HHH</dt>
327          * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
328          * <dt>MM</dt>
329          * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
330          * <dt>SS</dt>
331          * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
332          * <dt>mmm</dt>
333          * <dd>is the fractional part of seconds, written with 3-6 digits (any trailing zeros are dropped)
334          * </dl>
335          * 
336          * @return a string representation of the duration components
337          */
338         @Override
339         public String toString() {
340             // Format the string, and have at least 2 digits for the hours, minutes and whole seconds,
341             // and between 3 and 6 digits for the fractional part of the seconds...
342             String result = new DecimalFormat("######00").format(hours) + ':' + new DecimalFormat("00").format(minutes) + ':'
343                             + new DecimalFormat("00.000###").format(seconds);
344             return result;
345         }
346     }
347 
348 }