durationimpl.java

来自「JAVA 所有包」· Java 代码 · 共 1,826 行 · 第 1/5 页

JAVA
1,826
字号
     *      * @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>.     *      * @see #multiply(BigDecimal)     */    public Duration multiply(int factor) {        return multiply(BigDecimal.valueOf(factor));    }        /**     * Computes a new duration whose value is <code>factor</code> times     * longer than the value of this duration.     *      * <p>     * For example,     * <pre>     * "P1M" (1 month) * "12" = "P12M" (12 months)     * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)     * "P1M" (1 month) * "1.5" = IllegalStateException     * </pre>     *       * <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.     *      * <p>     * The operation will be performed field by field with the precision     * of {@link BigDecimal}. Since all the fields except seconds are     * restricted to hold integers,     * any fraction produced by the computation will be     * carried down toward the next lower unit. For example,     * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day,     * which will be carried down to "PT12H" (12 hours).     * When fractions of month cannot be meaningfully carried down     * to days, or year to months, this will cause an     * {@link IllegalStateException} to be thrown.      * For example if you multiple one month by 0.5.</p>     *      * <p>     * To avoid {@link IllegalStateException}, use     * the {@link #normalizeWith(Calendar)} method to remove the years     * and months fields.     *      * @param factor to multiply by     *      * @return     *      returns a non-null valid {@link Duration} object     *     * @throws IllegalStateException if operation produces fraction in      * the months field.     *     * @throws NullPointerException if the <code>factor</code> parameter is      * <code>null</code>.     *     */    public Duration multiply(BigDecimal factor) {        BigDecimal carry = ZERO;        int factorSign = factor.signum();        factor = factor.abs();                BigDecimal[] buf = new BigDecimal[6];                for (int i = 0; i < 5; i++) {            BigDecimal bd = getFieldAsBigDecimal(FIELDS[i]);            bd = bd.multiply(factor).add(carry);                        buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);                        bd = bd.subtract(buf[i]);            if (i == 1) {                if (bd.signum() != 0) {                    throw new IllegalStateException(); // illegal carry-down                } else {                    carry = ZERO;                }            } else {                carry = bd.multiply(FACTORS[i]);            }        }                if (seconds != null) {            buf[5] = seconds.multiply(factor).add(carry);        } else {            buf[5] = carry;        }                        return new DurationImpl(            this.signum * factorSign >= 0,            toBigInteger(buf[0], null == years),            toBigInteger(buf[1], null == months),            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 dur

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?