Java time parse "Jun 26th 2021, 04:30:15 pm NY"

Viewed 170

I have a String that look like this:

String str = "Jun 26th 2021, 04:30:15 pm NY";

I want to convert it to ZonedDateTime, for this I use DateTimeFormatterBuilder:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendPattern("MMM dd'th' uuuu, h:mm:ss a z")
        .toFormatter(Locale.US);
ZonedDateTime result = ZonedDateTime.parse(str, formatter);

but the parser is not happy with this format I got this error:

java.time.format.DateTimeParseException: Text 'Jun 26th 2021, 04:30:15 pm NY' could not be parsed at index 27

It seems that NY not covered by the z, any idea about this error please, also is there any trick to avoid 'th' in the parser ?

2 Answers

DateTimeFormatter#parse(CharSequence, ParsePosition) is at your disposal.

Note that NY is not the name of a timezone. The naming convention for timezone is Region/City e.g. Europe/Paris. You can get the list of timezone names using ZoneId#getAvailableZoneIds.

Also, for day-of-month with ordinal e.g. 26th, you can build a Map as shown in the following code.

Demo:

import java.text.ParsePosition;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        String strDateTime ="Jun 26th 2021, 04:30:15 pm NY";
    
        DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .appendPattern("[MMMM][MMM] ") // caters for both full name and 3-letter abbv.
                .appendText(ChronoField.DAY_OF_MONTH, ordinalMap())
                .appendPattern(" u, h:m:s a")
                .toFormatter(Locale.ENGLISH);

        LocalDateTime ldt = LocalDateTime.from(dtf.parse(strDateTime, new ParsePosition(0)));
        ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));
        System.out.println(zdt);
    }
    static Map<Long, String> ordinalMap() {
        String[] suffix = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
        Map<Long, String> map = new HashMap<>();
        
        for (int i = 1; i <= 31; i++) 
            map.put((long)i, String.valueOf(i) + suffix[(i > 3 && i < 21) ? 0 : (i % 10)]);
        
        return map;
    }
}

Output:

2021-06-26T16:30:15-04:00[America/New_York]

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.

Courtesy: The logic to build the Map is based on this excellent answer.

As highlighted by Sweeper, NY is not a recognised timezone by Java. To minimize changes to your code you can use the DateTimeFormatter#withZone method of your DateTimeFormatter formatter returning a copy of your formatter with the new override zone like below:

String str = "Jun 26th 2021, 04:30:15 pm"; //<-- erased NY
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendPattern("MMM dd'th' uuuu, h:mm:ss a")
        .toFormatter(Locale.US);
ZonedDateTime result = ZonedDateTime.parse(str, formatter.withZone(ZoneId.of("America/New_York")));
System.out.println(result); //<-- 2021-06-26T16:30:15-04:00[America/New_York]
Related