/*
 * Copyright 2013 FasterXML.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */

package com.fasterxml.jackson.datatype.jsr310;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.function.BiFunction;

Utilities to aid in the translation of decimal types to/from multiple parts.
Author:Nick Williams
Since:2.2
/** * Utilities to aid in the translation of decimal types to/from multiple parts. * * @author Nick Williams * @since 2.2 */
public final class DecimalUtils { private static final BigDecimal ONE_BILLION = new BigDecimal(1_000_000_000L); private DecimalUtils() { } public static String toDecimal(long seconds, int nanoseconds) { StringBuilder sb = new StringBuilder(20) .append(seconds) .append('.'); // 14-Mar-2016, tatu: Although we do not yet (with 2.7) trim trailing zeroes, // for general case, if (nanoseconds == 0L) { // !!! TODO: 14-Mar-2016, tatu: as per [datatype-jsr310], should trim // trailing zeroes if (seconds == 0L) { return "0.0"; } // sb.append('0'); sb.append("000000000"); } else { StringBuilder nanoSB = new StringBuilder(9); nanoSB.append(nanoseconds); // May need to both prepend leading nanos (if value less than 0.1) final int nanosLen = nanoSB.length(); int prepZeroes = 9 - nanosLen; while (prepZeroes > 0) { --prepZeroes; sb.append('0'); } // !!! TODO: 14-Mar-2016, tatu: as per [datatype-jsr310], should trim // trailing zeroes /* // AND possibly trim trailing ones int i = nanosLen; while ((i > 1) && nanoSB.charAt(i-1) == '0') { --i; } if (i < nanosLen) { nanoSB.setLength(i); } */ sb.append(nanoSB); } return sb.toString(); }
Factory method for constructing BigDecimal out of second, nano-second components.
Since:2.8
/** * Factory method for constructing {@link BigDecimal} out of second, nano-second * components. * * @since 2.8 */
public static BigDecimal toBigDecimal(long seconds, int nanoseconds) { if (nanoseconds == 0L) { // 14-Mar-2015, tatu: Let's retain one zero to avoid interpretation // as integral number if (seconds == 0L) { // except for "0.0" where it can not be done without scientific notation return BigDecimal.ZERO.setScale(1); } return BigDecimal.valueOf(seconds).setScale(9); } return new BigDecimal(toDecimal(seconds, nanoseconds)); }
Deprecated:due to potential unbounded latency on some JRE releases.
/** * @deprecated due to potential unbounded latency on some JRE releases. */
@Deprecated // since 2.9.8 public static int extractNanosecondDecimal(BigDecimal value, long integer) { // !!! 14-Mar-2016, tatu: Somewhat inefficient; should replace with functionally // equivalent code that just subtracts integral part? (or, measure and show // there's no difference and do nothing... ) return value.subtract(new BigDecimal(integer)).multiply(ONE_BILLION).intValue(); }
Extracts the seconds and nanoseconds component of seconds as long and int values, passing them to the given converter. The implementation avoids latency issues present on some JRE releases.
Since:2.9.8
/** * Extracts the seconds and nanoseconds component of {@code seconds} as {@code long} and {@code int} * values, passing them to the given converter. The implementation avoids latency issues present * on some JRE releases. * * @since 2.9.8 */
public static <T> T extractSecondsAndNanos(BigDecimal seconds, BiFunction<Long, Integer, T> convert) { // Complexity is here to workaround unbounded latency in some BigDecimal operations. // https://github.com/FasterXML/jackson-databind/issues/2141 long secondsOnly; int nanosOnly; BigDecimal nanoseconds = seconds.scaleByPowerOfTen(9); if (nanoseconds.precision() - nanoseconds.scale() <= 0) { // There are no non-zero digits to the left of the decimal point. // This protects against very negative exponents. secondsOnly = nanosOnly = 0; } else if (seconds.scale() < -63) { // There would be no low-order bits once we chop to a long. // This protects against very positive exponents. secondsOnly = nanosOnly = 0; } else { // Now we know that seconds has reasonable scale, we can safely chop it apart. secondsOnly = seconds.longValue(); nanosOnly = nanoseconds.subtract(new BigDecimal(secondsOnly).scaleByPowerOfTen(9)).intValue(); if (secondsOnly < 0 && secondsOnly > Instant.MIN.getEpochSecond()) { // Issue #69 and Issue #120: avoid sending a negative adjustment to the Instant constructor, we want this as the actual nanos nanosOnly = Math.abs(nanosOnly); } } return convert.apply(secondsOnly, nanosOnly); } }