2

I am trying to create a form, that will send back an object with a timestamp. Right now, the input format must be yyyy-MM-dd HH:mm:ss, I want the timestamp to be entered in the format dd.MM.yyyy HH:mm - how can I change the input format?

The object class:

public class Test {
    private Timestamp dateStart;

    public Timestamp getDateStart() {
        return dateStart;
    }
    public void setDateStart(Timestamp dateStart) {
        this.dateStart = new Timestamp(dateStart.getTime());
    }
}

The controller method:

@RequestMapping(value="test", method = RequestMethod.POST)
public View newTest(@ModelAttribute("test") Test test, Model model) {
    //save the Test object
}

The jsp form:

<form:form action="service/test" method="post" modelAttribute="test">
    <form:input type="text" path="dateStart" />
</form:form>

I get this error, when the format isn't right:

Field error in object 'test' on field 'dateStart': rejected value [22.05.2012 14:00]; codes [typeMismatch.test.dateStart,typeMismatch.dateStart,typeMismatch.java.sql.Timestamp,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [test.dateStart,dateStart]; arguments []; default message [dateStart]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.sql.Timestamp' for property 'dateStart'; nested exception is org.springframework.core.convert.ConversionFailedException: Unable to convert value "22.05.2012 14:00" from type 'java.lang.String' to type 'java.sql.Timestamp'; nested exception is java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]]
lmazgon
  • 1,066
  • 1
  • 19
  • 35

2 Answers2

10

Thanks to Tomasz I got the answer, I have to add a binder method to the controller:

@InitBinder
public void binder(WebDataBinder binder) {binder.registerCustomEditor(Timestamp.class,
    new PropertyEditorSupport() {
        public void setAsText(String value) {
            try {
                Date parsedDate = new SimpleDateFormat("dd.MM.yyyy HH:mm").parse(value);
                setValue(new Timestamp(parsedDate.getTime()));
            } catch (ParseException e) {
                setValue(null);
            }
        }
    });
}
lmazgon
  • 1,066
  • 1
  • 19
  • 35
0

FYI, here is the code for a complete Timestamp custom editor (it supports getAsText() too), courtesy of http://adfinmunich.blogspot.com/2011/04/how-to-write-sqltimestamppropertyeditor.html, just change DEFAULT_BATCH_PATTERN to match your desired date/timestamp pattern, OR send the desired pattern when you construct the SqlTimestampPropertyEditor:

package org.springframework.beans.custompropertyeditors;

import java.beans.PropertyEditorSupport;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * Property editor for java.sql.Timestamp, supporting SimpleDateFormat.
 * 
 * Using default Constructor uses the pattern yyyy-MM-dd
 * Using the constructor with String, you can use your own pattern.
 * 
 */
public class SqlTimestampPropertyEditor extends PropertyEditorSupport {

public static final String DEFAULT_BATCH_PATTERN = "yyyy-MM-dd";

private final SimpleDateFormat sdf;

/**
 * uses default pattern yyyy-MM-dd for date parsing.
 */
public SqlTimestampPropertyEditor() {
    this.sdf = new SimpleDateFormat(SqlTimestampPropertyEditor.DEFAULT_BATCH_PATTERN);
}

/**
 * Uses the given pattern for dateparsing, see {@link SimpleDateFormat} for allowed patterns.
 * 
 * @param pattern
 *            the pattern describing the date and time format
 * @see SimpleDateFormat#SimpleDateFormat(String)
 */
public SqlTimestampPropertyEditor(String pattern) {
    this.sdf = new SimpleDateFormat(pattern);
}

/**
 * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
 */
@Override
public void setAsText(String text) throws IllegalArgumentException {

    try {
        setValue(new Timestamp(this.sdf.parse(text).getTime()));
    } catch (ParseException ex) {
        throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
    }
}

/**
 * Format the Timestamp as String, using the specified DateFormat.
 */
@Override
public String getAsText() {
    Timestamp value = (Timestamp) getValue();
    return (value != null ? this.sdf.format(value) : "");
}
}

To use this class, you would define the following @InitBinder:

@InitBinder
public void binder(WebDataBinder binder) {binder.registerCustomEditor(Timestamp.class,
    new org.springframework.beans.custompropertyeditors.SqlTimestampPropertyEditor();}

If you want to use a non-default date/timestamp pattern, supply it in the constructor to the SqlTimestampPropertyEditor, so for this particular example you could use:

@InitBinder
public void binder(WebDataBinder binder) {binder.registerCustomEditor(Timestamp.class,
    new org.springframework.beans.custompropertyeditors.SqlTimestampPropertyEditor("dd.MM.yyyy HH:mm");}
pattmatt
  • 801
  • 6
  • 3
  • I would not recommend doing it like this, because you use the same instance of SimpleDateFormat for parsing, which is not thread safe (http://stackoverflow.com/questions/6840803/simpledateformat-thread-safety). Sure the structure of the code is better organized, but it is unsafe, unless you create a new SimpleDateFormat for every request. – lmazgon Aug 19 '14 at 16:36