f:convertDateTime support for Java8 LocalDate / LocalDateTime?

alfonx picture alfonx · May 22, 2015 · Viewed 13.9k times · Source

The JSF Core Tag f:convertDateTime can format java.util.Date objects. The Date class has many deprecated methods and with Java 8 come new classes to present local dates and times: LocalDateTime and LocalDate.

f:convertDateTime can not format LocalDateTime nor LocalDate.

Does anybody know, if there is an equivalent to the JSF core tag convertDateTime that can deal with LocalDateTime objects? Is support planned for a future release, or are alternative tags available?

Answer

hinneLinks picture hinneLinks · Jul 3, 2015

Just write your own Converter and extend the javax.faces.convert.DateTimeConverter - that way you can Reuse all the attributes that <f:convertDateTime> supports. Also it will take care of Localization too. Unfortunately it's a bit more complicated to write a Converter with Attributes.

Create component
First write your own Converter that extends javax.faces.convert.DateTimeConverter - just let the super-calls do all the work (including locale-stuff) and convert the result from/to LocalDate.

@FacesConverter(value = LocalDateConverter.ID)
public class LocalDateConverter extends DateTimeConverter {
    public static final String ID = "com.example.LocalDateConverter";

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) {
        Object o = super.getAsObject(facesContext, uiComponent, value);

        if (o == null) {
            return null;
        }

        if (o instanceof Date) {
            Instant instant = Instant.ofEpochMilli(((Date) o).getTime());
            return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
        } else {
            throw new IllegalArgumentException(String.format("value=%s could not be converted to a LocalDate, result super.getAsObject=%s", value, o));
        }
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) {
        if (value == null) {
            return super.getAsString(facesContext, uiComponent,value);
        }

        if (value instanceof LocalDate) {
            LocalDate lDate = (LocalDate) value;
            Instant instant = lDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            Date date = Date.from(instant);
            return super.getAsString(facesContext, uiComponent, date);
        } else {
            throw new IllegalArgumentException(String.format("value=%s is not a instanceof LocalDate", value));
        }
    }
}

Then create a file LocalDateConverter-taglib.xml in META-INF:

<facelet-taglib version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">

    <namespace>http://example.com/LocalDateConverter</namespace>
    <tag>
        <tag-name>convertLocalDate</tag-name>
        <converter>
            <converter-id>com.example.LocalDateConverter</converter-id>
        </converter>
    </tag>
</facelet-taglib>

And lastly, register the taglib in web.xml:

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/META-INF/LocalDateConverter-taglib.xml</param-value>
</context-param>

Usage
To use the new Tag in your JSF-Page add the new Taglib xmlns:ldc="http://example.com/LocalDateConverter" and use the tag:

<ldc:convertLocalDate type="both" dateStyle="full"/>