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