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

梦想的初衷 提交于 2019-12-04 05:46:01

问题


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 Sep 27, 2004 03:18:04.922896299 UTC.

The timestamp format includes the first 32-bit unsigned seconds as a field spanning 136 years and the 32-bit fraction field resolving 232 picoseconds. In the timestamp formats, the prime epoch, or base date of era 0, is 0 h 1 January 1900 UTC, when all bits are zero.

This is what I have written for my code so far:

    BigDecimal bi = new BigDecimal("1096255084000");
    double decimal_timestamp = bi.doubleValue();

    DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss.SSS");
    formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(decimal_timestamp);
    String date = formatter.format(calendar.getTime());

    System.out.println(decimal_timestamp + " = " + date); 

My thought is that it is probably not possible with calendar, so I'll have to do it from scratch, but I have no idea how to go about doing that.


回答1:


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.



来源:https://stackoverflow.com/questions/55023949/how-to-convert-decimal-timestamp-to-date-in-java-with-trailing-decimals

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!