简体   繁体   中英

Using java.time.LocalDate with JSTL <fmt:formatDate> action

I haven't been able to figure out how to display a java.time.LocalDate value in a JSP.

In my JSP, I have this:

<fmt:formatDate value="${std.datum}" type="date" pattern="dd.MM.yyyy" var="stdDatum" />


The std.datum is of type java.time.LocalDate . When rendering the JSP I get this exception:

javax.el.ELException:
Cannot convert 2015-02-14 of type class java.time.LocalDate to class java.util.Date

I'm assuming it's the conversion?

So is it possible to format an instance of LocalDate class with <fmr:formatDate> action?

I'm assuming it's the conversion?

Yes, it's a conversion related exception.


Solution

You could first use the <fmt:parseDate> action from the JSTL's "I18n capable formatting tag library" to do the conversion and then do the formatting with the <fmt:formatDate> action .

Here is an example:

<fmt:parseDate  value="${std.datum}"  type="date" pattern="yyyy-MM-dd" var="parsedDate" />
<fmt:formatDate value="${parsedDate}" type="date" pattern="dd.MM.yyyy" var="stdDatum" />


This solution is also presented right in the "JavaServer Pages™ Standard Tag Library (JSTL)" specification version 1.2 (see page 109).

This is an old question, but i find it is very best to do a custom tld in this case: without any double conversion to and from String.

Do your own tld file, then override the FormatDate class. Finally, declare your own custom prefix and use custom:formatDate instead of fmt:formatDate.

here is a simplified version

usage in JSP:

<%@ taglib uri="/WEB-INF/custom" prefix="custom" %>
...
<custom:formatDate value="${std.datum}" pattern="dd/MM/yyyy" />

WEB-INF/custom.tld file

<?xml version="1.0" encoding="UTF-8"?>
<tag ib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">

<tag>
    <description>
        FormatDate with java8 type
    </description>
    <name>formatDate</name>
    <tag-class>com.custom.tag.FormatDateTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
        <description>
            Date and/or time to be formatted.
        </description>
        <name>value</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>

    <attribute>
        <description>
            Custom formatting style for dates and times.
        </description>
        <name>pattern</name>
        <required>false</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
</taglib>

Then the java class tag file

public class FormatDateTag extends TagSupport  {

    protected Temporal value;
    protected String pattern; 
    private String var; 
    private int scope; 


    public FormatDateTag()
    {
        super ();
        init ();
    }

    private void init()
    {

        this.pattern = this.var = null;
        this.value = null;
        this.scope = PageContext.PAGE_SCOPE;
    }


    public void setVar(final String var)
    {
        this.var = var;
    }

    public void setScope(final String scope)
    {
        this.scope = Util.getScope (scope);
    }


    public void setValue(final Temporal value)
    {
        this.value = value;
    }


    public void setPattern(final String pattern)
    {
        this.pattern = pattern;
    }


    @Override
    public int doEndTag() throws JspException
    {

        String formatted = null;

        if (this.value == null)
        {
            if (this.var != null)
            {
                this.pageContext.removeAttribute (this.var, this.scope);
            }
            return EVAL_PAGE;
        }

        // Create formatter
        if (this.pattern != null)
        {
            final DateTimeFormatter formatter = DateTimeFormatter.ofPattern (this.pattern);
            formatted = formatter.format (this.value);
        }
        else
        {
            // no formatting locale available, use Date.toString()
            formatted = this.value.toString ();
        }

        if (this.var != null)
        {
            this.pageContext.setAttribute (this.var, formatted, this.scope);
        }
        else
        {
            try
            {
                this.pageContext.getOut ().print (formatted);
            }
            catch (final IOException ioe)
            {
                throw new JspTagException (ioe.toString (), ioe);
            }
        }

        return EVAL_PAGE;
    }


    @Override
    public void release()
    {
        init ();
    }

}

I wanted to avoid changing all the places where <fmt:formatDate/> where used, so I created the following two classes in stead:

First, a converter to convert (some of) the java.time classes. It checks that java.util.Date is the target type, if not, it does nothing. It supports source time as java.util.Date (including java.sql.Timestamp and java.sql.Date), LocalDate, LocalDateTime, ZonedDateTime, Instant or Long (time in millis).

package com.example.elresolvers;

import javax.el.ELContext;
import javax.el.TypeConverter;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

@SuppressWarnings("UseOfObsoleteDateTimeApi")
public class DateFromJavaTimeResolver extends TypeConverter {
    @Override
    public Object convertToType(ELContext context, Object obj, Class<?> type) {
        Date date = null;
        if (Date.class.isAssignableFrom(type)) {
            if (obj instanceof Date) {
                date = (Date) obj;
            } else {
                ZonedDateTime zdt = null;
                if (obj instanceof LocalDate) {
                    zdt = ((LocalDate) obj).atStartOfDay(ZoneId.systemDefault());
                } else if (obj instanceof LocalDateTime) {
                    zdt = ((LocalDateTime) obj).atZone(ZoneId.systemDefault());
                } else if (obj instanceof ZonedDateTime) {
                    zdt = (ZonedDateTime) obj;
                } else if (obj instanceof Instant) {
                    date = Date.from((Instant) obj);
                } else if (obj instanceof Long) {
                    date = new Date((Long) obj);
                }
                if (zdt != null) {
                    date = Date.from(zdt.toInstant());
                }
            }
            context.setPropertyResolved(date != null);
        }
        return date;
    }
}

Next, a ServletContextListener class to register the converter for use with JSP:

package com.example.web;

import com.example.elresolvers.DateFromJavaTimeResolver;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.jsp.JspApplicationContext;
import javax.servlet.jsp.JspFactory;

public class JspElResolverInitListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        JspApplicationContext context = JspFactory.getDefaultFactory().getJspApplicationContext(servletContext);
        context.addELResolver(new DateFromJavaTimeResolver());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

Finally, an entry in web.xml (if you are not using annotations or other means):

<listener>
    <listener-class>com.example.web.JspElResolverInitListener</listener-class>
</listener>

Java:

public class DateTimeUtil {
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");

    public static String toString(LocalDateTime ldt) {
        return ldt.format(DATE_FORMATTER);
    }

JSP:

<%@ page import="ru.javaops.topjava.util.DateTimeUtil" %>
...
<%=DateTimeUtil.toString(std.getDatum())%>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM