In my Grails app, the original date read from the database is equal to:
{ endDate=2015-10-19 19:00:00.0}
While the JSON result is:
{"endDate": "2015-10-19T16:00:00Z"}
I think this is maybe related to time zone conversion. How could I show the original date without any timezone conversions in JSON?
Depending on which time zone you're in, 2015-10-19 19:00:00.0
and 2015-10-19T16:00:00Z
may not be different times, they may be just different representations of the same time (instant).
In my case, I use a custom marshaller to ensure that times in my API's JSON response always use the UTC time zone. My custom marshaller looks like this:
import org.springframework.stereotype.Component
@Component
class DateMarshaller implements CustomMarshaller {
@Override
def getSupportedTypes() {
Date
}
@Override
Closure getMarshaller() {
{ Date date ->
TimeZone tz = TimeZone.getTimeZone('UTC')
date?.format("yyyy-MM-dd'T'HH:mm:ss'Z'", tz)
}
}
}
Remember to register the package this marshaller is in for Spring bean scanning in Config.groovy
. The interface it implements is:
interface CustomMarshaller {
/**
* Indicates the type(s) of object that this marshaller supports
* @return a {@link Class} or collection of {@link Class}
* if the marshaller supports multiple types
*/
def getSupportedTypes()
Closure getMarshaller()
}
Then I have a service that registers all my instances of CustomMarshaller
for the relevant type(s):
import grails.converters.JSON
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import javax.annotation.PostConstruct
class MarshallerRegistrarService implements ApplicationContextAware {
static transactional = false
ApplicationContext applicationContext
// a combination of eager bean initialization and @PostConstruct ensures that the marshallers are registered when
// the app (or a test thereof) starts
boolean lazyInit = false
@PostConstruct
void registerMarshallers() {
Map<String, CustomMarshaller> marshallerBeans = applicationContext.getBeansOfType(CustomMarshaller)
marshallerBeans.values().each { CustomMarshaller customMarshaller ->
customMarshaller.supportedTypes.each { Class supportedType ->
JSON.registerObjectMarshaller supportedType, customMarshaller.marshaller
}
}
}
}
This is a fairly involved solution, but In my case I'm using Grails 2.5.X. If I was using Grails 3.X I'd try to use JSON views instead.