Skip to content

Commit

Permalink
feat(databinding): Add a New ValueConverter for LocalDateTime (#13351)
Browse files Browse the repository at this point in the history
* add default data binding support for local date time

* local date time class

* Rename class FormattedLocalDateValueConverter

* feat(databinding): Add a New ValueConverter for LocalDateTime

Adds a new converter named `LocalDateTimeConverter` which will automatically convert `String` or `LocalDateTime` values when using data binder.

Fixes #13280

* update test

---------

Co-authored-by: Emma Richardson <49138186+Emrichardsone@users.noreply.github.com>
Co-authored-by: Puneet Behl <behlp@unityfoundation.io>
  • Loading branch information
3 people committed Mar 27, 2024
1 parent 1d2ad9c commit 67e2e04
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.databinding.converters

import grails.databinding.converters.FormattedValueConverter;
import groovy.transform.CompileStatic

import java.time.LocalDateTime

/**
* @author Emma Richardson
* @since 6.1
*/
@CompileStatic
class FormattedLocalDateTimeValueConverter implements FormattedValueConverter {

def convert(value, String format) {
if(value instanceof LocalDateTime) {
return value
}
else if(value instanceof CharSequence) {
String dateStr = value.toString()
if(!dateStr) {
return null
}
else {
LocalDateTime.parse((String) value)
}
}
}

@Override
Class<?> getTargetType() {
return LocalDateTime
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.databinding.converters

import grails.databinding.converters.ValueConverter
import groovy.transform.CompileStatic

import java.time.LocalDateTime

/**
* This class is a {@link ValueConverter} for {@link LocalDateTime} target type.
*
* @author Emma Richardson
* @since 6.2.0
*/
@CompileStatic
class LocalDateTimeConverter implements ValueConverter {
@Override
boolean canConvert(Object value) {
return value instanceof String || value instanceof LocalDateTime
}

@Override
Object convert(Object value) {
if (value instanceof LocalDateTime) {
return value
} else if (value instanceof CharSequence) {
String dateStr = value.toString()
if (!dateStr) {
return null
} else {
LocalDateTime.parse((String) value)
}
}
}

@Override
Class<?> getTargetType() {
return LocalDateTime
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import grails.databinding.converters.ValueConverter
import grails.databinding.errors.BindingError
import grails.databinding.events.DataBindingListenerAdapter
import org.grails.databinding.converters.DateConversionHelper

import org.grails.databinding.converters.LocalDateTimeConverter
import spock.lang.Ignore
import spock.lang.Issue
import spock.lang.Specification

import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

class SimpleDataBinderSpec extends Specification {

Expand Down Expand Up @@ -131,15 +133,24 @@ class SimpleDataBinderSpec extends Specification {
def obj = new DateContainer()
def nowUtilDate = new Date()
def nowSqlDate = new java.sql.Date(nowUtilDate.time)
def localDateTime = "2013-04-15T21:26:31.973"
def nowCalendar = Calendar.instance

when:
binder.bind(obj, new SimpleMapDataBindingSource([utilDate: nowUtilDate, sqlDate: nowSqlDate, calendar: nowCalendar]))
binder.bind(obj, new SimpleMapDataBindingSource([utilDate: nowUtilDate, sqlDate: nowSqlDate, calendar: nowCalendar, localDateTime: localDateTime]))

then:
obj.utilDate == nowUtilDate
obj.sqlDate == nowSqlDate
obj.calendar == nowCalendar
obj.localDateTime == null

when:
binder.registerConverter(new LocalDateTimeConverter())
binder.bind(obj, new SimpleMapDataBindingSource([localDateTime: "2013-04-15T21:26:31.974"]))

then:
obj.localDateTime == LocalDateTime.parse("2013-04-15T21:26:31.974", DateTimeFormatter.ISO_LOCAL_DATE_TIME)
}

@Issue('GRAILS-10925')
Expand Down Expand Up @@ -711,6 +722,7 @@ class Fidget {
}

class DateContainer {
LocalDateTime localDateTime
Date utilDate
java.sql.Date sqlDate
Calendar calendar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ protected DateConversionHelper defaultDateConverter() {
return converter;
}

@Bean("defaultLocalDateTimeConverter")
protected LocalDateTimeConverter defaultLocalDateTimeConverter() {
return new LocalDateTimeConverter();
}

@Bean("timeZoneConverter")
protected TimeZoneConverter defaultTimeZoneConverter() {
return new TimeZoneConverter();
Expand Down

0 comments on commit 67e2e04

Please sign in to comment.