📄 durationimpl.java
字号:
toBigInteger(buf[2], null == days), toBigInteger(buf[3], null == hours), toBigInteger(buf[4], null == minutes), (buf[5].signum() == 0 && seconds == null) ? null : buf[5]); } /** * <p>Gets the value of the field as a {@link BigDecimal}.</p> * * <p>If the field is unset, return 0.</p> * * @param f Field to get value for. * * @return non-null valid {@link BigDecimal}. */ private BigDecimal getFieldAsBigDecimal(DatatypeConstants.Field f) { if (f == DatatypeConstants.SECONDS) { if (seconds != null) { return seconds; } else { return ZERO; } } else { BigInteger bi = (BigInteger) getField(f); if (bi == null) { return ZERO; } else { return new BigDecimal(bi); } } } /** * <p>BigInteger value of BigDecimal value.</p> * * @param value Value to convert. * @param canBeNull Can returned value be null? * * @return BigInteger value of BigDecimal, possibly null. */ private static BigInteger toBigInteger( BigDecimal value, boolean canBeNull) { if (canBeNull && value.signum() == 0) { return null; } else { return value.unscaledValue(); } } /** * 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of * FIELDS[i+1]. */ private static final BigDecimal[] FACTORS = new BigDecimal[]{ BigDecimal.valueOf(12), null/*undefined*/, BigDecimal.valueOf(24), BigDecimal.valueOf(60), BigDecimal.valueOf(60) }; /** * <p>Computes a new duration whose value is <code>this+rhs</code>.</p> * * <p>For example,</p> * <pre> * "1 day" + "-3 days" = "-2 days" * "1 year" + "1 day" = "1 year and 1 day" * "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)" * "15 hours" + "-3 days" = "-(2 days,9 hours)" * "1 year" + "-1 day" = IllegalStateException * </pre> * * <p>Since there's no way to meaningfully subtract 1 day from 1 month, * there are cases where the operation fails in * {@link IllegalStateException}.</p> * * <p> * Formally, the computation is defined as follows.</p> * <p> * Firstly, we can assume that two {@link Duration}s to be added * are both positive without losing generality (i.e., * <code>(-X)+Y=Y-X</code>, <code>X+(-Y)=X-Y</code>, * <code>(-X)+(-Y)=-(X+Y)</code>) * * <p> * Addition of two positive {@link Duration}s are simply defined as * field by field addition where missing fields are treated as 0. * <p> * A field of the resulting {@link Duration} will be unset if and * only if respective fields of two input {@link Duration}s are unset. * <p> * Note that <code>lhs.add(rhs)</code> will be always successful if * <code>lhs.signum()*rhs.signum()!=-1</code> or both of them are * normalized.</p> * * @param rhs <code>Duration</code> to add to this <code>Duration</code> * * @return * non-null valid Duration object. * * @throws NullPointerException * If the rhs parameter is null. * @throws IllegalStateException * If two durations cannot be meaningfully added. For * example, adding negative one day to one month causes * this exception. * * * @see #subtract(Duration) */ public Duration add(final Duration rhs) { Duration lhs = this; BigDecimal[] buf = new BigDecimal[6]; buf[0] = sanitize((BigInteger) lhs.getField(DatatypeConstants.YEARS), lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.YEARS), rhs.getSign())); buf[1] = sanitize((BigInteger) lhs.getField(DatatypeConstants.MONTHS), lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.MONTHS), rhs.getSign())); buf[2] = sanitize((BigInteger) lhs.getField(DatatypeConstants.DAYS), lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.DAYS), rhs.getSign())); buf[3] = sanitize((BigInteger) lhs.getField(DatatypeConstants.HOURS), lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.HOURS), rhs.getSign())); buf[4] = sanitize((BigInteger) lhs.getField(DatatypeConstants.MINUTES), lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.MINUTES), rhs.getSign())); buf[5] = sanitize((BigDecimal) lhs.getField(DatatypeConstants.SECONDS), lhs.getSign()).add(sanitize((BigDecimal) rhs.getField(DatatypeConstants.SECONDS), rhs.getSign())); // align sign alignSigns(buf, 0, 2); // Y,M alignSigns(buf, 2, 6); // D,h,m,s // make sure that the sign bit is consistent across all 6 fields. int s = 0; for (int i = 0; i < 6; i++) { if (s * buf[i].signum() < 0) { throw new IllegalStateException(); } if (s == 0) { s = buf[i].signum(); } } return new DurationImpl( s >= 0, toBigInteger(sanitize(buf[0], s), lhs.getField(DatatypeConstants.YEARS) == null && rhs.getField(DatatypeConstants.YEARS) == null), toBigInteger(sanitize(buf[1], s), lhs.getField(DatatypeConstants.MONTHS) == null && rhs.getField(DatatypeConstants.MONTHS) == null), toBigInteger(sanitize(buf[2], s), lhs.getField(DatatypeConstants.DAYS) == null && rhs.getField(DatatypeConstants.DAYS) == null), toBigInteger(sanitize(buf[3], s), lhs.getField(DatatypeConstants.HOURS) == null && rhs.getField(DatatypeConstants.HOURS) == null), toBigInteger(sanitize(buf[4], s), lhs.getField(DatatypeConstants.MINUTES) == null && rhs.getField(DatatypeConstants.MINUTES) == null), (buf[5].signum() == 0 && lhs.getField(DatatypeConstants.SECONDS) == null && rhs.getField(DatatypeConstants.SECONDS) == null) ? null : sanitize(buf[5], s)); } private static void alignSigns(BigDecimal[] buf, int start, int end) { // align sign boolean touched; do { // repeat until all the sign bits become consistent touched = false; int s = 0; // sign of the left fields for (int i = start; i < end; i++) { if (s * buf[i].signum() < 0) { // this field has different sign than its left field. touched = true; // compute the number of unit that needs to be borrowed. BigDecimal borrow = buf[i].abs().divide( FACTORS[i - 1], BigDecimal.ROUND_UP); if (buf[i].signum() > 0) { borrow = borrow.negate(); } // update values buf[i - 1] = buf[i - 1].subtract(borrow); buf[i] = buf[i].add(borrow.multiply(FACTORS[i - 1])); } if (buf[i].signum() != 0) { s = buf[i].signum(); } } } while (touched); } /** * Compute <code>value*signum</code> where value==null is treated as * value==0. * @param value Value to sanitize. * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>. * * @return non-null {@link BigDecimal}. */ private static BigDecimal sanitize(BigInteger value, int signum) { if (signum == 0 || value == null) { return ZERO; } if (signum > 0) { return new BigDecimal(value); } return new BigDecimal(value.negate()); } /** * <p>Compute <code>value*signum</code> where <code>value==null</code> is treated as <code>value==0</code></p>. * * @param value Value to sanitize. * @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>. * * @return non-null {@link BigDecimal}. */ static BigDecimal sanitize(BigDecimal value, int signum) { if (signum == 0 || value == null) { return ZERO; } if (signum > 0) { return value; } return value.negate(); } /** * <p>Computes a new duration whose value is <code>this-rhs</code>.</p> * * <p>For example:</p> * <pre> * "1 day" - "-3 days" = "4 days" * "1 year" - "1 day" = IllegalStateException * "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)" * "15 hours" - "-3 days" = "3 days and 15 hours" * "1 year" - "-1 day" = "1 year and 1 day" * </pre> * * <p>Since there's no way to meaningfully subtract 1 day from 1 month, * there are cases where the operation fails in {@link IllegalStateException}.</p> * * <p>Formally the computation is defined as follows. * First, we can assume that two {@link Duration}s are both positive * without losing generality. (i.e., * <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>, * <code>(-X)-(-Y)=-(X-Y)</code>)</p> * * <p>Then two durations are subtracted field by field. * If the sign of any non-zero field <tt>F</tt> is different from * the sign of the most significant field, * 1 (if <tt>F</tt> is negative) or -1 (otherwise) * will be borrowed from the next bigger unit of <tt>F</tt>.</p> * * <p>This process is repeated until all the non-zero fields have * the same sign.</p> * * <p>If a borrow occurs in the days field (in other words, if * the computation needs to borrow 1 or -1 month to compensate * days), then the computation fails by throwing an * {@link IllegalStateException}.</p> * * @param rhs <code>Duration</code> to substract from this <code>Duration</code>. * * @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>. * * @throws IllegalStateException * If two durations cannot be meaningfully subtracted. For * example, subtracting one day from one month causes * this exception. * * @throws NullPointerException * If the rhs parameter is null. * * @see #add(Duration) */ public Duration subtract(final Duration rhs) { return add(rhs.negate()); } /** * Returns a new {@link Duration} object whose * value is <code>-this</code>. * * <p> * Since the {@link Duration} class is immutable, this method * doesn't change the value of this object. It simply computes * a new Duration object and returns it. * * @return * always return a non-null valid {@link Duration} object. */ public Duration negate() { return new DurationImpl( signum <= 0, years, months, days, hours, minutes, seconds); } /** * Returns the sign of this duration in -1,0, or 1. * * @return * -1 if this duration is negative, 0 if the duration is zero, * and 1 if the duration is postive. */ public int signum() { return signum; } /** * Adds this duration to a {@link Calendar} object. * * <p> * Calls {@link java.util.Calendar#add(int,int)} in the * order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS * if those fields are present. Because the {@link Calendar} class * uses int to hold values, there are cases where this method * won't work correctly (for example if values of fields * exceed the range of int.) * </p> * * <p> * Also, since this duration class is a Gregorian duration, this * method will not work correctly if the given {@link Calendar} * object is based on some other calendar systems. * </p> * * <p> * Any fractional parts of this {@link Duration} object * beyond milliseconds will be simply ignored. For example, if * this duration is "P1.23456S", then 1 is added to SECONDS, * 234 is added to MILLISECONDS, and the rest will be unused. * </p> * * <p> * Note that because {@link Calendar#add(int, int)} is using * <tt>int</tt>, {@link Duration} with values beyond the * range of <tt>int</tt> in its fields * will cause overflow/underflow to the given {@link Calendar}. * {@link XMLGregorianCalendar#add(Duration)} provides the same * basic operation as this method while avoiding * the overflow/underflow issues. * * @param calendar * A calendar object whose value will be modified. * @throws NullPointerException * if the calendar parameter is null. */ public void addTo(Calendar calendar) { calendar.add(Calendar.YEAR, getYears() * signum); calendar.add(Calendar.MONTH, getMonths() * signum); calendar.add(Calendar.DAY_OF_MONTH, getDays() * signum); calendar.add(Calendar.HOUR, getHours() * signum); calendar.add(Calendar.MINUTE, getMinutes() * signum); calendar.add(Calendar.SECOND, getSeconds() * signum); if (seconds != null) { BigDecimal fraction =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -