DateTime("2019-08-07T19:20-5:00") conversion to local date time with offset value

Viewed 107

I get the datetime content in the below string format with offset time value from the source system.

2019-08-07T19:20-5:00

Where I want to convert that into date time using the offset value. I tried the below, but not getting the expected result.

OffsetDateTime sourceDateTime = OffsetDateTime.parse("2019-08-07T19:20-5:00"); 
System.out.println(sourceDateTime.getOffset());

Output

Exception in thread "main" java.time.format.DateTimeParseException: Text '2019-08-07T19:20-5:00' could not be parsed at index 16
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:402)
    at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:387)
    at com.example.demo.Testfile.main(Testfile.java:19)

Is there any simple way to the expected output?

Note: I am using Java 8

2 Answers

You cannot use an ISO formatter if you don't get an ISO String… The problem seems to be the format of the offset you get, it is +H:mm instead of the one used in the ISO standard, that is +HH:mm. In words: It does not consider single-digit hours.

You will have to build a specific DateTimeFormatter for this situation.

Unfortunately, Java 8 does not support patterns with a single-digit representation of offset hours, but if you can use Java 9 or higher, you'll be able to define a DateTimeFormatter that considers zone offsets formatted as +H:mm:

Java 9+

public static void main(String[] args) {
    DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                                    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                                    .appendOffset("+H:mm", "UTC")
                                    .toFormatter(Locale.ENGLISH);
    OffsetDateTime sourceDateTime = OffsetDateTime.parse("2019-08-07T19:20-5:00", dtf);
    System.out.println(sourceDateTime.getOffset());
}

This uses the ISO formatter for a LocalDateTime and appends a specific pattern for parsing offsets.

Admittedly, I'm not sure if "UTC" is a good (hard-coded) choice as a default offset in case the String does not contain any.

Output of this code:

-05:00

Java 8 --> String manipulation

public static void main(String[] args) {
    // provide some variants of input
    List<String> variants = List.of(
            "2019-08-07T19:20-5:00", "2019-08-07T19:20+5:00",
            "2019-08-07T19:20-05:00", "2019-08-07T19:20+05:00"
    );
    
    // handle offset extraction via String manipulation for each variant
    variants.forEach(variant -> {
        String parseable = variant;
        /*
         * This assumes an offset always has a sign and 
         * a colon that separates hours and minutes.
         *
         * Get all their indexes in order to find the offset
         */
        int lastIndexOfColon = variant.lastIndexOf(':');
        int lastIndexOfMinus = variant.lastIndexOf('-');
        int lastIndexOfPlus = variant.lastIndexOf('+');
        
        /*
         * Decide which sign actually leads the offset
         * (it's either a plus or the very last minus).
         * In either case insert a leading zero if the amount of digits/chars
         * between '-'/'+' and ':' is 1, otherwise don't do anything…
         */
        if (lastIndexOfMinus > lastIndexOfPlus && lastIndexOfMinus + 2 == lastIndexOfColon) {
            parseable = variant.substring(0, lastIndexOfMinus + 1) 
                        + '0' 
                        + variant.substring(lastIndexOfMinus + 1);
        } else if (lastIndexOfPlus + 2 == lastIndexOfColon) {
            parseable = variant.substring(0, lastIndexOfPlus + 1)
                        + '0'
                        + variant.substring(lastIndexOfPlus + 1);   
        }
        // then parse the manipulated String
        OffsetDateTime odt = OffsetDateTime.parse(parseable);
        // get the offset
        ZoneOffset zoneOffset = odt.getOffset();
        // and print some result
        String msg = String.format("%s has an offset of %s", variant, zoneOffset);
        System.out.println(msg);
    });
}

The output of this example is

2019-08-07T19:20-5:00 has an offset of -05:00
2019-08-07T19:20+5:00 has an offset of +05:00
2019-08-07T19:20-05:00 has an offset of -05:00
2019-08-07T19:20+05:00 has an offset of +05:00

Another way could be to separate the offset part from the date and time and create a ZoneOffset.of(int, int) using parsed values left and right to the last colon. Parsing the date-time part and applying the offset afterwards could also lead to the desired result.

This is the correct code

OffsetDateTime sourceDateTime = OffsetDateTime.parse("2019-08-07T19:20-05:00");
System.out.println(sourceDateTime.getOffset());
Related