0

A user picks a date and a time on a page in my web app (Grails, if that matters). I know the user's time zone (they set it in user preferences). I now want to save that Date to the database as UTC (using Hibernate, if that matters).

So, here is my domain class:

class Even {
    Date startTime
}

I'm kinda thinking that I want to do this:

def beforeInsert() {
    startTime = new DateTime(startTime, user.timeZone).withZone(TimeDateZone.UTC).toDate()
}

But the toDate() at the end just returns the same startTime with which I started with (because Date has no time zone information I think).

So, how do I get the Date the user has provided (taking into consideration their time zone) and persist it to the database as UTC?

I was thinking about doing something like new Date() - user.timeZone.rawOffset but then I have to handle daylight time savings and it just feels very error prone.

zoran119
  • 10,657
  • 12
  • 46
  • 88
  • have a look at this: http://stackoverflow.com/questions/11294307/convert-java-date-to-utc-string – MihaiC Nov 14 '14 at 09:55
  • http://stackoverflow.com/questions/12208305/grails-saves-datetime-as-utc-time-but-reads-it-as-local-server-time – MKB Nov 14 '14 at 11:12

1 Answers1

0

There might be a quicker solution, but the standard way of doing this is with a custom Hibernate UserType. Here's an example implementation that handles the Hibernate 3.x parts (the interface is slightly different in 4.x but the code will be nearly identical) and leaves the conversion of string -> date and date -> string as an exercise for the reader:

package my.package

import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.sql.Types

import org.hibernate.usertype.UserType

class UtcDateUserType implements UserType {

    def nullSafeGet(ResultSet rs, String[] names, owner) throws SQLException {
        String value = rs.getString(names[0])
        if (value) {
            return utcStringToDate(value)
        }
    }

    void nullSafeSet(PreparedStatement st, value, int index) throws SQLException {
        if (value) {
            st.setString index, dateToUtcString(value)
        }
        else {
            st.setNull index, Types.VARCHAR
        }
    }

    protected Date utcStringToDate(String s) {
        // TODO
    }

    protected String dateToUtcString(Date d) {
        // TODO
    }

    def assemble(Serializable cached, owner) { cached.toString() }
    def deepCopy(value) { value.toString() }
    Serializable disassemble(value) { value.toString() }
    boolean equals(x, y) { x == y }
    int hashCode(x) { x.hashCode() }
    boolean isMutable() { false }
    def replace(original, target, owner) { original }
    Class<String> returnedClass() { String }
    int[] sqlTypes() { [Types.VARCHAR] as int[] }
}

Register it in the domain class in the mapping block, e.g.

import my.package.UtcDateUserType

....

static mapping = {
   startTime type: UtcDateUserType
}

This is described in the docs here.

Burt Beckwith
  • 75,342
  • 5
  • 143
  • 156