1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 //$Id: Duration.java 759828 2009-03-30 01:26:29Z mrglavas $ 19 20 package javax.xml.datatype; 21 22 import java.math.BigDecimal; 23 import java.math.BigInteger; 24 import java.util.Calendar; 25 import java.util.Date; 26 import java.util.GregorianCalendar; 27 import javax.xml.namespace.QName; 28 29 /** 30 * <p>Immutable representation of a time span as defined in 31 * the W3C XML Schema 1.0 specification.</p> 32 * 33 * <p>A Duration object represents a period of Gregorian time, 34 * which consists of six fields (years, months, days, hours, 35 * minutes, and seconds) plus a sign (+/-) field.</p> 36 * 37 * <p>The first five fields have non-negative (>=0) integers or null 38 * (which represents that the field is not set), 39 * and the seconds field has a non-negative decimal or null. 40 * A negative sign indicates a negative duration.</p> 41 * 42 * <p>This class provides a number of methods that make it easy 43 * to use for the duration datatype of XML Schema 1.0 with 44 * the errata.</p> 45 * 46 * <h2>Order relationship</h2> 47 * <p>Duration objects only have partial order, where two values A and B 48 * maybe either:</p> 49 * <ol> 50 * <li>A<B (A is shorter than B) 51 * <li>A>B (A is longer than B) 52 * <li>A==B (A and B are of the same duration) 53 * <li>A<>B (Comparison between A and B is indeterminate) 54 * </ol> 55 * 56 * <p>For example, 30 days cannot be meaningfully compared to one month. 57 * The {@link #compare(Duration duration)} method implements this 58 * relationship.</p> 59 * 60 * <p>See the {@link #isLongerThan(Duration)} method for details about 61 * the order relationship among <code>Duration</code> objects.</p> 62 * 63 * <h2>Operations over Duration</h2> 64 * <p>This class provides a set of basic arithmetic operations, such 65 * as addition, subtraction and multiplication. 66 * Because durations don't have total order, an operation could 67 * fail for some combinations of operations. For example, you cannot 68 * subtract 15 days from 1 month. See the javadoc of those methods 69 * for detailed conditions where this could happen.</p> 70 * 71 * <p>Also, division of a duration by a number is not provided because 72 * the <code>Duration</code> class can only deal with finite precision 73 * decimal numbers. For example, one cannot represent 1 sec divided by 3.</p> 74 * 75 * <p>However, you could substitute a division by 3 with multiplying 76 * by numbers such as 0.3 or 0.333.</p> 77 * 78 * <h2>Range of allowed values</h2> 79 * <p> 80 * Because some operations of <code>Duration</code> rely on {@link Calendar} 81 * even though {@link Duration} can hold very large or very small values, 82 * some of the methods may not work correctly on such <code>Duration</code>s. 83 * The impacted methods document their dependency on {@link Calendar}. 84 * 85 * 86 * @author <a href="mailto:Joseph.Fialli@Sun.COM">Joseph Fialli</a> 87 * @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a> 88 * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a> 89 * @version $Revision: 759828 $, $Date: 2009-03-29 18:26:29 -0700 (Sun, 29 Mar 2009) $ 90 * @see XMLGregorianCalendar#add(Duration) 91 * @since 1.5 92 */ 93 public abstract class Duration { 94 95 /** 96 * <p>Return the name of the XML Schema date/time type that this instance 97 * maps to. Type is computed based on fields that are set, 98 * i.e. {@link #isSet(DatatypeConstants.Field field)} == <code>true</code>.</p> 99 * 100 * <table border="2" rules="all" cellpadding="2"> 101 * <thead> 102 * <tr> 103 * <th align="center" colspan="7"> 104 * Required fields for XML Schema 1.0 Date/Time Datatypes.<br/> 105 * <i>(timezone is optional for all date/time datatypes)</i> 106 * </th> 107 * </tr> 108 * </thead> 109 * <tbody> 110 * <tr> 111 * <td>Datatype</td> 112 * <td>year</td> 113 * <td>month</td> 114 * <td>day</td> 115 * <td>hour</td> 116 * <td>minute</td> 117 * <td>second</td> 118 * </tr> 119 * <tr> 120 * <td>{@link DatatypeConstants#DURATION}</td> 121 * <td>X</td> 122 * <td>X</td> 123 * <td>X</td> 124 * <td>X</td> 125 * <td>X</td> 126 * <td>X</td> 127 * </tr> 128 * <tr> 129 * <td>{@link DatatypeConstants#DURATION_DAYTIME}</td> 130 * <td></td> 131 * <td></td> 132 * <td>X</td> 133 * <td>X</td> 134 * <td>X</td> 135 * <td>X</td> 136 * </tr> 137 * <tr> 138 * <td>{@link DatatypeConstants#DURATION_YEARMONTH}</td> 139 * <td>X</td> 140 * <td>X</td> 141 * <td></td> 142 * <td></td> 143 * <td></td> 144 * <td></td> 145 * </tr> 146 * </tbody> 147 * </table> 148 * 149 * @return one of the following constants: 150 * {@link DatatypeConstants#DURATION}, 151 * {@link DatatypeConstants#DURATION_DAYTIME} or 152 * {@link DatatypeConstants#DURATION_YEARMONTH}. 153 * 154 * @throws IllegalStateException If the combination of set fields does not match one of the XML Schema date/time datatypes. 155 */ getXMLSchemaType()156 public QName getXMLSchemaType() { 157 158 boolean yearSet = isSet(DatatypeConstants.YEARS); 159 boolean monthSet = isSet(DatatypeConstants.MONTHS); 160 boolean daySet = isSet(DatatypeConstants.DAYS); 161 boolean hourSet = isSet(DatatypeConstants.HOURS); 162 boolean minuteSet = isSet(DatatypeConstants.MINUTES); 163 boolean secondSet = isSet(DatatypeConstants.SECONDS); 164 165 // DURATION 166 if (yearSet 167 && monthSet 168 && daySet 169 && hourSet 170 && minuteSet 171 && secondSet) { 172 return DatatypeConstants.DURATION; 173 } 174 175 // DURATION_DAYTIME 176 if (!yearSet 177 && !monthSet 178 && daySet 179 && hourSet 180 && minuteSet 181 && secondSet) { 182 return DatatypeConstants.DURATION_DAYTIME; 183 } 184 185 // DURATION_YEARMONTH 186 if (yearSet 187 && monthSet 188 && !daySet 189 && !hourSet 190 && !minuteSet 191 && !secondSet) { 192 return DatatypeConstants.DURATION_YEARMONTH; 193 } 194 195 // nothing matches 196 throw new IllegalStateException( 197 "javax.xml.datatype.Duration#getXMLSchemaType():" 198 + " this Duration does not match one of the XML Schema date/time datatypes:" 199 + " year set = " + yearSet 200 + " month set = " + monthSet 201 + " day set = " + daySet 202 + " hour set = " + hourSet 203 + " minute set = " + minuteSet 204 + " second set = " + secondSet 205 ); 206 } 207 208 /** 209 * Returns the sign of this duration in -1,0, or 1. 210 * 211 * @return 212 * -1 if this duration is negative, 0 if the duration is zero, 213 * and 1 if the duration is positive. 214 */ getSign()215 public abstract int getSign(); 216 217 /** 218 * <p>Get the years value of this <code>Duration</code> as an <code>int</code> or <code>0</code> if not present.</p> 219 * 220 * <p><code>getYears()</code> is a convenience method for 221 * {@link #getField(DatatypeConstants.Field field) getField(DatatypeConstants.YEARS)}.</p> 222 * 223 * <p>As the return value is an <code>int</code>, an incorrect value will be returned for <code>Duration</code>s 224 * with years that go beyond the range of an <code>int</code>. 225 * Use {@link #getField(DatatypeConstants.Field field) getField(DatatypeConstants.YEARS)} to avoid possible loss of precision.</p> 226 * 227 * @return If the years field is present, return its value as an <code>int</code>, else return <code>0</code>. 228 */ getYears()229 public int getYears() { 230 return getFieldValueAsInt(DatatypeConstants.YEARS); 231 } 232 233 /** 234 * Obtains the value of the MONTHS field as an integer value, 235 * or 0 if not present. 236 * 237 * This method works just like {@link #getYears()} except 238 * that this method works on the MONTHS field. 239 * 240 * @return Months of this <code>Duration</code>. 241 */ getMonths()242 public int getMonths() { 243 return getFieldValueAsInt(DatatypeConstants.MONTHS); 244 } 245 246 /** 247 * Obtains the value of the DAYS field as an integer value, 248 * or 0 if not present. 249 * 250 * This method works just like {@link #getYears()} except 251 * that this method works on the DAYS field. 252 * 253 * @return Days of this <code>Duration</code>. 254 */ getDays()255 public int getDays() { 256 return getFieldValueAsInt(DatatypeConstants.DAYS); 257 } 258 259 /** 260 * Obtains the value of the HOURS field as an integer value, 261 * or 0 if not present. 262 * 263 * This method works just like {@link #getYears()} except 264 * that this method works on the HOURS field. 265 * 266 * @return Hours of this <code>Duration</code>. 267 * 268 */ getHours()269 public int getHours() { 270 return getFieldValueAsInt(DatatypeConstants.HOURS); 271 } 272 273 /** 274 * Obtains the value of the MINUTES field as an integer value, 275 * or 0 if not present. 276 * 277 * This method works just like {@link #getYears()} except 278 * that this method works on the MINUTES field. 279 * 280 * @return Minutes of this <code>Duration</code>. 281 * 282 */ getMinutes()283 public int getMinutes() { 284 return getFieldValueAsInt(DatatypeConstants.MINUTES); 285 } 286 287 /** 288 * Obtains the value of the SECONDS field as an integer value, 289 * or 0 if not present. 290 * 291 * This method works just like {@link #getYears()} except 292 * that this method works on the SECONDS field. 293 * 294 * @return seconds in the integer value. The fraction of seconds 295 * will be discarded (for example, if the actual value is 2.5, 296 * this method returns 2) 297 */ getSeconds()298 public int getSeconds() { 299 return getFieldValueAsInt(DatatypeConstants.SECONDS); 300 } 301 302 /** 303 * <p>Returns the length of the duration in milliseconds.</p> 304 * 305 * <p>If the seconds field carries more digits than millisecond order, 306 * those will be simply discarded (or in other words, rounded to zero.) 307 * For example, for any Calendar value <code>x</code>,</p> 308 * <pre> 309 * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>. 310 * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>. 311 * </pre> 312 * 313 * <p> 314 * Note that this method uses the {@link #addTo(Calendar)} method, 315 * which may work incorrectly with <code>Duration</code> objects with 316 * very large values in its fields. See the {@link #addTo(Calendar)} 317 * method for details. 318 * 319 * @param startInstant 320 * The length of a month/year varies. The <code>startInstant</code> is 321 * used to disambiguate this variance. Specifically, this method 322 * returns the difference between <code>startInstant</code> and 323 * <code>startInstant+duration</code> 324 * 325 * @return milliseconds between <code>startInstant</code> and 326 * <code>startInstant</code> plus this <code>Duration</code> 327 * 328 * @throws NullPointerException if <code>startInstant</code> parameter 329 * is null. 330 * 331 */ getTimeInMillis(final Calendar startInstant)332 public long getTimeInMillis(final Calendar startInstant) { 333 Calendar cal = (Calendar) startInstant.clone(); 334 addTo(cal); 335 return getCalendarTimeInMillis(cal) 336 - getCalendarTimeInMillis(startInstant); 337 } 338 339 /** 340 * <p>Returns the length of the duration in milliseconds.</p> 341 * 342 * <p>If the seconds field carries more digits than millisecond order, 343 * those will be simply discarded (or in other words, rounded to zero.) 344 * For example, for any <code>Date</code> value <code>x</code>,</p> 345 * <pre> 346 * <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>. 347 * <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>. 348 * </pre> 349 * 350 * <p> 351 * Note that this method uses the {@link #addTo(Date)} method, 352 * which may work incorrectly with <code>Duration</code> objects with 353 * very large values in its fields. See the {@link #addTo(Date)} 354 * method for details. 355 * 356 * @param startInstant 357 * The length of a month/year varies. The <code>startInstant</code> is 358 * used to disambiguate this variance. Specifically, this method 359 * returns the difference between <code>startInstant</code> and 360 * <code>startInstant+duration</code>. 361 * 362 * @throws NullPointerException 363 * If the startInstant parameter is null. 364 * 365 * @return milliseconds between <code>startInstant</code> and 366 * <code>startInstant</code> plus this <code>Duration</code> 367 * 368 * @see #getTimeInMillis(Calendar) 369 */ getTimeInMillis(final Date startInstant)370 public long getTimeInMillis(final Date startInstant) { 371 Calendar cal = new GregorianCalendar(); 372 cal.setTime(startInstant); 373 this.addTo(cal); 374 return getCalendarTimeInMillis(cal) - startInstant.getTime(); 375 } 376 377 /** 378 * Gets the value of a field. 379 * 380 * Fields of a duration object may contain arbitrary large value. 381 * Therefore this method is designed to return a {@link Number} object. 382 * 383 * In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned 384 * number will be a non-negative integer. In case of seconds, 385 * the returned number may be a non-negative decimal value. 386 * 387 * @param field 388 * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS, 389 * MINUTES, or SECONDS.) 390 * @return 391 * If the specified field is present, this method returns 392 * a non-null non-negative {@link Number} object that 393 * represents its value. If it is not present, return null. 394 * For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method 395 * returns a {@link java.math.BigInteger} object. For SECONDS, this 396 * method returns a {@link java.math.BigDecimal}. 397 * 398 * @throws NullPointerException If the <code>field</code> is <code>null</code>. 399 */ getField(final DatatypeConstants.Field field)400 public abstract Number getField(final DatatypeConstants.Field field); 401 402 /** 403 * Gets the value of a field as an <code>int</code>. 404 * 405 * @param field 406 * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS, 407 * MINUTES, or SECONDS.) 408 * @return 409 * If the field is present, return its value as an <code>int</code>, 410 * else return <code>0</code>. 411 */ getFieldValueAsInt(final DatatypeConstants.Field field)412 private int getFieldValueAsInt(final DatatypeConstants.Field field) { 413 Number n = getField(field); 414 if (n != null) { 415 return n.intValue(); 416 } 417 return 0; 418 } 419 420 /** 421 * Checks if a field is set. 422 * 423 * A field of a duration object may or may not be present. 424 * This method can be used to test if a field is present. 425 * 426 * @param field 427 * one of the six Field constants (YEARS,MONTHS,DAYS,HOURS, 428 * MINUTES, or SECONDS.) 429 * @return 430 * true if the field is present. false if not. 431 * 432 * @throws NullPointerException 433 * If the field parameter is null. 434 */ isSet(final DatatypeConstants.Field field)435 public abstract boolean isSet(final DatatypeConstants.Field field); 436 437 /** 438 * <p>Computes a new duration whose value is <code>this+rhs</code>.</p> 439 * 440 * <p>For example,</p> 441 * <pre> 442 * "1 day" + "-3 days" = "-2 days" 443 * "1 year" + "1 day" = "1 year and 1 day" 444 * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)" 445 * "15 hours" + "-3 days" = "-(2 days,9 hours)" 446 * "1 year" + "-1 day" = IllegalStateException 447 * </pre> 448 * 449 * <p>Since there's no way to meaningfully subtract 1 day from 1 month, 450 * there are cases where the operation fails in 451 * {@link IllegalStateException}.</p> 452 * 453 * <p> 454 * Formally, the computation is defined as follows.</p> 455 * <p> 456 * Firstly, we can assume that two <code>Duration</code>s to be added 457 * are both positive without losing generality (i.e., 458 * <code>(-X)+Y=Y-X</code>, <code>X+(-Y)=X-Y</code>, 459 * <code>(-X)+(-Y)=-(X+Y)</code>) 460 * 461 * <p> 462 * Addition of two positive <code>Duration</code>s are simply defined as 463 * field by field addition where missing fields are treated as 0. 464 * <p> 465 * A field of the resulting <code>Duration</code> will be unset if and 466 * only if respective fields of two input <code>Duration</code>s are unset. 467 * <p> 468 * Note that <code>lhs.add(rhs)</code> will be always successful if 469 * <code>lhs.signum()*rhs.signum()!=-1</code> or both of them are 470 * normalized.</p> 471 * 472 * @param rhs <code>Duration</code> to add to this <code>Duration</code> 473 * 474 * @return 475 * non-null valid Duration object. 476 * 477 * @throws NullPointerException 478 * If the rhs parameter is null. 479 * @throws IllegalStateException 480 * If two durations cannot be meaningfully added. For 481 * example, adding negative one day to one month causes 482 * this exception. 483 * 484 * 485 * @see #subtract(Duration) 486 */ add(final Duration rhs)487 public abstract Duration add(final Duration rhs); 488 489 /** 490 * Adds this duration to a {@link Calendar} object. 491 * 492 * <p> 493 * Calls {@link java.util.Calendar#add(int,int)} in the 494 * order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS 495 * if those fields are present. Because the {@link Calendar} class 496 * uses int to hold values, there are cases where this method 497 * won't work correctly (for example if values of fields 498 * exceed the range of int.) 499 * </p> 500 * 501 * <p> 502 * Also, since this duration class is a Gregorian duration, this 503 * method will not work correctly if the given {@link Calendar} 504 * object is based on some other calendar systems. 505 * </p> 506 * 507 * <p> 508 * Any fractional parts of this <code>Duration</code> object 509 * beyond milliseconds will be simply ignored. For example, if 510 * this duration is "P1.23456S", then 1 is added to SECONDS, 511 * 234 is added to MILLISECONDS, and the rest will be unused. 512 * </p> 513 * 514 * <p> 515 * Note that because {@link Calendar#add(int, int)} is using 516 * <tt>int</tt>, <code>Duration</code> with values beyond the 517 * range of <tt>int</tt> in its fields 518 * will cause overflow/underflow to the given {@link Calendar}. 519 * {@link XMLGregorianCalendar#add(Duration)} provides the same 520 * basic operation as this method while avoiding 521 * the overflow/underflow issues. 522 * 523 * @param calendar 524 * A calendar object whose value will be modified. 525 * @throws NullPointerException 526 * if the calendar parameter is null. 527 */ addTo(Calendar calendar)528 public abstract void addTo(Calendar calendar); 529 530 /** 531 * Adds this duration to a {@link Date} object. 532 * 533 * <p> 534 * The given date is first converted into 535 * a {@link java.util.GregorianCalendar}, then the duration 536 * is added exactly like the {@link #addTo(Calendar)} method. 537 * 538 * <p> 539 * The updated time instant is then converted back into a 540 * {@link Date} object and used to update the given {@link Date} object. 541 * 542 * <p> 543 * This somewhat redundant computation is necessary to unambiguously 544 * determine the duration of months and years. 545 * 546 * @param date 547 * A date object whose value will be modified. 548 * @throws NullPointerException 549 * if the date parameter is null. 550 */ addTo(Date date)551 public void addTo(Date date) { 552 553 // check data parameter 554 if (date == null) { 555 throw new NullPointerException("date == null"); 556 } 557 558 Calendar cal = new GregorianCalendar(); 559 cal.setTime(date); 560 this.addTo(cal); 561 date.setTime(getCalendarTimeInMillis(cal)); 562 } 563 564 /** 565 * <p>Computes a new duration whose value is <code>this-rhs</code>.</p> 566 * 567 * <p>For example:</p> 568 * <pre> 569 * "1 day" - "-3 days" = "4 days" 570 * "1 year" - "1 day" = IllegalStateException 571 * "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)" 572 * "15 hours" - "-3 days" = "3 days and 15 hours" 573 * "1 year" - "-1 day" = "1 year and 1 day" 574 * </pre> 575 * 576 * <p>Since there's no way to meaningfully subtract 1 day from 1 month, 577 * there are cases where the operation fails in {@link IllegalStateException}.</p> 578 * 579 * <p>Formally the computation is defined as follows. 580 * First, we can assume that two <code>Duration</code>s are both positive 581 * without losing generality. (i.e., 582 * <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>, 583 * <code>(-X)-(-Y)=-(X-Y)</code>)</p> 584 * 585 * <p>Then two durations are subtracted field by field. 586 * If the sign of any non-zero field <tt>F</tt> is different from 587 * the sign of the most significant field, 588 * 1 (if <tt>F</tt> is negative) or -1 (otherwise) 589 * will be borrowed from the next bigger unit of <tt>F</tt>.</p> 590 * 591 * <p>This process is repeated until all the non-zero fields have 592 * the same sign.</p> 593 * 594 * <p>If a borrow occurs in the days field (in other words, if 595 * the computation needs to borrow 1 or -1 month to compensate 596 * days), then the computation fails by throwing an 597 * {@link IllegalStateException}.</p> 598 * 599 * @param rhs <code>Duration</code> to subtract from this <code>Duration</code>. 600 * 601 * @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>. 602 * 603 * @throws IllegalStateException 604 * If two durations cannot be meaningfully subtracted. For 605 * example, subtracting one day from one month causes 606 * this exception. 607 * 608 * @throws NullPointerException 609 * If the rhs parameter is null. 610 * 611 * @see #add(Duration) 612 */ subtract(final Duration rhs)613 public Duration subtract(final Duration rhs) { 614 return add(rhs.negate()); 615 } 616 617 /** 618 * <p>Computes a new duration whose value is <code>factor</code> times 619 * longer than the value of this duration.</p> 620 * 621 * <p>This method is provided for the convenience. 622 * It is functionally equivalent to the following code:</p> 623 * <pre> 624 * multiply(new BigDecimal(String.valueOf(factor))) 625 * </pre> 626 * 627 * @param factor Factor times longer of new <code>Duration</code> to create. 628 * 629 * @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>. 630 * 631 * @see #multiply(BigDecimal) 632 */ multiply(int factor)633 public Duration multiply(int factor) { 634 return multiply(BigDecimal.valueOf(factor)); 635 } 636 637 /** 638 * Computes a new duration whose value is <code>factor</code> times 639 * longer than the value of this duration. 640 * 641 * <p> 642 * For example, 643 * <pre> 644 * "P1M" (1 month) * "12" = "P12M" (12 months) 645 * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds) 646 * "P1M" (1 month) * "1.5" = IllegalStateException 647 * </pre> 648 * 649 * <p> 650 * Since the <code>Duration</code> class is immutable, this method 651 * doesn't change the value of this object. It simply computes 652 * a new Duration object and returns it. 653 * 654 * <p> 655 * The operation will be performed field by field with the precision 656 * of {@link BigDecimal}. Since all the fields except seconds are 657 * restricted to hold integers, 658 * any fraction produced by the computation will be 659 * carried down toward the next lower unit. For example, 660 * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day, 661 * which will be carried down to "PT12H" (12 hours). 662 * When fractions of month cannot be meaningfully carried down 663 * to days, or year to months, this will cause an 664 * {@link IllegalStateException} to be thrown. 665 * For example if you multiple one month by 0.5.</p> 666 * 667 * <p> 668 * To avoid {@link IllegalStateException}, use 669 * the {@link #normalizeWith(Calendar)} method to remove the years 670 * and months fields. 671 * 672 * @param factor to multiply by 673 * 674 * @return 675 * returns a non-null valid <code>Duration</code> object 676 * 677 * @throws IllegalStateException if operation produces fraction in 678 * the months field. 679 * 680 * @throws NullPointerException if the <code>factor</code> parameter is 681 * <code>null</code>. 682 * 683 */ multiply(final BigDecimal factor)684 public abstract Duration multiply(final BigDecimal factor); 685 686 /** 687 * Returns a new <code>Duration</code> object whose 688 * value is <code>-this</code>. 689 * 690 * <p> 691 * Since the <code>Duration</code> class is immutable, this method 692 * doesn't change the value of this object. It simply computes 693 * a new Duration object and returns it. 694 * 695 * @return 696 * always return a non-null valid <code>Duration</code> object. 697 */ negate()698 public abstract Duration negate(); 699 700 /** 701 * <p>Converts the years and months fields into the days field 702 * by using a specific time instant as the reference point.</p> 703 * 704 * <p>For example, duration of one month normalizes to 31 days 705 * given the start time instance "July 8th 2003, 17:40:32".</p> 706 * 707 * <p>Formally, the computation is done as follows:</p> 708 * <ol> 709 * <li>the given Calendar object is cloned</li> 710 * <li>the years, months and days fields will be added to the {@link Calendar} object 711 * by using the {@link Calendar#add(int,int)} method</li> 712 * <li>the difference between the two Calendars in computed in milliseconds and converted to days, 713 * if a remainder occurs due to Daylight Savings Time, it is discarded</li> 714 * <li>the computed days, along with the hours, minutes and seconds 715 * fields of this duration object is used to construct a new 716 * Duration object.</li> 717 * </ol> 718 * 719 * <p>Note that since the Calendar class uses <code>int</code> to 720 * hold the value of year and month, this method may produce 721 * an unexpected result if this duration object holds 722 * a very large value in the years or months fields.</p> 723 * 724 * @param startTimeInstant <code>Calendar</code> reference point. 725 * 726 * @return <code>Duration</code> of years and months of this <code>Duration</code> as days. 727 * 728 * @throws NullPointerException If the startTimeInstant parameter is null. 729 */ normalizeWith(final Calendar startTimeInstant)730 public abstract Duration normalizeWith(final Calendar startTimeInstant); 731 732 /** 733 * <p>Partial order relation comparison with this <code>Duration</code> instance.</p> 734 * 735 * <p>Comparison result must be in accordance with 736 * <a href="http://www.w3.org/TR/xmlschema-2/#duration-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2, 737 * <i>Order relation on duration</i></a>.</p> 738 * 739 * <p>Return:</p> 740 * <ul> 741 * <li>{@link DatatypeConstants#LESSER} if this <code>Duration</code> is shorter than <code>duration</code> parameter</li> 742 * <li>{@link DatatypeConstants#EQUAL} if this <code>Duration</code> is equal to <code>duration</code> parameter</li> 743 * <li>{@link DatatypeConstants#GREATER} if this <code>Duration</code> is longer than <code>duration</code> parameter</li> 744 * <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li> 745 * </ul> 746 * 747 * @param duration to compare 748 * 749 * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as 750 * {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER} 751 * or {@link DatatypeConstants#INDETERMINATE}. 752 * 753 * @throws UnsupportedOperationException If the underlying implementation 754 * cannot reasonably process the request, e.g. W3C XML Schema allows for 755 * arbitrarily large/small/precise values, the request may be beyond the 756 * implementations capability. 757 * @throws NullPointerException if <code>duration</code> is <code>null</code>. 758 * 759 * @see #isShorterThan(Duration) 760 * @see #isLongerThan(Duration) 761 */ compare(final Duration duration)762 public abstract int compare(final Duration duration); 763 764 /** 765 * <p>Checks if this duration object is strictly longer than 766 * another <code>Duration</code> object.</p> 767 * 768 * <p>Duration X is "longer" than Y if and only if X>Y 769 * as defined in the section 3.2.6.2 of the XML Schema 1.0 770 * specification.</p> 771 * 772 * <p>For example, "P1D" (one day) > "PT12H" (12 hours) and 773 * "P2Y" (two years) > "P23M" (23 months).</p> 774 * 775 * @param duration <code>Duration</code> to test this <code>Duration</code> against. 776 * 777 * @throws UnsupportedOperationException If the underlying implementation 778 * cannot reasonably process the request, e.g. W3C XML Schema allows for 779 * arbitrarily large/small/precise values, the request may be beyond the 780 * implementations capability. 781 * @throws NullPointerException If <code>duration</code> is null. 782 * 783 * @return 784 * true if the duration represented by this object 785 * is longer than the given duration. false otherwise. 786 * 787 * @see #isShorterThan(Duration) 788 * @see #compare(Duration duration) 789 */ isLongerThan(final Duration duration)790 public boolean isLongerThan(final Duration duration) { 791 return compare(duration) == DatatypeConstants.GREATER; 792 } 793 794 /** 795 * <p>Checks if this duration object is strictly shorter than 796 * another <code>Duration</code> object.</p> 797 * 798 * @param duration <code>Duration</code> to test this <code>Duration</code> against. 799 * 800 * @return <code>true</code> if <code>duration</code> parameter is shorter than this <code>Duration</code>, 801 * else <code>false</code>. 802 * 803 * @throws UnsupportedOperationException If the underlying implementation 804 * cannot reasonably process the request, e.g. W3C XML Schema allows for 805 * arbitrarily large/small/precise values, the request may be beyond the 806 * implementations capability. 807 * @throws NullPointerException if <code>duration</code> is null. 808 * 809 * @see #isLongerThan(Duration duration) 810 * @see #compare(Duration duration) 811 */ isShorterThan(final Duration duration)812 public boolean isShorterThan(final Duration duration) { 813 return compare(duration) == DatatypeConstants.LESSER; 814 } 815 816 /** 817 * <p>Checks if this duration object has the same duration 818 * as another <code>Duration</code> object.</p> 819 * 820 * <p>For example, "P1D" (1 day) is equal to "PT24H" (24 hours).</p> 821 * 822 * <p>Duration X is equal to Y if and only if time instant 823 * t+X and t+Y are the same for all the test time instants 824 * specified in the section 3.2.6.2 of the XML Schema 1.0 825 * specification.</p> 826 * 827 * <p>Note that there are cases where two <code>Duration</code>s are 828 * "incomparable" to each other, like one month and 30 days. 829 * For example,</p> 830 * <pre> 831 * !new Duration("P1M").isShorterThan(new Duration("P30D")) 832 * !new Duration("P1M").isLongerThan(new Duration("P30D")) 833 * !new Duration("P1M").equals(new Duration("P30D")) 834 * </pre> 835 * 836 * @param duration 837 * A non-null valid <code>Duration</code> object. 838 * 839 * @return 840 * <code>true</code> if this duration is the same length as 841 * <code>duration</code>. 842 * <code>false</code> if <code>duration</code> is not a 843 * <code>Duration</code> object, is <code>null</code>, 844 * or its length is different from this duration. 845 * 846 * @throws UnsupportedOperationException If the underlying implementation 847 * cannot reasonably process the request, e.g. W3C XML Schema allows for 848 * arbitrarily large/small/precise values, the request may be beyond the 849 * implementations capability. 850 * 851 * @see #compare(Duration duration) 852 */ equals(final Object duration)853 public boolean equals(final Object duration) { 854 if (duration == this) { 855 return true; 856 } 857 if (duration instanceof Duration) { 858 return compare((Duration) duration) == DatatypeConstants.EQUAL; 859 } 860 return false; 861 } 862 863 /** 864 * Returns a hash code consistent with the definition of the equals method. 865 * 866 * @see Object#hashCode() 867 */ hashCode()868 public abstract int hashCode(); 869 870 /** 871 * <p>Returns a <code>String</code> representation of this <code>Duration</code> <code>Object</code>.</p> 872 * 873 * <p>The result is formatted according to the XML Schema 1.0 specification and can be always parsed back later into the 874 * equivalent <code>Duration</code> <code>Object</code> by {@link DatatypeFactory#newDuration(String lexicalRepresentation)}.</p> 875 * 876 * <p>Formally, the following holds for any <code>Duration</code> 877 * <code>Object</code> x:</p> 878 * <pre> 879 * new Duration(x.toString()).equals(x) 880 * </pre> 881 * 882 * @return A non-<code>null</code> valid <code>String</code> representation of this <code>Duration</code>. 883 */ toString()884 public String toString() { 885 StringBuilder buf = new StringBuilder(); 886 887 if (getSign() < 0) { 888 buf.append('-'); 889 } 890 buf.append('P'); 891 892 BigInteger years = (BigInteger) getField(DatatypeConstants.YEARS); 893 if (years != null) { 894 buf.append(years).append('Y'); 895 } 896 897 BigInteger months = (BigInteger) getField(DatatypeConstants.MONTHS); 898 if (months != null) { 899 buf.append(months).append('M'); 900 } 901 902 BigInteger days = (BigInteger) getField(DatatypeConstants.DAYS); 903 if (days != null) { 904 buf.append(days).append('D'); 905 } 906 907 BigInteger hours = (BigInteger) getField(DatatypeConstants.HOURS); 908 BigInteger minutes = (BigInteger) getField(DatatypeConstants.MINUTES); 909 BigDecimal seconds = (BigDecimal) getField(DatatypeConstants.SECONDS); 910 if (hours != null || minutes != null || seconds != null) { 911 buf.append('T'); 912 if (hours != null) { 913 buf.append(hours).append('H'); 914 } 915 if (minutes != null) { 916 buf.append(minutes).append('M'); 917 } 918 if (seconds != null) { 919 buf.append(toString(seconds)).append('S'); 920 } 921 } 922 923 return buf.toString(); 924 } 925 926 /** 927 * <p>Turns {@link BigDecimal} to a string representation.</p> 928 * 929 * <p>Due to a behavior change in the {@link BigDecimal#toString()} 930 * method in JDK1.5, this had to be implemented here.</p> 931 * 932 * @param bd <code>BigDecimal</code> to format as a <code>String</code> 933 * 934 * @return <code>String</code> representation of <code>BigDecimal</code> 935 */ toString(BigDecimal bd)936 private String toString(BigDecimal bd) { 937 String intString = bd.unscaledValue().toString(); 938 int scale = bd.scale(); 939 940 if (scale == 0) { 941 return intString; 942 } 943 944 /* Insert decimal point */ 945 StringBuilder buf; 946 int insertionPoint = intString.length() - scale; 947 if (insertionPoint == 0) { /* Point goes right before intVal */ 948 return "0." + intString; 949 } 950 else if (insertionPoint > 0) { /* Point goes inside intVal */ 951 buf = new StringBuilder(intString); 952 buf.insert(insertionPoint, '.'); 953 } 954 else { /* We must insert zeros between point and intVal */ 955 buf = new StringBuilder(3 - insertionPoint + intString.length()); 956 buf.append("0."); 957 for (int i = 0; i < -insertionPoint; i++) { 958 buf.append('0'); 959 } 960 buf.append(intString); 961 } 962 return buf.toString(); 963 } 964 965 966 /** 967 * <p>Calls the {@link Calendar#getTimeInMillis} method. 968 * Prior to JDK1.4, this method was protected and therefore 969 * cannot be invoked directly.</p> 970 * 971 * <p>TODO: In future, this should be replaced by <code>cal.getTimeInMillis()</code>.</p> 972 * 973 * @param cal <code>Calendar</code> to get time in milliseconds. 974 * 975 * @return Milliseconds of <code>cal</code>. 976 */ getCalendarTimeInMillis(final Calendar cal)977 private static long getCalendarTimeInMillis(final Calendar cal) { 978 return cal.getTime().getTime(); 979 } 980 } 981