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 (at) Sun.COM">Joseph Fialli</a> 87 * @author <a href="mailto:Kohsuke.Kawaguchi (at) Sun.com">Kohsuke Kawaguchi</a> 88 * @author <a href="mailto:Jeff.Suttor (at) 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 551 public void addTo(Date date) { 552 553 // check data parameter 554 if (date == null) { 555 throw new NullPointerException( 556 "Cannot call " 557 + this.getClass().getName() 558 + "#addTo(Date date) with date == null." 559 ); 560 } 561 562 Calendar cal = new GregorianCalendar(); 563 cal.setTime(date); 564 this.addTo(cal); 565 date.setTime(getCalendarTimeInMillis(cal)); 566 } 567 568 /** 569 * <p>Computes a new duration whose value is <code>this-rhs</code>.</p> 570 * 571 * <p>For example:</p> 572 * <pre> 573 * "1 day" - "-3 days" = "4 days" 574 * "1 year" - "1 day" = IllegalStateException 575 * "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)" 576 * "15 hours" - "-3 days" = "3 days and 15 hours" 577 * "1 year" - "-1 day" = "1 year and 1 day" 578 * </pre> 579 * 580 * <p>Since there's no way to meaningfully subtract 1 day from 1 month, 581 * there are cases where the operation fails in {@link IllegalStateException}.</p> 582 * 583 * <p>Formally the computation is defined as follows. 584 * First, we can assume that two <code>Duration</code>s are both positive 585 * without losing generality. (i.e., 586 * <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>, 587 * <code>(-X)-(-Y)=-(X-Y)</code>)</p> 588 * 589 * <p>Then two durations are subtracted field by field. 590 * If the sign of any non-zero field <tt>F</tt> is different from 591 * the sign of the most significant field, 592 * 1 (if <tt>F</tt> is negative) or -1 (otherwise) 593 * will be borrowed from the next bigger unit of <tt>F</tt>.</p> 594 * 595 * <p>This process is repeated until all the non-zero fields have 596 * the same sign.</p> 597 * 598 * <p>If a borrow occurs in the days field (in other words, if 599 * the computation needs to borrow 1 or -1 month to compensate 600 * days), then the computation fails by throwing an 601 * {@link IllegalStateException}.</p> 602 * 603 * @param rhs <code>Duration</code> to subtract from this <code>Duration</code>. 604 * 605 * @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>. 606 * 607 * @throws IllegalStateException 608 * If two durations cannot be meaningfully subtracted. For 609 * example, subtracting one day from one month causes 610 * this exception. 611 * 612 * @throws NullPointerException 613 * If the rhs parameter is null. 614 * 615 * @see #add(Duration) 616 */ 617 public Duration subtract(final Duration rhs) { 618 return add(rhs.negate()); 619 } 620 621 /** 622 * <p>Computes a new duration whose value is <code>factor</code> times 623 * longer than the value of this duration.</p> 624 * 625 * <p>This method is provided for the convenience. 626 * It is functionally equivalent to the following code:</p> 627 * <pre> 628 * multiply(new BigDecimal(String.valueOf(factor))) 629 * </pre> 630 * 631 * @param factor Factor times longer of new <code>Duration</code> to create. 632 * 633 * @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>. 634 * 635 * @see #multiply(BigDecimal) 636 */ 637 public Duration multiply(int factor) { 638 return multiply(BigDecimal.valueOf(factor)); 639 } 640 641 /** 642 * Computes a new duration whose value is <code>factor</code> times 643 * longer than the value of this duration. 644 * 645 * <p> 646 * For example, 647 * <pre> 648 * "P1M" (1 month) * "12" = "P12M" (12 months) 649 * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds) 650 * "P1M" (1 month) * "1.5" = IllegalStateException 651 * </pre> 652 * 653 * <p> 654 * Since the <code>Duration</code> class is immutable, this method 655 * doesn't change the value of this object. It simply computes 656 * a new Duration object and returns it. 657 * 658 * <p> 659 * The operation will be performed field by field with the precision 660 * of {@link BigDecimal}. Since all the fields except seconds are 661 * restricted to hold integers, 662 * any fraction produced by the computation will be 663 * carried down toward the next lower unit. For example, 664 * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day, 665 * which will be carried down to "PT12H" (12 hours). 666 * When fractions of month cannot be meaningfully carried down 667 * to days, or year to months, this will cause an 668 * {@link IllegalStateException} to be thrown. 669 * For example if you multiple one month by 0.5.</p> 670 * 671 * <p> 672 * To avoid {@link IllegalStateException}, use 673 * the {@link #normalizeWith(Calendar)} method to remove the years 674 * and months fields. 675 * 676 * @param factor to multiply by 677 * 678 * @return 679 * returns a non-null valid <code>Duration</code> object 680 * 681 * @throws IllegalStateException if operation produces fraction in 682 * the months field. 683 * 684 * @throws NullPointerException if the <code>factor</code> parameter is 685 * <code>null</code>. 686 * 687 */ 688 public abstract Duration multiply(final BigDecimal factor); 689 690 /** 691 * Returns a new <code>Duration</code> object whose 692 * value is <code>-this</code>. 693 * 694 * <p> 695 * Since the <code>Duration</code> class is immutable, this method 696 * doesn't change the value of this object. It simply computes 697 * a new Duration object and returns it. 698 * 699 * @return 700 * always return a non-null valid <code>Duration</code> object. 701 */ 702 public abstract Duration negate(); 703 704 /** 705 * <p>Converts the years and months fields into the days field 706 * by using a specific time instant as the reference point.</p> 707 * 708 * <p>For example, duration of one month normalizes to 31 days 709 * given the start time instance "July 8th 2003, 17:40:32".</p> 710 * 711 * <p>Formally, the computation is done as follows:</p> 712 * <ol> 713 * <li>the given Calendar object is cloned</li> 714 * <li>the years, months and days fields will be added to the {@link Calendar} object 715 * by using the {@link Calendar#add(int,int)} method</li> 716 * <li>the difference between the two Calendars in computed in milliseconds and converted to days, 717 * if a remainder occurs due to Daylight Savings Time, it is discarded</li> 718 * <li>the computed days, along with the hours, minutes and seconds 719 * fields of this duration object is used to construct a new 720 * Duration object.</li> 721 * </ol> 722 * 723 * <p>Note that since the Calendar class uses <code>int</code> to 724 * hold the value of year and month, this method may produce 725 * an unexpected result if this duration object holds 726 * a very large value in the years or months fields.</p> 727 * 728 * @param startTimeInstant <code>Calendar</code> reference point. 729 * 730 * @return <code>Duration</code> of years and months of this <code>Duration</code> as days. 731 * 732 * @throws NullPointerException If the startTimeInstant parameter is null. 733 */ 734 public abstract Duration normalizeWith(final Calendar startTimeInstant); 735 736 /** 737 * <p>Partial order relation comparison with this <code>Duration</code> instance.</p> 738 * 739 * <p>Comparison result must be in accordance with 740 * <a href="http://www.w3.org/TR/xmlschema-2/#duration-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2, 741 * <i>Order relation on duration</i></a>.</p> 742 * 743 * <p>Return:</p> 744 * <ul> 745 * <li>{@link DatatypeConstants#LESSER} if this <code>Duration</code> is shorter than <code>duration</code> parameter</li> 746 * <li>{@link DatatypeConstants#EQUAL} if this <code>Duration</code> is equal to <code>duration</code> parameter</li> 747 * <li>{@link DatatypeConstants#GREATER} if this <code>Duration</code> is longer than <code>duration</code> parameter</li> 748 * <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li> 749 * </ul> 750 * 751 * @param duration to compare 752 * 753 * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as 754 * {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER} 755 * or {@link DatatypeConstants#INDETERMINATE}. 756 * 757 * @throws UnsupportedOperationException If the underlying implementation 758 * cannot reasonably process the request, e.g. W3C XML Schema allows for 759 * arbitrarily large/small/precise values, the request may be beyond the 760 * implementations capability. 761 * @throws NullPointerException if <code>duration</code> is <code>null</code>. 762 * 763 * @see #isShorterThan(Duration) 764 * @see #isLongerThan(Duration) 765 */ 766 public abstract int compare(final Duration duration); 767 768 /** 769 * <p>Checks if this duration object is strictly longer than 770 * another <code>Duration</code> object.</p> 771 * 772 * <p>Duration X is "longer" than Y if and only if X>Y 773 * as defined in the section 3.2.6.2 of the XML Schema 1.0 774 * specification.</p> 775 * 776 * <p>For example, "P1D" (one day) > "PT12H" (12 hours) and 777 * "P2Y" (two years) > "P23M" (23 months).</p> 778 * 779 * @param duration <code>Duration</code> to test this <code>Duration</code> against. 780 * 781 * @throws UnsupportedOperationException If the underlying implementation 782 * cannot reasonably process the request, e.g. W3C XML Schema allows for 783 * arbitrarily large/small/precise values, the request may be beyond the 784 * implementations capability. 785 * @throws NullPointerException If <code>duration</code> is null. 786 * 787 * @return 788 * true if the duration represented by this object 789 * is longer than the given duration. false otherwise. 790 * 791 * @see #isShorterThan(Duration) 792 * @see #compare(Duration duration) 793 */ 794 public boolean isLongerThan(final Duration duration) { 795 return compare(duration) == DatatypeConstants.GREATER; 796 } 797 798 /** 799 * <p>Checks if this duration object is strictly shorter than 800 * another <code>Duration</code> object.</p> 801 * 802 * @param duration <code>Duration</code> to test this <code>Duration</code> against. 803 * 804 * @return <code>true</code> if <code>duration</code> parameter is shorter than this <code>Duration</code>, 805 * else <code>false</code>. 806 * 807 * @throws UnsupportedOperationException If the underlying implementation 808 * cannot reasonably process the request, e.g. W3C XML Schema allows for 809 * arbitrarily large/small/precise values, the request may be beyond the 810 * implementations capability. 811 * @throws NullPointerException if <code>duration</code> is null. 812 * 813 * @see #isLongerThan(Duration duration) 814 * @see #compare(Duration duration) 815 */ 816 public boolean isShorterThan(final Duration duration) { 817 return compare(duration) == DatatypeConstants.LESSER; 818 } 819 820 /** 821 * <p>Checks if this duration object has the same duration 822 * as another <code>Duration</code> object.</p> 823 * 824 * <p>For example, "P1D" (1 day) is equal to "PT24H" (24 hours).</p> 825 * 826 * <p>Duration X is equal to Y if and only if time instant 827 * t+X and t+Y are the same for all the test time instants 828 * specified in the section 3.2.6.2 of the XML Schema 1.0 829 * specification.</p> 830 * 831 * <p>Note that there are cases where two <code>Duration</code>s are 832 * "incomparable" to each other, like one month and 30 days. 833 * For example,</p> 834 * <pre> 835 * !new Duration("P1M").isShorterThan(new Duration("P30D")) 836 * !new Duration("P1M").isLongerThan(new Duration("P30D")) 837 * !new Duration("P1M").equals(new Duration("P30D")) 838 * </pre> 839 * 840 * @param duration 841 * A non-null valid <code>Duration</code> object. 842 * 843 * @return 844 * <code>true</code> if this duration is the same length as 845 * <code>duration</code>. 846 * <code>false</code> if <code>duration</code> is not a 847 * <code>Duration</code> object, is <code>null</code>, 848 * or its length is different from this duration. 849 * 850 * @throws UnsupportedOperationException If the underlying implementation 851 * cannot reasonably process the request, e.g. W3C XML Schema allows for 852 * arbitrarily large/small/precise values, the request may be beyond the 853 * implementations capability. 854 * 855 * @see #compare(Duration duration) 856 */ 857 public boolean equals(final Object duration) { 858 if (duration == this) { 859 return true; 860 } 861 if (duration instanceof Duration) { 862 return compare((Duration) duration) == DatatypeConstants.EQUAL; 863 } 864 return false; 865 } 866 867 /** 868 * Returns a hash code consistent with the definition of the equals method. 869 * 870 * @see Object#hashCode() 871 */ 872 public abstract int hashCode(); 873 874 /** 875 * <p>Returns a <code>String</code> representation of this <code>Duration</code> <code>Object</code>.</p> 876 * 877 * <p>The result is formatted according to the XML Schema 1.0 specification and can be always parsed back later into the 878 * equivalent <code>Duration</code> <code>Object</code> by {@link DatatypeFactory#newDuration(String lexicalRepresentation)}.</p> 879 * 880 * <p>Formally, the following holds for any <code>Duration</code> 881 * <code>Object</code> x:</p> 882 * <pre> 883 * new Duration(x.toString()).equals(x) 884 * </pre> 885 * 886 * @return A non-<code>null</code> valid <code>String</code> representation of this <code>Duration</code>. 887 */ 888 public String toString() { 889 StringBuilder buf = new StringBuilder(); 890 891 if (getSign() < 0) { 892 buf.append('-'); 893 } 894 buf.append('P'); 895 896 BigInteger years = (BigInteger) getField(DatatypeConstants.YEARS); 897 if (years != null) { 898 buf.append(years).append('Y'); 899 } 900 901 BigInteger months = (BigInteger) getField(DatatypeConstants.MONTHS); 902 if (months != null) { 903 buf.append(months).append('M'); 904 } 905 906 BigInteger days = (BigInteger) getField(DatatypeConstants.DAYS); 907 if (days != null) { 908 buf.append(days).append('D'); 909 } 910 911 BigInteger hours = (BigInteger) getField(DatatypeConstants.HOURS); 912 BigInteger minutes = (BigInteger) getField(DatatypeConstants.MINUTES); 913 BigDecimal seconds = (BigDecimal) getField(DatatypeConstants.SECONDS); 914 if (hours != null || minutes != null || seconds != null) { 915 buf.append('T'); 916 if (hours != null) { 917 buf.append(hours).append('H'); 918 } 919 if (minutes != null) { 920 buf.append(minutes).append('M'); 921 } 922 if (seconds != null) { 923 buf.append(toString(seconds)).append('S'); 924 } 925 } 926 927 return buf.toString(); 928 } 929 930 /** 931 * <p>Turns {@link BigDecimal} to a string representation.</p> 932 * 933 * <p>Due to a behavior change in the {@link BigDecimal#toString()} 934 * method in JDK1.5, this had to be implemented here.</p> 935 * 936 * @param bd <code>BigDecimal</code> to format as a <code>String</code> 937 * 938 * @return <code>String</code> representation of <code>BigDecimal</code> 939 */ 940 private String toString(BigDecimal bd) { 941 String intString = bd.unscaledValue().toString(); 942 int scale = bd.scale(); 943 944 if (scale == 0) { 945 return intString; 946 } 947 948 /* Insert decimal point */ 949 StringBuilder buf; 950 int insertionPoint = intString.length() - scale; 951 if (insertionPoint == 0) { /* Point goes right before intVal */ 952 return "0." + intString; 953 } 954 else if (insertionPoint > 0) { /* Point goes inside intVal */ 955 buf = new StringBuilder(intString); 956 buf.insert(insertionPoint, '.'); 957 } 958 else { /* We must insert zeros between point and intVal */ 959 buf = new StringBuilder(3 - insertionPoint + intString.length()); 960 buf.append("0."); 961 for (int i = 0; i < -insertionPoint; i++) { 962 buf.append('0'); 963 } 964 buf.append(intString); 965 } 966 return buf.toString(); 967 } 968 969 970 /** 971 * <p>Calls the {@link Calendar#getTimeInMillis} method. 972 * Prior to JDK1.4, this method was protected and therefore 973 * cannot be invoked directly.</p> 974 * 975 * <p>TODO: In future, this should be replaced by <code>cal.getTimeInMillis()</code>.</p> 976 * 977 * @param cal <code>Calendar</code> to get time in milliseconds. 978 * 979 * @return Milliseconds of <code>cal</code>. 980 */ 981 private static long getCalendarTimeInMillis(final Calendar cal) { 982 return cal.getTime().getTime(); 983 } 984 } 985