How to convert decimal timestamp to date in Java with trailing decimals

前端 未结 1 1942
甜味超标
甜味超标 2021-01-28 21:46

I have been trying to figure out how to convert a timestamp to a date but with the trailing decimals at the end, so for example: Timestamp - C50204EC EC42EE92 is equivalent to S

1条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-28 22:09

    java.time

    Using the example from the explanation:

    Timestamp - C50204EC EC42EE92 is equivalent to Sep 27, 2004 03:18:04.922896299 UTC.

        Instant epoch = OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant();
    
        BigInteger timeStamp = new BigInteger("C50204ECEC42EE92", 16);
    
        // To get the whole part and the fraction right, divide by 2^32
        double secondsSince1900 = timeStamp.doubleValue() / 0x1_0000_0000L;
    
        // Convert seconds to nanos by multiplying by 1 000 000 000
        Instant converted = epoch.plusNanos(Math.round(secondsSince1900 * 1_000_000_000L));
        System.out.println(converted);
    

    Output is:

    2004-09-27T03:18:04.922896384Z

    It’s off by 85 nanoseconds. Likely better floating-point arithmetic can do even better. Edit: A little loss of precision is unavoidable since the original time stamp has a resolution of 2^-32 seconds, which is more than 4 times as fine as the nanosecond (10^-9 second) resolution of Instant.

    The Calendar class that you were trying to use was always poorly designed and is now long outdated. Instead I do as Amongalen suggested in a comment, I am using java.time, the modern Java date and time API. Edit: For comparison Calendar has millisecond resolution, so would at best give you a substabtial loss of precision.

    Edit: More precise math

    I couldn’t let the 85 nanoseconds be. Here’s a version that preserves precision as far as possible and gives the expected result:

        BigDecimal timeStamp = new BigDecimal(new BigInteger("C50204ECEC42EE92", 16));
    
        // To get the whole part and the fraction right, divide by 2^32
        BigDecimal bit32 = new BigDecimal(0x1_0000_0000L);
        BigDecimal secondsSince1900 = timeStamp.divide(bit32);
    
        // Convert seconds to nanos by multiplying by 1 000 000 000; round to long
        long nanosSince1900 = secondsSince1900.multiply(new BigDecimal(TimeUnit.SECONDS.toNanos(1)))
                .setScale(0, RoundingMode.HALF_UP)
                .longValueExact();
    
        Instant converted = epoch.plusNanos(nanosSince1900);
    

    2004-09-27T03:18:04.922896300Z

    1 nano too much? This is because I used half-up rounding in the call to setScale. If instead I truncate (using RoundingMode.FLOOR), I get the exact result from the explanation. So my version doesn’t lose more precision than theirs.

    Link

    Oracle tutorial: Date Time explaining how to use java.time.

    0 讨论(0)
提交回复
热议问题