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 ins
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.
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.