Grails generally makes it very easy to bind request parameters to domain objects or command objects with properties of varying types. For most request parameters, a simple call to a controller’s bindData() method is all you need to handle property binding and type conversion. Unfortunately, the default Date binder is very rigid and only handles parameters in a single specific format. If you are using Grails built-inGSP tags or a plugin to generate your date fields, then you should be fine, since these should conform the Grails standard. However, if you want your controllers to be accessible to a wider set of clients, and seamlessly handle a variety of date formats, the built-in data binder falls short. Fortunately, Grails allows us to register our own custom property editors to handle type conversions.

The following is an example of a property editor that converts date strings according to a set of allowed formats. Once this editor is registered, a simple call to bindData() will once again handle our parameter type conversions gracefully, even allowing for varied date formats.

package util

import java.beans.PropertyEditorSupport
import java.text.ParseException
import org.apache.commons.lang.time.DateUtils
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry

class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    def dateEditor

    void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Date.class, dateEditor)
    }
}

class CustomDateEditor extends PropertyEditorSupport {
    boolean allowEmpty
    String[] formats

    /**
     * Parse the Date from the given text
     */
    void setAsText(String text) throws IllegalArgumentException {
        if (this.allowEmpty && !text) {
            // Treat empty String as null value.
            setValue(null)
        }
        else {
            try {
                setValue(DateUtils.parseDate(text, formats))
            }
            catch (ParseException ex) {
                throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex)
            }
        }
    }

    /**
     * Format the Date as String, using the first specified format
     */
    String getAsText() {
        def val = getValue()
        val?.respondsTo('format') ? val.format(formats[0]) : ''
    }
}

To take advantage of this editor, register the bean in conf/spring/resources.groovy and configure it with your own set of allowed date formats.

beans = {
  customPropertyEditorRegistrar(util.CustomPropertyEditorRegistrar) {
    dateEditor = { util.CustomDateEditor e ->
      formats = ['yy-MM-dd HH:mm', 'yy-MM-dd', 'MM/dd/yy HH:mm', 'MM/dd/yy']
      allowEmpty = true
    } 
  }
}