Simon Martinelli
Simon Martinelli

Reputation: 36203

Java MessageFormat and LocalDate

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?

Upvotes: 9

Views: 2680

Answers (4)

Arend von Reinersdorff
Arend von Reinersdorff

Reputation: 4233

Java 23 added support for java.time objects in MessageFormat. See Java 23 MessageFormat Javadoc.

Examples:

System.out.println(MessageFormat.format("LocalDateTime: {0,dtf_datetime}", LocalDateTime.now()));
// Jan 25, 2025, 9:12:48 PM
System.out.println(MessageFormat.format("LocalTime with style: {0,dtf_time,short}", LocalTime.now()));
// 9:12 PM
System.out.println(MessageFormat.format("LocalDate with custom format: {0,dtf_date,GGGG yyyy MMMM dd}", LocalDate.now()));
// Anno Domini 2025 January 25
System.out.println(MessageFormat.format("LocalDate with pre-defined formatter: {0,ISO_WEEK_DATE}", LocalDate.now()));
// 2025-W04-6

Upvotes: 1

Jegors Čemisovs
Jegors Čemisovs

Reputation: 740

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;
    }
}

Upvotes: 4

Simon Martinelli
Simon Martinelli

Reputation: 36203

I finally found a bug report about supporting java.time in MessageFormat.

https://bugs.openjdk.java.net/browse/JDK-8254654

Until this is solved I will go with the suggested workaround and convert LocalDate etc. to java.util.Date

Upvotes: 3

Sweeper
Sweeper

Reputation: 272760

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.

Upvotes: 11

Related Questions