Cannot convert from java Double to java Date

2019-03-06 20:02发布

问题:

I am having an issue with converting a double to a Java date object in the format: yyyy-MM-dd HH:mm:ss

I have tried to convert this double value to a long value then instanciate a Date object with the result, but I got errors saying that I cannot convert from double to long.

my timestamp double values are in this format:

1.511554592277516E9

Can anybody help me with this issue please, thanks in advance.

回答1:

Assuming that this floating point value is seconds past the Unix Epoch of 1 Jan 1970 0:00 GMT, this will provide a conversion to a LocalDateTime with that offset:

LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(
                Double.valueOf(1.511554592277516E9).longValue(), 0, ZoneOffset.UTC);
System.out.println(localDateTime);

I leave converting this to a Date as an exercise for the reader.



回答2:

Your floating-point value, 1.511554592277516E9, no doubt denotes seconds since the epoch of January 1, 1970 at midnight UTC, with microsecond precision: 1 511 554 592 seconds and 277 516 microseconds (millionths of a second).

I suggest using java.time, the modern Java date and time API also known as JSR-310, for this. It is much nicer to work with than the outdated Date class and friends, and also offers nanosecond precision (Date only has millisecond precision, so you would lose precision if converting into one). More specifically I will first create a java.time.Instant object (conversion to other date-time types will be easy, I’ll touch on one example at the end).

Getting the full precision through into an Instant requires a little thought. I played a little with double and long, but realized (1) double doesn’t have the full precision required, the nanoseconds will not be right (2) converting to a long holding the nanoseconds (not the only way, but certainly the easiest) will create a “year 2262 problem”, so if you are handling dates in a far future, it will not work. In any case, I think that the easy and safe solution is to use BigDecimal for the math required before feeding the numbers into an Instant.

    String secondsSinceEpoch = "1.511554592277516E9";
    BigDecimal decimalSeconds = new BigDecimal(secondsSinceEpoch);
    long seconds = decimalSeconds.longValue();
    long nanos = decimalSeconds.subtract(BigDecimal.valueOf(seconds))
            .movePointRight(9)
            .longValueExact();
    Instant inst = Instant.ofEpochSecond(seconds, nanos);
    System.out.println(inst);

This prints:

2017-11-24T20:16:32.277516Z

The printed date-time is in UTC. If the value is as expected, I should say it confirms that your floating-point value was indeed seconds since the epoch.

You requested a date-time in the format yyyy-MM-dd HH:mm:ss. You will need to decide in which time zone you want the date-time. Date objects don’t have a format, so you will also need to get the format in a string. For example:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    System.out.println(inst.atZone(ZoneId.of("America/Vancouver")).format(formatter));

This prints

2017-11-24 12:16:32

You substitute your desired time zone, of course. atZone() converts the Instant into a ZonedDateTime, another often used class of java.time.

EDIT: In case you don’t want to bother with BigDecimal, you accept a slight inaccuracy and/or your receive your value as a double rather than a string, here’s an alternative in fewer lines of code:

    double secondsSinceEpoch = 1.511554592277516E9;
    long longSeconds = (long) secondsSinceEpoch;
    long micros = Math.round((secondsSinceEpoch - longSeconds) * 1_000_000);
    Instant inst = Instant.ofEpochSecond(longSeconds).plus(micros , ChronoUnit.MICROS);

In this particular case it gives exactly the same result, down to the nanosecond. I’m not sure whether with other inputs, the microseconds may end up inaccurate, but on the other hand, if you receive a double (not a string), there’s nothing you could do about that anyway.



标签: java date double