0

I have following timestamp string "2021010112:12:12.10:00" and I want to convert it to java.time.Instant.

The issue in parsing string through DateTimeFormatter is due to absence of time zone sign before last 4 digits. Logically it is yyyyMMddHH:mm:ss. and 10:00 is time zone offset e.g UTC+10:00, but issue is it does not have sign.

How can I parse this string to Instant object?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161

3 Answers3

2

Not very elegant, but you could split the input by a dot. That would separate the datetime part from the offset and you can concatenate the desired (and required) sign with the value.

This requires you to know which sign to apply! The code cannot guess it...

Maybe write a method that takes this input String and a sign to be applied as arguments.

Since it seems not possible to parse an unsigned String representation of an offset, you would need something like the following:

public static void main(String[] args) {
    String timestamp = "2021010112:12:12.10:00";
    // provide a formatter that parses the datetime (the part before the dot)
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHH:mm:ss");
    // split the timestamp String by the dot to separate datetime from offset
    String[] split = timestamp.split("\\.");
    // parse the datetime part using the formatter defined above
    LocalDateTime ldt = LocalDateTime.parse(split[0], dtf);
    // and build up an offset using offset part adding a plus sign
    ZoneOffset zoneOffset = ZoneOffset.of("+" + split[1]);
    // then create an OffsetDateTime from the LocalDateTime and the ZoneOffset
    OffsetDateTime result = OffsetDateTime.of(ldt, zoneOffset);
    // finally get an Instant from it
    Instant instant = result.toInstant();  // <--- INSTANT HERE
    // and print the values
    System.out.println(result + " = " + instant.toEpochMilli());
}

This outputs

2021-01-01T12:12:12+10:00 = 1609467132000
deHaar
  • 17,687
  • 10
  • 38
  • 51
  • 1
    *// and build up an offset using offset part adding a plus sign* How would you know you *can* assume a plus sign? – g00se Aug 30 '21 at 11:36
  • 1
    @g00se I can't... Probably nobody can, but OP could use this example to create a method that passes the `String` and the desired sign. If OP wants the code to *guess* the sign, there won't be any possibility of it guessing right in every single case. – deHaar Aug 30 '21 at 11:40
2

The answer by deHaar is correct. This answer shows an easier way of solving this problem.

You can use the regex, (\.)(\d{1,2}:\d{1,2}) to replace the . (group#1 in the regex) before the timezone offset part (group#2 in the regex) with a +.

Demo:

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2021010112:12:12.10:00";
        strDateTime = strDateTime.replaceFirst("(\\.)(\\d{1,2}:\\d{1,2})", "+$2");

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHH:mm:ssXXX", Locale.ENGLISH);
        Instant instant = OffsetDateTime.parse(strDateTime, dtf).toInstant();
        System.out.println(instant);
    }
}

Output:

2021-01-01T02:12:12Z

ONLINE DEMO

Alternatively, you can use the regex, \.(\d{1,2}:\d{1,2}) and prepend group#1 with a + sign. Note that the DateTimeFormatter needs to be adjusted accordingly.

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2021010112:12:12.10:00";
        strDateTime = strDateTime.replaceFirst("\\.(\\d{1,2}:\\d{1,2})", ".+$1");

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHH:mm:ss.XXX", Locale.ENGLISH);
        Instant instant = OffsetDateTime.parse(strDateTime, dtf).toInstant();
        System.out.println(instant);
    }
}

ONLINE DEMO

The benefits of using this alternative solution is as described in the following comment by Ole V.V.:

Just speculating, maybe a minus sign would be present in case of a negative UTC offset. If so, maybe use strDateTime.replaceFirst("\\.(\\d{1,2}:\\d{1,2})", ".+$1") to obtain 2021010112:12:12.+10:00 in the positive offset case, after which both positive and negative offset (and zero) could be parsed.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • Just speculating, maybe a minus sign would be present in case of a negative UTC offset. If so, maybe use `strDateTime.replaceFirst("\\.(\\d{1,2}:\\d{1,2})", ".+$1")` to obtain `2021010112:12:12.+10:00` in the positive offset case, after which both positive and negative offset (and zero) could be parsed. – Ole V.V. Aug 30 '21 at 17:51
  • @OleV.V. - What a co-incidence! I had done exactly this way in my first attempt but before posting it finally, I changed my mind. Let me post it as an alternative solution. – Arvind Kumar Avinash Aug 30 '21 at 19:14
2

ParsePosition

There are two good answers already. Here’s my suggestion.

    String timestampString = "2021010112:12:12.10:00";
    
    ParsePosition position = new ParsePosition(0);
    TemporalAccessor parsed = PARSER.parse(timestampString, position);
    LocalDateTime dateTime = LocalDateTime.from(parsed);
    
    String offsetString = timestampString.substring(position.getIndex());
    if (Character.isDigit(offsetString.charAt(0))) { // no sign
        offsetString = "+" + offsetString;
    }
    ZoneOffset offset = ZoneOffset.of(offsetString);
    
    Instant timestamp = dateTime.atOffset(offset).toInstant();
    
    System.out.println(timestamp);

Output:

2021-01-01T02:12:12Z

The downside is the use of the TemporalAccessor interface, a low-level interface that we should not normally use in application code. The upsides include that the code accepts strings with and without sign before the offset and we don’t need any split operation or other application of regular expressions. If the UTC offset is negative, the sign must be there, or we can’t tell. Let’s also try this:

    String timestampString = "2021010112:12:12.-10:00";

2021-01-01T22:12:12Z

I believe that the ParsePosition class is the only class from the old java.text package that is reused in java.time, which I personally find curious.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161