Java MessageFormat and LocalDate

Viewed 898

I'm using

MessageFormat.format("Hello {0}", "World"));

Now I want to use LocalDate or LocalDateTime as parameters but as far as I can see MessageFormat.format doesn't support java.time!

So I have to use

MessageFormat.format("Today is {0,date}", 
              Date.from(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));

This is terrible!

Is there a better way to use MessageFormat with java.time? Or are there better solutions to replace placeholders in a text that considers Locale configuration?

Update

I'm aware of how to format LocalDate and LocalDateTime but I have the requirement to format a message with various types.

Example

MessageFormat.format("Today is {0,date} {1,number} {2}", aDate, aNumber, aString);

Where is the replacement for MessageFormat with java.time Types?

3 Answers

There had been an issue opened for this, which was resolved as "won't fix". The reason is that:

The MessageFormat is designed to work with java.text.Format classes, so it uses DateFormat/SimpleDateFormat to format date/time. Providing support for java.time.format.DateTimeFormatter to format java.time types (TemporalAccessors) may complicate the MessageFormat API. It is always recommended to use java.util.Formatter which provides support for formatting java.time types.

So you should use Formatter instead:

StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb, Locale.US);
int someNumber = 10;
String someString = "Hello";
formatter.format("Today is %tD and someNumber is %d %s", LocalDate.now(), someNumber, someString);
System.out.println(sb);
// prints "Today is 03/30/21 and someNumber is 10 Hello"

This works with any kind of TemporalAccessor.

MessageFormat allows to create and set up a custom format for any argument.

val randomDate = LocalDate.EPOCH.plusDays(random.nextInt(20_000));
val fmt = new MessageFormat("Today is {0}");
fmt.setFormat(0, new SummaryDateFormat());
val out = fmt.format(new Object[]{randomDate});
System.out.println(out);

Here is a sample implementation of your custom format:

 public class SummaryDateFormat extends Format {
    @Override
    public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
        toAppendTo.append(String.format("%tA, %<tB %<te", obj));
        return toAppendTo;
    }

    @Override
    public Object parseObject(final String source, final ParsePosition pos) {
        return null;
    }
}
Related