001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.common.math; 017 018import java.math.BigDecimal; 019import java.text.DecimalFormat; 020import java.util.concurrent.TimeUnit; 021import org.modeshape.common.annotation.Immutable; 022 023/** 024 * A number representing an immutable duration of time. This is intended to be used in the same manner as other {@link Number} 025 * subclasses. 026 */ 027@Immutable 028public class Duration extends Number implements Comparable<Duration> { 029 030 private static final long serialVersionUID = 1L; 031 032 private final long durationInNanos; 033 private Components components; 034 035 /** 036 * Create a duration given the number of nanoseconds. 037 * 038 * @param nanos the number of nanoseconds in the duration 039 */ 040 public Duration( long nanos ) { 041 this(nanos, TimeUnit.NANOSECONDS); 042 } 043 044 /** 045 * Create a duration and the time unit. 046 * 047 * @param duration the duration in the supplied time units 048 * @param unit the time unit 049 */ 050 public Duration( long duration, 051 TimeUnit unit ) { 052 this.durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit); 053 } 054 055 @Override 056 public double doubleValue() { 057 return this.durationInNanos; 058 } 059 060 @Override 061 public float floatValue() { 062 return this.durationInNanos; 063 } 064 065 @Override 066 public int intValue() { 067 return (int)this.durationInNanos; 068 } 069 070 @Override 071 public long longValue() { 072 return this.durationInNanos; 073 } 074 075 public BigDecimal toBigDecimal() { 076 return new BigDecimal(this.durationInNanos); 077 } 078 079 /** 080 * Add the supplied duration to this duration, and return the result. 081 * 082 * @param duration the duration to add to this object 083 * @param unit the unit of the duration being added; may not be null 084 * @return the total duration 085 */ 086 public Duration add( long duration, 087 TimeUnit unit ) { 088 long durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit); 089 return new Duration(this.durationInNanos + durationInNanos); 090 } 091 092 /** 093 * Subtract the supplied duration from this duration, and return the result. 094 * 095 * @param duration the duration to subtract from this object 096 * @param unit the unit of the duration being subtracted; may not be null 097 * @return the total duration 098 */ 099 public Duration subtract( long duration, 100 TimeUnit unit ) { 101 long durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit); 102 return new Duration(this.durationInNanos - durationInNanos); 103 } 104 105 /** 106 * Add the supplied duration to this duration, and return the result. A null value is treated as a duration of 0 nanoseconds. 107 * 108 * @param duration the duration to add to this object 109 * @return the total duration 110 */ 111 public Duration add( Duration duration ) { 112 return new Duration(this.durationInNanos + (duration == null ? 0l : duration.longValue())); 113 } 114 115 /** 116 * Subtract the supplied duration from this duration, and return the result. A null value is treated as a duration of 0 117 * nanoseconds. 118 * 119 * @param duration the duration to subtract from this object 120 * @return the resulting duration 121 */ 122 public Duration subtract( Duration duration ) { 123 return new Duration(this.durationInNanos - (duration == null ? 0l : duration.longValue())); 124 } 125 126 /** 127 * Multiply the duration by the supplied scale factor, and return the result. 128 * 129 * @param scale the factor by which the duration is to be scaled. 130 * @return the scaled duration 131 */ 132 public Duration multiply( long scale ) { 133 return new Duration(this.durationInNanos * scale); 134 } 135 136 /** 137 * Divide the duration by the supplied number, and return the result. 138 * 139 * @param denominator the factor by which the duration is to be divided. 140 * @return the resulting duration 141 */ 142 public Duration divide( long denominator ) { 143 return new Duration(this.durationInNanos / denominator); 144 } 145 146 /** 147 * Divide the duration by another duration to calculate the ratio. 148 * 149 * @param duration the duration that this duration is to be divided by; may not be null 150 * @return the resulting duration 151 */ 152 public double divide( Duration duration ) { 153 return this.toBigDecimal().divide(duration.toBigDecimal()).doubleValue(); 154 } 155 156 @Override 157 public int compareTo( Duration that ) { 158 if (that == null) return 1; 159 return this.durationInNanos < that.durationInNanos ? -1 : this.durationInNanos > that.durationInNanos ? 1 : 0; 160 } 161 162 /** 163 * Return the total duration in nanoseconds. 164 * 165 * @return the total duration in nanoseconds 166 */ 167 public long getDuratinInNanoseconds() { 168 return this.durationInNanos; 169 } 170 171 /** 172 * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component. 173 * 174 * @return the total duration in microseconds 175 */ 176 public BigDecimal getDurationInMicroseconds() { 177 return this.toBigDecimal().divide(new BigDecimal(1000)); 178 } 179 180 /** 181 * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component. 182 * 183 * @return the total duration in microseconds 184 */ 185 public BigDecimal getDurationInMilliseconds() { 186 return this.toBigDecimal().divide(new BigDecimal(1000000)); 187 } 188 189 /** 190 * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component. 191 * 192 * @return the total duration in microseconds 193 */ 194 public BigDecimal getDurationInSeconds() { 195 return this.toBigDecimal().divide(new BigDecimal(1000000000)); 196 } 197 198 /** 199 * Return the duration components. 200 * 201 * @return the individual time components of this duration 202 */ 203 public Components getComponents() { 204 if (this.components == null) { 205 // This is idempotent, so no need to synchronize ... 206 207 // Calculate how many seconds, and don't lose any information ... 208 BigDecimal bigSeconds = new BigDecimal(this.durationInNanos).divide(new BigDecimal(1000000000)); 209 // Calculate the minutes, and round to lose the seconds 210 int minutes = bigSeconds.intValue() / 60; 211 // Remove the minutes from the seconds, to just have the remainder of seconds 212 double dMinutes = minutes; 213 double seconds = bigSeconds.doubleValue() - dMinutes * 60; 214 // Now compute the number of full hours, and change 'minutes' to hold the remainding minutes 215 int hours = minutes / 60; 216 minutes = minutes - (hours * 60); 217 this.components = new Components(hours, minutes, seconds); 218 } 219 return this.components; 220 } 221 222 /** 223 * Get the duration value in the supplied unit of time. 224 * 225 * @param unit the unit of time for the returned value; may not be null 226 * @return the value of this duration in the supplied unit of time 227 */ 228 public long getDuration( TimeUnit unit ) { 229 if (unit == null) throw new IllegalArgumentException(); 230 return unit.convert(durationInNanos, TimeUnit.NANOSECONDS); 231 } 232 233 /** 234 * Writes the duration in a form containing hours, minutes, and seconds, including the fractional part of the seconds. The 235 * format is essentially <code>HHH:MM:SS.mmm,mmm</code>, where 236 * <dl> 237 * <dt>HHH</dt> 238 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 239 * <dt>MM</dt> 240 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 241 * <dt>SS</dt> 242 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 243 * <dt>mmm,mmm</dt> 244 * <dd>is the fractional part of seconds, written in at least millisecond precision and up to microsecond precision. The comma 245 * appears if more than 3 digits are used.</dd> 246 * </dl> 247 * 248 * @return a string representation of the duration 249 */ 250 @Override 251 public String toString() { 252 // Insert a comma after the milliseconds, if there are enough digits .. 253 return this.getComponents().toString().replaceAll("(\\d{2}).(\\d{3})(\\d{1,3})", "$1.$2,$3"); 254 } 255 256 /** 257 * Writes the duration in a form containing hours, minutes, and seconds, excluding the fractional part of the seconds. The 258 * format is essentially <code>HHH:MM:SS</code>, where 259 * <dl> 260 * <dt>HHH</dt> 261 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 262 * <dt>MM</dt> 263 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 264 * <dt>SS</dt> 265 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 266 * </dl> 267 * 268 * @return a string representation of the duration 269 */ 270 public String toSimpleString() { 271 // Insert a comma after the milliseconds, if there are enough digits .. 272 return this.getComponents().toSimpleString(); 273 } 274 275 /** 276 * The atomic components of this duration, broken down into whole hours, minutes and (fractional) seconds. 277 */ 278 public class Components { 279 280 private final int hours; 281 private final int minutes; 282 private final double seconds; 283 284 protected Components( int hours, 285 int minutes, 286 double seconds ) { 287 this.hours = hours; 288 this.minutes = minutes; 289 this.seconds = seconds; 290 } 291 292 /** 293 * Get the whole hours in this duration. 294 * 295 * @return the hours 296 */ 297 public int getHours() { 298 return hours; 299 } 300 301 /** 302 * Get the whole minutes in this duration. 303 * 304 * @return the minutes, from 0 to 59. 305 */ 306 public int getMinutes() { 307 return minutes; 308 } 309 310 /** 311 * Get the duration's seconds component. 312 * 313 * @return the number of seconds, including fractional part. 314 */ 315 public double getSeconds() { 316 return seconds; 317 } 318 319 /** 320 * Return the duration as a string in a form containing hours, minutes, and seconds, including the fractional part of the 321 * seconds. The format is essentially <code>HHH:MM:SS.mmm</code>, where 322 * <dl> 323 * <dt>HHH</dt> 324 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 325 * <dt>MM</dt> 326 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 327 * <dt>SS</dt> 328 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 329 * <dt>mmm</dt> 330 * <dd>is the fractional part of seconds, written with 3-6 digits (any trailing zeros are dropped)</dd> 331 * </dl> 332 * 333 * @return a string representation of the duration components 334 */ 335 @Override 336 public String toString() { 337 // Format the string, and have at least 2 digits for the hours, minutes and whole seconds, 338 // and between 3 and 6 digits for the fractional part of the seconds... 339 String result = new DecimalFormat("######00").format(hours) + ':' + new DecimalFormat("00").format(minutes) + ':' 340 + new DecimalFormat("00.000###").format(seconds); 341 return result; 342 } 343 344 /** 345 * Return the duration as a string in a form containing hours, minutes, and seconds, excluding the fractional part of the 346 * seconds. The format is essentially <code>HHH:MM:SS.mmm</code>, where 347 * <dl> 348 * <dt>HHH</dt> 349 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 350 * <dt>MM</dt> 351 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 352 * <dt>SS</dt> 353 * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd> 354 * </dl> 355 * 356 * @return a simple string representation of the duration components 357 */ 358 public String toSimpleString() { 359 // Format the string, and have at least 2 digits for the hours, minutes and whole seconds ... 360 String result = new DecimalFormat("######00").format(hours) + ':' + new DecimalFormat("00").format(minutes) + ':' 361 + new DecimalFormat("00").format(seconds); 362 return result; 363 } 364 } 365 366}