Skip to content
Advertisement

converting epoch to ZonedDateTime in Java

How to convert epoch like 1413225446.92000 to ZonedDateTime in java?

The code given expects long value hence this will throw NumberFormatException for the value given above.

ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(dateInMillis)), ZoneId.of(TIME_ZONE_PST));

Advertisement

Answer

java.time can directly parse your string

Edit: If your millisecond value is always non-negative, the following DateTimeFormatter can parse it.

private static final String TIME_ZONE_PST = "America/Los_Angeles";
private static final DateTimeFormatter epochFormatter = new DateTimeFormatterBuilder()
        .appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
        .optionalStart()
        .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
        .optionalEnd()
        .toFormatter()
        .withZone(ZoneId.of(TIME_ZONE_PST));

Now parsing into a ZonedDateTime is just one method call:

    ZonedDateTime zdt = ZonedDateTime.parse(dateInMillis, epochFormatter);
    System.out.println(zdt);

Output is:

2014-10-13T11:37:26.920-07:00[America/Los_Angeles]

It will not work correctly with a negative value: the fraction would still be parsed as positive, which I am assuming would be incorrect. To be sure to be notified in case of a negative value I have specified in the formatter that the number cannot be signed.

A more general solution: use BigDecimal

If you need a more general solution, for example including negative numbers, I think it’s best to let BigDecinmal parse the number and do the math.

    BigDecimal bd = new BigDecimal(dateInMillis);
    BigDecimal[] wholeAndFractional = bd.divideAndRemainder(BigDecimal.ONE);
    long seconds = wholeAndFractional[0].longValueExact();
    int nanos = wholeAndFractional[1].movePointRight(9).intValue();
    ZonedDateTime zdt = Instant.ofEpochSecond(seconds, nanos)
            .atZone(ZoneId.of(TIME_ZONE_PST));

Output is the same as before. Only now we can also handle negative numbers according to expectations:

    String dateInMillis = "-1.5";

1969-12-31T15:59:58.500-08:00[America/Los_Angeles]

Even scientific notation is accepted:

    String dateInMillis = "1.41322544692E9";

2014-10-13T11:37:26.920-07:00[America/Los_Angeles]

If finer precision than nanoseconds is possible in the string, consider how you want to truncate or round, and instruct BigDecimal accordingly, there are a number of options.

Original answer

Basil Bourque’s answer is a good one. Taking out the nanoseconds from the fractional part into an integer for nanoseconds may entail a pitfall or two. I suggest:

    String dateInMillis = "1413225446.92000";
    String[] secondsAndFraction = dateInMillis.split("\.");
    int nanos = 0;
    if (secondsAndFraction.length > 1) { // there’s a fractional part
        // extend fractional part to 9 digits to obtain nanoseconds
        String nanosecondsString
                = (secondsAndFraction[1] + "000000000").substring(0, 9);
        nanos = Integer.parseInt(nanosecondsString);
        // if the double number was negative, the nanos must be too
        if (dateInMillis.startsWith("-")) {
            nanos = -nanos;
        } 
    }
    ZonedDateTime zdt = Instant
            .ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
            .atZone(ZoneId.of("Asia/Manila"));
    System.out.println(zdt);

This prints

2014-10-14T02:37:26.920+08:00[Asia/Manila]

We don’t need 64 bits for the nanoseconds, so I am just using an int.

Assumption: I have assumed that your string contains a floating-point number and that it may be signed, for example -1.50 would mean one and a half seconds before the epoch. If one day your epoch time comes in scientific notation (1.41322544692E9), the above will not work.

Please substitute your desired time zone in the region/city format if it didn’t happen to be Asia/Manila, for example America/Vancouver, America/Los_Angeles or Pacific/Pitcairn. Avoid three letter abbreviations like PST, they are ambiguous and often not true time zones.

2 People found this is helpful
Advertisement