Search

Dark theme | Light theme

January 11, 2012

Grails Goodness: Date Request Parameter Value Conversions

Grails has great support for type conversion on request parameters. And since Grails 2.0 the support has been extended to include dates. In our controller we can use the date() method on the params object to get a date value. The value of a request parameter is a String, so the String value is parsed to a Date object.

The default expected date format is yyyy-MM-dd HH:mm:ss.S. If we don't specify a specific date format in the date() method then this format is used. Or we can add a format to our messages.properties with the key date.<param-name>.format. Grails will first try the default format, but if the request parameter cannot be parsed to a valid Date object then Grails will do a lookup of the date format in messages.properties. Technically Grails uses the MessageSource bean to get the format, so we even can define the format per language or country.

Alternatively we can pass a date format or multiple date formats to the date() method. Grails will use these date formats to parse the request parameter into a valid Date object.

Let's show the different options we have in a simple sample controller:

// File: grails-app/controllers/param/date/SampleController.groovy
package param.date

class SampleController {

    final def dateFormats = ['yyyy-MM-dd', 'yyyyMMdd']

    def index() {
        [
                defaultFormatDate: defaultFormatDate,
                defaultFormatNameDate: defaultFormatNameDate,
                singleFormatDate: singleFormatDate,
                multipleFormatsDate1: multipleFormatsDate1,
                multipleFormatsDate2: multipleFormatsDate2
        ]
    }

    private Date getDefaultFormatDate() {
        // Use default format yyyy-MM-dd HH:mm:ss.S
        params.date 'defaultFormatDate'
    }

    private Date getDefaultFormatNameDate() {
        // Lookup format with key date.defaultFormatNameDate.format
        // in messages.properties: yyyy-MM-dd
        params.date 'defaultFormatNameDate'
    }

    private Date getSingleFormatDate() {
        params.date 'singleFormatDate', 'yyyyMMdd'
    }

    private Date getMultipleFormatsDate1() {
        params.date 'multipleFormatsDate1', dateFormats
    }

    private Date getMultipleFormatsDate2() {
        params.date 'multipleFormatsDate2', dateFormats
    }

}

In messages.properties we define the format for the request parameter defaultFormatNameDate:

# File: grails-app/i18n/messages.properties
...
date.defaultFormatNameDate.format=yyyy-MM-dd
...

To show that the date parsing works we write a little integration test. We need this to be an integration test, because then the lookup of the key via the MessageSource bean works.

package param.date

import org.junit.Test

class SampleControllerTests extends GroovyTestCase {

    @Test
    void testDateParameters() {
        def controller = new SampleController()

        // Set request parameters.
        def params = [
                defaultFormatDate: inputDateTime.format('yyyy-MM-dd HH:mm:ss.S'),
                defaultFormatNameDate: inputDateTime.format('yyyy-MM-dd'),
                singleFormatDate: inputDateTime.format('yyyyMMdd'),
                multipleFormatsDate1: inputDateTime.format('yyyy-MM-dd'),
                multipleFormatsDate2: inputDateTime.format('yyyyMMdd')
        ]
        controller.request.parameters = params

        def model = controller.index()

        assertDates inputDateTime, model.defaultFormatDate
        assertDates inputDate, model.defaultFormatNameDate
        assertDates inputDate, model.singleFormatDate
        assertDates inputDate, model.multipleFormatsDate1
        assertDates inputDate, model.multipleFormatsDate2
    }

    private void assertDates(final Date expected, final Date controllerDate) {
        assertEquals expected.toGMTString(), controllerDate.toGMTString()
    }

    /**
     * Create Date object for January 10, 2012 14:12:01.120
     */
    private Date getInputDateTime() {
        final Calendar cal = Calendar.instance
        cal.updated(year: 2012, month: Calendar.JANUARY, date: 10, 
                    hours: 14, minutes: 12, seconds: 1, milliSeconds: 120)
        cal.time
    }

    private Date getInputDate() {
        final Date inputDateTime = inputDateTime
        inputDateTime.clearTime()
        inputDateTime
    }
}