4

I have a date in the following format: 1/1/2020 3:4:7 AM I am trying to format it using DateTimeFormatter.

I have the following code with a formatter to parse it, but it doesn't work.

LocalDateTime date = LocalDateTime.parse("1/1/2020 3:4:7 AM", DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a"));

I get the following exception:

java.time.format.DateTimeParseException: Text '1/1/2020 3:4:7 AM' could not be parsed at index 0

Can anyone help me?

Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
user2254180
  • 844
  • 13
  • 30

4 Answers4

8

Two separate problems:

Wrong counts

You're using e.g. MM which is an explicit: Always the full amount of digits, zero-padded. Your string is not like that, it's just the number. So, make that M/d/uuuu h:m:s a.

EDIT: Changed yyyy to uuuu, thanks, @deHaar. Reasoning: yyyy or uuuu rarely matters, but note that this mean 4 digits are required. The difference kicks in for years before 0: uuuu goes negative, yyyy does not and expects you to use e.g. GG so that you get 44 BC instead of -44. In that way, uuuu is just more correct, even though usually the difference is not going to come up.

Missing locale

The second problem is that you should pretty much never use this version of ofPattern - it has a bug, which you can't catch with unit tests, which makes that a bug that is thousands of times 'heavier', and thus, a real problem.

You need to specify locale. Without it, 'AM' is not going to parse unless your platform default locale is english.

Putting it together

LocalDateTime date = LocalDateTime.parse("1/1/2020 3:4:7 AM",
  DateTimeFormatter.ofPattern("M/d/uuuu h:m:s a", Locale.ENGLISH));

works great.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Thank you, I am marking your answer as accepted as you pointed out that Local.ENGLISH was also required. – user2254180 Oct 05 '20 at 14:11
  • 1
    I would use `uuuu` for the year (of no specific era), otherwise this answer got a well-deserved accept... – deHaar Oct 05 '20 at 14:22
4
  1. Use single letters for date and time components (month, day, year, hour, minute, and second).

  2. You can also make the formatter handle the pattern in a case insensitive way (e.g. am, AM, Am) as shown below:

    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.util.Locale;
    
    public class Main {
        public static void main(String[] args) {
             // Formatter to handle the pattern in case insensitive way (e.g. am, AM, Am)
            DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                                                .parseCaseInsensitive()
                                                .appendPattern("M/d/u h:m:s a")
                                                .toFormatter(Locale.ENGLISH);
            LocalDateTime date = LocalDateTime.parse("1/1/2020 3:4:7 AM", formatter);
            System.out.println(date);
        }
    }
    

Output:

2020-01-01T03:04:07
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
4

In your snippet:

LocalDateTime
    .parse("1/1/2020 3:4:7 AM", DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a"));
  • 1 - does not match MM
  • 1 - does not match dd
  • 3 - does not match hh
  • 4 - does not match mm
  • 7 - does not match ss

i.e. the lengths of the formatter pattern parts (e.g. MM) and their respective parts from the string text (e.g. 1) do not match.

You can match them in a few ways, e.g. you can either change the string text to match the formatter pattern or other way around.

You can try this instead:

LocalDateTime
    .parse("01/01/2020 03:04:07 AM", DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a"));

Additionally, have a look at the Pattern Letters and Symbols.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
2

According to the documentation of DateTimeFormatter:

Number: If the count of letters is one, then the value is output using the minimum number of digits and without padding. Otherwise, the count of digits is used as the width of the output field, with the value zero-padded as necessary.

By reversing the reasoning, you can try using this formatter instead:

DateTimeFormatter.ofPattern("M/d/yyyy h:m:s a")
0009laH
  • 1,960
  • 13
  • 27