0

I am getting input from user in this format "Tue, 12 May" and I would like to change the format into this: "Tue_12_May".

The input format can have 3/4 letters for month, for example: May or July or Feb etc. Output format for month doesn't matter.

So far I have tried the following code snippet:

public static String dateFormatter(String dateWithSpace) {
    DateTimeFormatter formatter1 = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("EEE, dd LLL").toFormatter(Locale.ENGLISH);
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("E_d_M/L");
    LocalDate date = LocalDate.parse(dateWithSpace.trim(), formatter1);
    return date.format(formatter2);
}

I keep getting this error:

java.time.format.DateTimeParseException: Text 'Tue, 12 May' could not be parsed at index 8

UkFLSUI
  • 5,509
  • 6
  • 32
  • 47
  • 1
    The format is what you use to take the input, and it has to match your input. The output format is different. If you have something that simple though, you can just `dateWithSpace.replaceAll(",? ", "_")` – Zoe May 03 '20 at 10:50
  • it is not working for the input cause there are both "," and " " from the input. – Farbod Alidaei May 03 '20 at 10:54
  • yeah, I saw your edit, so I edited. replaceAll uses regex, `?` makes the `,` optional – Zoe May 03 '20 at 10:55
  • It works in this way but that would be nice if someone can tell me how could I change my own code to have a proper output – Farbod Alidaei May 03 '20 at 10:57
  • Does this answer your question? [Change date format in a Java string](https://stackoverflow.com/questions/4772425/change-date-format-in-a-java-string) – Zoe May 03 '20 at 10:59
  • No, @Zoe, thank your for trying to find an existing solution, but it does not answer this question. It recommends `LocalDateTime` or `ZonedDateTime`, but no date/time class fits the information in this questioner’s strings. We’ve got some challenges here that are quite unique. – Ole V.V. May 03 '20 at 12:11

2 Answers2

3

The hack

    String dateWithSpace = "Tue, 12 May";
    DateTimeFormatter formatter1 = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("EEE, d [MMMM][MMM]")
            .toFormatter(Locale.ENGLISH);
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("E_d_MMMM", Locale.ENGLISH);
    TemporalAccessor parsed = formatter1.parse(dateWithSpace .trim());
    String formatted = formatter2.format(parsed);

    System.out.println(formatted);

Output:

Tue_12_May

The challenge here is that we cannot represent “Tuesday 12th May” in a LocalDate. For a LocalDate we need a year. We cannot just decide on some fixed year for two reasons: (1) Whatever we decide would probably be wrong and hence introduce an error in our program, also when the error would not surface initially; (2) LocalDate would object to the incorrect day of week if we didn’t have the luck to pick a year where May 12 falls on a Tuesday. And even if we forced it somehow, it would not print Tue back, but the correct day of week for the year we had picked. There isn’t any other type that holds a day of week, day of month and month without a year either.

The good solution would be to find out which year is correct. Maybe try this year and next year in turn and throw an exception if neither matches?

In the meantime my hack is not to decide on any concrete type but just use the undecided TemporalAccessor interface.

EDIT: To accept short month names in full (June, July and also May) and three letter abbreviations for longer month names (e.g., Feb, Dec), I am using [MMMM][MMM]: optional full month name followed by optional month abbreviation. So one of them will always match and the other be ignored.

Other examples of input and output:

Wed, 1 July  -> Wed_1_July
Thu, 27 Aug  -> Thu_27_August
Mon, 8 Feb   -> Mon_8_February

On your code

I have some comments, most of them minor:

  • You shouldn’t need parseCaseInsensitive() for your example string (maybe for other strings you might be getting, I cannot know).
  • EDIT: To accept one digit day of month put just one d in the format pattern string for parsing. It will still accept two digits too. Further decide whether you want two digits in the output always or only one digit for the first 9 days of the month.
  • Prefer MMM over LLL in the format pattern for parsing. LLL is for when the month is not part of a date (in some languages that makes a difference as to what form of the month name to use).
  • For formatting too use MMM for month abbreviation, not M/L.
  • Provide a locale for formatting too.
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thank you. It seems working. The only problem is, when the input date contains 1 digit like "Wed, 1 July" system throws an error saying "java.time.format.DateTimeParseException: Text 'Wed, 1 July' could not be parsed at index 5". – Farbod Alidaei May 03 '20 at 11:28
  • It is weird but it does not work. I am still getting the following error:java.time.format.DateTimeParseException: Text 'Wed, 1 July' could not be parsed, unparsed text found at index 10. – Farbod Alidaei May 03 '20 at 11:45
  • Is that possible to make the input format flexible? Sometimes month contains 3 digits, sometimes 4 but the output format does not matter. – Farbod Alidaei May 03 '20 at 11:47
  • 1
    Ah, I think I understand. Does the month always come written out in full, as `January`, `February`, etc.? In that case we need `MMMM`, not `MMM`. – Ole V.V. May 03 '20 at 11:48
  • Months always come either in 3 digit like "May" or 4 digit like "July". – Farbod Alidaei May 03 '20 at 11:49
  • If February may come as either `Feb` or `Febr`, it’s a bit more complicated. It can be done, there are a couple of ways. – Ole V.V. May 03 '20 at 11:50
  • February always com as Feb. Months like July which contains less letters are stated as full. – Farbod Alidaei May 03 '20 at 11:51
  • I think I fixed that. If you change formatter1 to "EEE, d MMMM" and formatter2 to "E_d_MMMM" it gona be fixed. Your explanation was super helpful. Tnx a lot. – Farbod Alidaei May 03 '20 at 11:53
  • I’m afraid that `EEE, d MMMM` won’t cover all cases. Please see my new edit for a more flexible solution. – Ole V.V. May 03 '20 at 12:02
  • V.V Is there a way I can manage to perform the same thing on my input but based on the US style date formatting and Europian style date formatting? Like Wed, 12 May and Wed, May 12 – Farbod Alidaei May 03 '20 at 15:49
  • @FarbodAlidaei I think you can go ahead with `[[MMMM][MMM] d][d [MMMM][MMM]]` in your format pattern string. If it requires more than this, please post a new question. And a link to the new question here, then I’ll take a look. – Ole V.V. May 03 '20 at 18:06
0

You could do it simply in this way:

public static String dateFormatter(String dateWithSpace) {
    DateTimeFormatter formatter1 = new DateTimeFormatterBuilder()
                    .appendOptional(DateTimeFormatter.ofPattern("E, d LLLL"))
                    .appendOptional(DateTimeFormatter.ofPattern("E, d LLL"))
                    .toFormatter();
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("E_d_LLL");
    return formatter2.format(formatter1.parse(dateWithSpace));
}

In your case the problem is that you are using LocalDate, but missing one important parameter of LocalDate, i.e. year. You can refer to the LocalDate Documentation:

LocalDate is an immutable date-time object that represents a date, often viewed as year-month-day. Other date fields, such as day-of-year, day-of-week and week-of-year, can also be accessed. For example, the value "2nd October 2007" can be stored in a LocalDate.

UkFLSUI
  • 5,509
  • 6
  • 32
  • 47