0

I have a piece of code like this on my java side:

private static DateFormat getHourFormatter(){

        //DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(_locale);
        Locale locale = Locale.FRENCH; //locale : "fr"
        DateFormat hourFormatter = new SimpleDateFormat( "hh:mm a",locale); //hourFormatter: simpleDateFormat@103068 locale: "fr"
        hourFormatter.setTimeZone( TimeZone.getTimeZone("GMT") );
        return hourFormatter; //hourFormatter: SimpleDateFormat@103068
    }



protected static boolean isHoursTimeStringValid( String hourDisplay ) {
         try {
            getHourFormatter().parse( hourDisplay ); //hourDisplay: "01:01 Matin"
            return true;
         } catch (ParseException e) { //e: "java.text.ParseException: Upparseable date "01:01 Matin"
            return false; 
         }
    }

It is working fine for English locale if I change the locale value to US.

But for French locale it throwing parsing error.

java.text.ParseException: Upparseable date "01:01 Matin"

I have added the debug info as commented line for better understanding

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
SubhenduGN
  • 21
  • 2
  • 14
  • Can you post the exception as well? – Bgvv1983 Nov 24 '17 at 07:05
  • java.text.ParseException: Upparseable date "01:01 Matin" -- this is the exception – SubhenduGN Nov 24 '17 at 07:08
  • whats the value of String hourDisplay – Bgvv1983 Nov 24 '17 at 07:23
  • hourDisplay: "01:01 Matin – SubhenduGN Nov 24 '17 at 07:27
  • 1
    Any reason why you are still using the long outmoded `SimpleDateFormat` class? [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/), is much nicer to work with. Look for classes `DateTimeFormatter` and `LocalTime`. – Ole V.V. Nov 24 '17 at 07:33
  • Even with `Locale.FRENCH`, your `SimpleDateFormat` still expects (the English/Latin) AM or PM (not the French Matin as one might have expected). – Ole V.V. Nov 24 '17 at 07:38
  • Hi Ole, This is old class used by other application. As of now cant afford to change it – SubhenduGN Nov 24 '17 at 07:41
  • As you can see I am passing the locale-specific AM/PM value from JSP. Why am I doing this? The reason is I want to render the default selected date on UI. In that case, the value attribute should match the actual value of the option value. then only during rendering it will come as "selected=selected" – SubhenduGN Nov 24 '17 at 07:47
  • The code you have posted, does what @OleVV said. There is nothing JSP specific in that code. As a general hint, if a `Format` refuses your input in `parse`, you can pass the expect result to `format` to see what `parse` would expect. – Holger Nov 24 '17 at 08:18
  • Is “01:01 Matin” really used in (any) French-speaking areas? Can’t recall having seen it. I would expect a 24 hour clock. It seems `DateFormat.getTimeInstance()` does the same: With French locale it generates formats like 10:01, 10:01:29, 10:01:29 CET and 10 h 01 CET. No AM/PM marker in any of them. – Ole V.V. Nov 24 '17 at 09:03
  • Facing the same problem with Arabic locale also. – SubhenduGN Nov 24 '17 at 09:18
  • 1
    Your problem is very similar to what I have described in [my post here](https://stackoverflow.com/a/46379998/2491410). – Meno Hochschild Nov 24 '17 at 09:47
  • Ole V.V is right. Most French people use 24-hour-clock, and if they use 12 hour clock then never with AM/PM. For example: 1 AM would never be associated with "Matin" (=morning) but "Nuit" (=night) in representation. – Meno Hochschild Nov 24 '17 at 10:38

3 Answers3

1

Without rewriting your existing code base, you may still introduce java.time, the modern Java date and time API, for this particular purpose. It does offer a solution for French:

    Map<Long, String> amPmText = new HashMap<>(4);
    amPmText.put(0L, "Matin");
    amPmText.put(1L, "Soir");
    DateTimeFormatter timeFormatter = new DateTimeFormatterBuilder().appendPattern("hh:mm ")
            .appendText(ChronoField.AMPM_OF_DAY, amPmText)
            .toFormatter(Locale.FRENCH);

    System.out.println(LocalTime.parse("01:01 Matin", timeFormatter));
    System.out.println(LocalTime.parse("10:59 Soir", timeFormatter));

This prints

01:01
22:59

Mixing java.time and the outdated classes

So far our code base (which is old) is a funny mix of old and new date and time API use. Frankly we’re seldom rewriting any of the old and working code, but we always use the modern API for new code.

I do warmly recommend using java.time wherever you can. It is generally so much nicer to work with. Once you have embarked on using it, I’m sure you will not want to go back.

For a pure SimpleDateFormat solution see Meno Hochschild’s comment below.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    It is possible to realize your proposal with `SimpleDateFormat`. Just create a customized version of `DateFormatSymbols` and [feed the formatter](https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html#setDateFormatSymbols-java.text.DateFormatSymbols-) with it. The new API is surely more convenient than the old one although still not optimal (for comparison: my lib Time4J can even [work with enums](http://time4j.net/javadoc-en/net/time4j/format/expert/ChronoFormatter.Builder.html#addText-net.time4j.engine.ChronoElement-java.util.Map-) instead of long-primitives). – Meno Hochschild Nov 24 '17 at 10:00
  • Even though I am using DateTimeFormatter, but still from my old method I need to return DateFormat. Is it possible to convert DateTimeFormatter to DateFormat ?? – SubhenduGN Nov 24 '17 at 10:04
  • But I also fear that just using the AM/PM-field is not sufficient. There is also "nuit" or "après-midi" requring more than two values. Using the pattern symbol "B" instead of "a" is obviously more appropriate (but only supported by ICU4J and Time4J. Here Java-8 is limited. – Meno Hochschild Nov 24 '17 at 10:04
  • @SubhenduGN I am afraid that is not possible. I have not researched thoroughly, but I think that `DateTimeFormatter` can do things that `SimpleDateFormat` cannot do, so developing a general conversion might not be trivial. – Ole V.V. Nov 24 '17 at 10:09
  • The ugly hack: you may write your own subclass of `DateFormat` embedding the `DateTimeFormatter` from my answer in it. You will need to implement two methods, `format(Date, StringBuffer, FieldPosition)` and `parse(String, ParsePosition)` and in your implementations convert between `Date` and `LocalTime`. I am not sure I would dare. – Ole V.V. Nov 24 '17 at 10:24
  • 1
    @SubhenduGN `DateTimeFormatter` can only be converted to `java.text.Format` (without attributed-character-support), see [API](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#toFormat--) – Meno Hochschild Nov 24 '17 at 10:24
1

If and only if you have just two possible values (here AM/PM) then you can do it with SimpleDateFormat this way:

DateFormatSymbols dfs = DateFormatSymbols.getInstance(Locale.FRENCH);
dfs.setAmPmStrings(new String[] { "Matin", "Soir" });
SimpleDateFormat input = new SimpleDateFormat("hh:mm a", Locale.FRENCH);
input.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
input.setDateFormatSymbols(dfs);

Date parsed = input.parse("01:01 Matin");

// control of parsing
SimpleDateFormat output = new SimpleDateFormat("HH:mm");
output.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
System.out.println(output.format(parsed)); // 01:01 (24-hour-clock)

I have here set the timezone to GMT in order to prevent any zone effects. You can deviate from it if needed (but care is necessary).

As mentioned in some comments, I still don't think that using the AM/PM-field is really appropriate for other languages than English. French for example knows at least two or more values like "nuit" (=night) or "après-midi" (=afternoon). But that way is not possible with old API or new java.time-package (would require external libs like ICU4J or Time4J).

Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
  • It may be the first time ever I have upvoted an answer using `SimpleDateFormat`, but this one is good and matches the OP’s requirements well. – Ole V.V. Nov 24 '17 at 10:20
  • @OleV.V. Thanks, I always try to take the requirements of OP serious even if this means using old APIs. But showing a more modern alternative like in your answer is also okay. – Meno Hochschild Nov 24 '17 at 10:29
0

Thank you guys for all of these answers.

As I mentioned earlier, I can't afford to change the code base.

So, what I have done is :

public void setBeginAMPM( String ampm ) {
    if(ampm.equals(new I18NStringFactory().getString("Calendar", _locale , "default.time.am" ))) {
        _beginAMPM = "AM";
    }
    else if(ampm.equals(new I18NStringFactory().getString("Calendar", _locale , "default.time.pm" ))) {
        _beginAMPM = "PM";
    }
    else{
        _beginAMPM = ampm;
    }
}


public void setEndAMPM( String ampm ) {
    if(ampm.equals(new I18NStringFactory().getString("Calendar", _locale , "default.time.am" ))) {
        _endAMPM = "AM";
    }
    else if(ampm.equals(new I18NStringFactory().getString("Calendar", _locale , "default.time.pm" ))) {
        _endAMPM = "PM";
    }
    else{
        _endAMPM = ampm;
    }
}

_locale value I am passing from Action class to From class. If it is other than English it will come into one of the if block or in case of English it will come to the else block by default. Based on the local value it is taking AM/PM value from the properties file and converting that accordingly.

I am just modifying the AM/PM value from other locale-specific languages to English, as SimpleDateFormat() only supports English.

You guys can call it an ugly hack, but guess what, It is solving my purpose.

SubhenduGN
  • 21
  • 2
  • 14