java.text.ParseException: Unparseable date : "..."
Asked Answered
T

3

5

I get this error with this code:

SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM HH:mm yyyy",myDateFormatSymbols);
sdf.parse("понеділок 12 квітень 07:00 2021");

Whis is "Monday 12 April 07:00 2021". The thing is, whenever I change the day from Monday to Tuesday ("вівторок"), I don't get this error, and the code works. Here's the code for myDateFormatSymbols:

private final static DateFormatSymbols myDateFormatSymbols = new DateFormatSymbols(){
        @Override
        public String[] getWeekdays(){
             return new String[]{"понеділок","вівторок", "середа", "четвер", "пятниця", "субота", "неділя"};
        }
        @Override
        public String[] getMonths() {
            return new String[]{...};
        }
}

All the months and weekdays are working correctly, seems that this error only occurs with Monday.

Tragopan answered 9/4, 2021 at 22:14 Comment(5)
I recommend you don’t use SimpleDateFormat and Date. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use LocalDateTime and DateTimeFormatter, both from java.time, the modern Java date and time API.Estren
@OleV.V. Actually, I tried and failed to do this in java.time. I used a Locale of Ukrainian (per Google Translate's guess): new Locale.Builder().setLanguage( "uk" ).setRegion( "UA" ).build();. I get a DateTimeParseException saying "could not be parsed at index 13" (the month name). I pasted a variation of the month name Квітень copied from Google Translate translating "April" to Ukrainian. I verified that понеділок = Monday = 2021-04-12. If you can see deleted Answers, see mine. Perhaps you can take a stab at it.Hypercorrection
@Tragopan What language is this input string? What Locale is in effect when your code runs successfully?Hypercorrection
I take it that you are trying to parse strings in Ukrainian, and your strings haven’t got the apostrophe in п'ятниця (Friday) that Java thinks should be there, which is why you need to supply your own day names. Is this correct? Are there other reasons too why you are creating myDateFormatSymbols?Estren
Which Java version are you using? In particular the solution may be different for Java 7, 8 and 9.Estren
E
2

You can check the Javadoc for DateFormatSymbols#weekdays, the element at index 0 is always ignored unfortunately.

I'd simply replace it by an empty string.

Weekday strings. For example: "Sunday", "Monday", etc. An array of 8 strings, indexed by Calendar.SUNDAY, Calendar.MONDAY, etc. The element weekdays[0] is ignored.


The following code now prints the expected answer

DateFormatSymbols myDateFormatSymbols = new DateFormatSymbols() {
    @Override
    public String[] getWeekdays() {
        return new String[]{"", "понеділок", "вівторок", "середа", "четвер", "пятниця", "субота", "неділя"};
    }

    @Override
    public String[] getMonths() {
        return new String[]{"квітень"};
    }
};

SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM HH:mm yyyy", myDateFormatSymbols);
System.out.println(sdf.parse("понеділок 12 квітень 07:00 2021")); // Tue Jan 12 07:00:00 CET 2021
Elicia answered 9/4, 2021 at 22:22 Comment(2)
Thank you so much for the answer.So, the proper solution would be to add "" at the beginning of the array?Tragopan
Yes indeed @Tragopan I've added how the code should look like nowElicia
E
5
  • java.time

  • Standalone form of month name

  • DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>)

I warmly recommend that you use java.time, the modern Java date and time API (links at the bottom), for your date and time work.

You are trying to parse a date-time string in Ukrainian. We would immediately have expected Java to do this out of the box using uk or uk-UA locale. Edit: To my surprise your strings are using what Java considers the standalone form of month names (it seems that I have not understood what the standalone form of a month name means). To specify this form in formatting use LLLL in the format pattern string rather than MMMM. Also the name of Friday in your strings is different from the name that Java knows (пʼятниця with an apostrophe, which, as Basil Bourque has already said, comes from CLDR). The solution is, as you already tried, to specify your own names of the days. In java.time this is done through a DateTimeFormatterBuilder and its two-arg appendText method. For example:

private static final Map<Long, String> DAY_NAMES = Map.of(1L, "понеділок", 2L, "вівторок",
        3L, "середа", 4L, "четвер", 5L, "пятниця", 6L, "субота", 7L, "неділя");
private static final Locale UKRAINIAN = Locale.forLanguageTag("uk-UA");
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
        .appendText(ChronoField.DAY_OF_WEEK, DAY_NAMES)
        .appendPattern(" dd LLLL HH:mm uuuu")
        .toFormatter(UKRAINIAN);

Now with the formatter in place, parsing is trivial as it should be:

    String stringToParse = "понеділок 12 квітень 07:00 2021";
    LocalDateTime dateTime = LocalDateTime.parse(stringToParse, FORMATTER);
    System.out.println(dateTime);

Output:

2021-04-12T07:00

I find my code much clearer to read than yours, which is what counts for code. There are no funny overrides. There is no insane numbering of the months from 0. Monday is the first day of the week as it is in your question and in Ukraine.

Also java.time performs better validation by default. When your string says that April 12 is a Monday, java.time checks this and would have objected if it had not been the case.

Writing months in dates in Ukrainian (for other readers)

I knew nothing about writing dates in Ukrainian before writing this answer. For curious readers I would like to pass on a couple of observations from my searches.

Two forms of month names seem to be used:

  1. The nominative, which Java knows as the standalone form, for example січень for January. This form often ends in -ень (-en).
  2. The genitive, which Java uses as the normal (non-standalone) form, for example січня for January. Possibly “of January” could be used as a translation? This form often ends in -ня (-nya?)

In the Internet I have seen both forms used in dates. I am not very sure, but possibly the trends are: The nominative (standalone) form is used when no day of month is present and sometimes informally when one is; the genitive is used formally when day of month is present.

Links

Estren answered 10/4, 2021 at 7:6 Comment(1)
Nice answer indeed !Elicia
H
3

java.time

The Answer by Hajaj looks correct. But the Question and that Answer both use terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310.

Trying original input given in Question.

String input = "понеділок 12 квітень 07:00 2021";
Locale locale = new Locale.Builder().setLanguage( "uk" ).setRegion( "UA" ).build();
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE dd MMMM HH:mm yyyy" ).withLocale( locale );
LocalDateTime ldt = LocalDateTime.parse( input , f );
System.out.println( "ldt = " + ldt );

See that code run live at IdeOne.com, using Java 12.

With your input I get a DateTimeParseException saying "Text 'понеділок 12 квітень 07:00 2021' could not be parsed at index 13". That means a problem with your month name.

Incorrect month name?

I know nothing about the Ukrainian language. So as an experiment, I tried the opposite, generating text rather than parsing text. I get a different variation on the month name.

Locale locale = new Locale.Builder().setLanguage( "uk" ).setRegion( "UA" ).build();
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE dd MMMM HH:mm yyyy" ).withLocale( locale );

LocalDateTime ldt = LocalDateTime.of( 2021 , Month.APRIL , 12 , 7 , 0 );
String output = ldt.format( f );
LocalDateTime ldt2 = LocalDateTime.parse( output , f );

System.out.println( "ldt.toString() = " + ldt );
System.out.println( "output = " + output );
System.out.println( "ldt2.toString() = " + ldt2 );

See this code run live at IdeOne.com.

Results:

ldt.toString() = 2021-04-12T07:00
output = понеділок 12 квітня 07:00 2021
ldt2.toString() = 2021-04-12T07:00

So the publisher of your data is using a variation of the month name not expected by the current locale definitions used by Java. The primary default set of locale definitions used by modern Java (Java 9 and later in general, and Java 16 here for me) is the Unicode Common Locale Data Repository (CLDR). The fallback definitions may be an obsolete Java-specific set that was bundled as the primary set in older versions of Java. I do not know which set of locale definitions is in play here, but I think it is a safe bet to say the CLDR has Ukrainian covered and therefore must be in play here.

As I said, I do not know Ukrainian. But I suspect your input’s month name is not correct linguistically/grammatically/spelling-wise.

Hypercorrection answered 9/4, 2021 at 22:46 Comment(1)
It so turns out that квітень is the standalone form of the Ukrainian name for April (according to Java (and CLDR)). I found the usage of the standalone form in dates confirmed in a couple of places in the Internet. I was surprised. I thought I had understood what standalone form means, but apparently not. In any case it seems to be correct.Estren
E
2

You can check the Javadoc for DateFormatSymbols#weekdays, the element at index 0 is always ignored unfortunately.

I'd simply replace it by an empty string.

Weekday strings. For example: "Sunday", "Monday", etc. An array of 8 strings, indexed by Calendar.SUNDAY, Calendar.MONDAY, etc. The element weekdays[0] is ignored.


The following code now prints the expected answer

DateFormatSymbols myDateFormatSymbols = new DateFormatSymbols() {
    @Override
    public String[] getWeekdays() {
        return new String[]{"", "понеділок", "вівторок", "середа", "четвер", "пятниця", "субота", "неділя"};
    }

    @Override
    public String[] getMonths() {
        return new String[]{"квітень"};
    }
};

SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM HH:mm yyyy", myDateFormatSymbols);
System.out.println(sdf.parse("понеділок 12 квітень 07:00 2021")); // Tue Jan 12 07:00:00 CET 2021
Elicia answered 9/4, 2021 at 22:22 Comment(2)
Thank you so much for the answer.So, the proper solution would be to add "" at the beginning of the array?Tragopan
Yes indeed @Tragopan I've added how the code should look like nowElicia

© 2022 - 2024 — McMap. All rights reserved.