6

I need to store date/time in UTC zone into MySQL database (of a DATETIME type column). When a user enters a date, it is first converted to org.joda.time.DateTime by a JSF converter.

Before inserting this date into MySQL database, it again needs to be converted to java.util.Date - thanks to EclipseLink.

The following is the converter that again converters org.joda.time.DateTime to java.util.Date though it is not really needed to see this converter.

package joda.converter;

import java.util.Date;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

public final class JodaDateTimeConverter implements Converter
{
    private static final long serialVersionUID = 1L;

    @Override
    public Object convertObjectValueToDataValue(Object objectValue, Session session)
    {
        return objectValue instanceof DateTime?((DateTime) objectValue).withZone(DateTimeZone.UTC).toDate():null;
    }

    @Override
    public Object convertDataValueToObjectValue(Object dataValue, Session session)
    {
        return dataValue instanceof Date?new DateTime((Date) dataValue):null;
    }

    @Override
    public boolean isMutable()
    {
        return true;
    }

    @Override
    public void initialize(DatabaseMapping databaseMapping, Session session)
    {
        databaseMapping.getField().setType(java.util.Date.class);
    }
}

In the convertObjectValueToDataValue() method (the first one), the value of the first parameter - objectValue received is the correct UTC date/time converted by Joda-Time in a JSF converter.

For example, if I entered a date - 02-Oct-2013 11:34:26 AM then, the value of objectValue would be - 2013-10-02T06:04:26.000Z. This date/time should be inserted into the database.

But when this value is converted by this expression - (DateTime) objectValue).withZone(DateTimeZone.UTC).toDate(), it is again evaluated to 2013-10-02 11:34:26.0 and this value is supplied to the database which is incorrect.

Anyway, how to set the UTC zone to (DateTime) objectValue).withZone(DateTimeZone.UTC).toDate()?


A property of type org.joda.time.DateTime is designated in a model class as follows.

@Column(name = "discount_start_date", columnDefinition = "DATETIME")
@Converter(name = "dateTimeConverter", converterClass = JodaDateTimeConverter.class)
@Convert("dateTimeConverter")
private DateTime discountStartDate;

EDIT: (The following JSF converter works as expected along with the EclipseLink converter above which remains intact - from the only answer until now by BalusC)

This is my JSF converter.

package converter;

import java.util.TimeZone;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import util.Utility;

@ManagedBean
@RequestScoped
public final class DateTimeConverter implements Converter
{
    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value)
    {
        DateTime dateTime=null;

        try
        {
            dateTime = DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa").withZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone("IST"))).parseDateTime(value);
        }
        catch (IllegalArgumentException e)
        {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", Utility.getMessage("datetime.converter.error", DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa").print(DateTime.now().withZone(DateTimeZone.forID("Asia/Kolkata"))))), e);
        }
        catch(UnsupportedOperationException e)
        {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", Utility.getMessage("datetime.converter.error", DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa").print(DateTime.now().withZone(DateTimeZone.forID("Asia/Kolkata"))))), e);
        }
        return dateTime;
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value)
    {
        DateTimeFormatter dateTimeFormatter=DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa").withZone(DateTimeZone.forID("Asia/Kolkata")); //This zone will be tackled/handled later from the database to display.              
        return value instanceof DateTime?dateTimeFormatter.print((DateTime)value):null;
    }
}
Community
  • 1
  • 1
Tiny
  • 27,221
  • 105
  • 339
  • 599
  • UTF? maybe UTC, timestamp is usually in UTC already, so the problem is only during conversion from millis to string, for this check http://stackoverflow.com/questions/7670355/convert-date-time-for-given-timezone-java, also, check proper database timezone setting – Iłya Bursov Oct 14 '13 at 23:58
  • Apology it is UTC - sleepy typing. – Tiny Oct 15 '13 at 00:00
  • I cannot return a `Calendar` object. It should return `java.sql.Timestamp`, when data is supplied to the database. This is how JPA mappings work. – Tiny Oct 15 '13 at 00:14
  • Where do you see that database holds invalid values? – Iłya Bursov Oct 15 '13 at 00:15
  • When I enter a date like `02-Oct-2013 11:34:26 AM` through a PrimeFaces calendar, the date/time inserted into the database is the same as the one chosen from the calendar - `2013-10-02 11:34:26`. It should be `2013-10-02T06:04:26.000Z` after conversion to UTC. Shouldn't it? Am I wrong? – Tiny Oct 15 '13 at 00:21
  • so, either you calendar doesn't convert properly during string to millis convertion, or your mysql interface displays dates with correction – Iłya Bursov Oct 15 '13 at 00:22
  • MySQL Workbench displays the correct date as they are inserted into. In another project with Hibernate as a JPA provider, this date - `02-Oct-2013 11:34:26 AM` is converted to `2013-10-02T06:04:26.000Z` and it is inserted into the database (`2013-10-02 06:04:26`) and not `2013-10-02 11:34:26` as it happens in this case. – Tiny Oct 15 '13 at 03:06
  • @BalusC : It is JPA with EclipseLink as a provider. The date/time entered by users is first converted to Joda-Time from String by a JSF converter and then it is converted to `Timestamp` before being sent to the database. I have now changed the [EclipseLink converter](http://pastebin.com/kFUFNV4d) to have `java.util.Date` but I always see the same date/time in the database table which is chosen from a PrimeFaces calendar. The date/time should be stored according to the UTC zone that doesn't happen. – Tiny Oct 15 '13 at 16:36
  • @BalusC : Sorry, but your answer previously posted worked correctly without any modifications. Please undelete it. It was my mistake in my test cases. I really apologize to my mistakes, sorry. – Tiny Oct 25 '13 at 15:00

1 Answers1

4

Your concrete problem is caused because DateTime#toDate() doesn't use the time zone during the conversion to java.util.Date. It basically returns new Date(millis) wherein the millis is the internally stored epoch time of DateTime instance, exactly as described by DateTime javadoc and required by the java.util.Date constructor.

In other words, the withZone(DateTimeZone.UTC) part has totally no effect here. The code behaves exactly the same as if that part was absent. That explains why you end up with the originally entered time.

Technically, the problem is in your custom JSF converter which converts from String to DateTime. That converter is apparently not taking the time zone into account and assuming that the input is already in GMT timezone (as by default). The converter must be instructed that the input is in IST timezone. If you were using standard java.util.Date property with standard JSF <f:convertDateTime>, then you could have solved it by setting its timeZone attribute to IST.

<h:inputText value="#{bean.date}">
    <f:convertDateTime pattern="dd-MMM-yyyy hh:mm:ss a" locale="en" timeZone="IST" />
</h:inputText>

Your custom JSF converter should under the covers be doing exactly the same: tell the API that the supplied String is in IST timezone instead of letting it assume that it's already in GMT timezone. You didn't show the custom JSF converter anywhere, so it's hard to supply the exact answer, but it should boil down to the following kickoff example:

String inputDateString = "02-Oct-2013 11:34:26 AM";
String inputDatePattern = "dd-MMM-yyyy hh:mm:ss a";
TimeZone inputTimeZone = TimeZone.getTimeZone("IST");

DateTime dateTime = DateTimeFormat
    .forPattern(inputDatePattern)
    .withZone(DateTimeZone.forTimeZone(inputTimeZone))
    .parseDateTime(inputDateString);

The resulting DateTime instance will end up having the right internal epoch time in millis.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I have [this](http://pastebin.com/xjJtnh8m) JSF converter but unfortunately, this didn't make a difference. Thanks – Tiny Oct 24 '13 at 10:19
  • Edited the question itself to include the JSF converter. – Tiny Oct 24 '13 at 10:22
  • Then you're either not running the new converter or you're misinterpreting the results or the problem has moved elsewhere not visible in the information provided so far. The new converter should fix particularly the part *"But when this value is converted by this expression - (DateTime) objectValue).withZone(DateTimeZone.UTC).toDate(), it is again evaluated to 2013-10-02 11:34:26.0"*. – BalusC Oct 24 '13 at 10:42
  • 1
    Both converters do their work. This line - `DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa").withZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone("IST"))).parseDateTime("02-Oct-2013 11:34:26 AM")` returns `2013-10-02T11:34:26.000+05:30`. This looks wonky. These two lines in JSF convertter `DateTimeFormatter dateTimeFormatter=DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa");` `dateTimeFormatter.parseDateTime(value).withZone(DateTimeZone.UTC);` returns `02-Oct-2013 06:04:26 AM` but that doesn't make any difference when it is sent to MySQL. I don't see anything wrong somewhere else. – Tiny Oct 24 '13 at 19:26
  • The "wonky" result is the right result. It says that the date is entered as 11:34:26 with an offset which matches the IST timezone (and thus not GMT!). Well, then the problem is apparently in JPA/JDBC side. – BalusC Oct 24 '13 at 19:30