Jsf 如何使用Joda time通过EclipseLink将UTC区域中的日期/时间插入MySQL?

Jsf 如何使用Joda time通过EclipseLink将UTC区域中的日期/时间插入MySQL?,jsf,timezone,eclipselink,converter,jodatime,Jsf,Timezone,Eclipselink,Converter,Jodatime,我需要将UTC区域中的日期/时间存储到MySQL数据库(DATETIME类型的列)。当用户输入日期时,它首先由JSF转换器转换为org.joda.time.DateTime package joda.converter; import java.util.Date; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.converters.Convert

我需要将UTC区域中的日期/时间存储到MySQL数据库(DATETIME类型的列)。当用户输入日期时,它首先由JSF转换器转换为
org.joda.time.DateTime

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);
    }
}
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;
    }
}
在将此日期插入MySQL数据库之前,需要再次将其转换为
java.util.date
——这要感谢EclipseLink

下面是一个转换器,它再次将
org.joda.time.DateTime
转换为
java.util.Date
,尽管实际上并不需要查看此转换器

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);
    }
}
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;
    }
}
convertObjectValueToDataValue()
方法(第一个)中,接收到的第一个参数-
objectValue
的值是由JSF转换器中的Joda time转换的正确UTC日期/时间

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);
    }
}
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;
    }
}
例如,如果我输入日期-
02-Oct-2013 11:34:26 AM
,则
objectValue
的值将为-
2013-10-02T06:04:26.000Z
。应将此日期/时间插入数据库

但是,当此值通过以下表达式转换时-
(DateTime)objectValue).withZone(DateTimeZone.UTC).toDate()
,它将再次计算为
2013-10-02 11:34:26.0
,并且此值提供给数据库,但该值不正确

无论如何,如何将UTC区域设置为
(DateTime)objectValue).withZone(DateTimeZone.UTC).toDate()


在模型类中指定
org.joda.time.DateTime
类型的属性,如下所示

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

编辑:(下面的JSF转换器与上面的EclipseLink转换器一样工作,它保持不变-从BalusC的唯一版本到现在)

这是我的JSF转换器

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);
    }
}
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;
    }
}

您的具体问题是由于在转换到
java.util.Date
期间没有使用时区而造成的。它基本上返回
newdate(millis)
,其中
millis
DateTime
实例的内部存储的历元时间,与

换句话说,
withZone(DateTimeZone.UTC)
部分在这里完全没有效果。代码的行为与缺少该部分时完全相同。这就解释了为什么最终会得到最初输入的时间

从技术上讲,问题在于自定义JSF转换器,它将
String
转换为
DateTime
。该转换器显然没有考虑时区,并假设输入已经在GMT时区(默认情况下)。必须指示转换器输入处于IST时区。如果您在标准JSF中使用标准的
java.util.Date
属性,那么您可以通过将其
时区属性设置为
IST
来解决它


您的定制JSF转换器应该在幕后做完全相同的事情:告诉API提供的
字符串
在IST时区,而不是让它假设它已经在GMT时区。您没有在任何地方显示自定义JSF转换器,因此很难提供准确的答案,但可以归结为以下启动示例:

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);

生成的
DateTime
实例最终将拥有正确的内部纪元时间(以毫秒为单位)。

UTF?也许UTC,时间戳通常已经是UTC格式,所以问题只是在从毫秒到字符串的转换过程中,对于此检查,还应检查正确的数据库时区设置。这是UTC-sleepy键入。我无法返回
日历
对象。当数据提供给数据库时,它应该返回
java.sql.Timestamp
。这就是JPA映射的工作方式。您在哪里看到该数据库包含无效值?当我通过PrimeFaces日历输入类似于
02-Oct-2013 11:34:26 AM
的日期时,插入数据库的日期/时间与从日历中选择的日期/时间相同-
2013-10-02 11:34:26
。转换为UTC后应为
2013-10-02T06:04:26.000Z
。不是吗?我错了吗?我有JSF转换器,但不幸的是,这并没有什么不同。感谢您将问题本身包括在JSF转换器中。那么您可能没有运行新的转换器,或者您误解了结果,或者问题已经转移到了目前为止提供的信息中看不到的其他地方。新的转换器应该特别修复“但是当这个值被这个表达式-(DateTime)objectValue).withZone(DateTimeZone.UTC).toDate()转换时,它再次被计算为2013-10-02 11:34:26.0”。这两个转换器都完成了它们的工作。这一行-
DateTimeFormat.forPattern(“dd-MMM-yyy-hh:mm:ss-aa”).withZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone(“IST”)).parseDateTime(“02-Oct-2013 11:34:26 AM”)
返回
2013-10-02T11:34:26.000+05:30
。这看起来不可靠。JSF转换器中的这两行代码
DateTimeFormatter DateTimeFormatter=DateTimeFormat.forPattern(“dd-MMM-yyy-hh:mm:ss-aa”)
dateTimeFormatter.parseDateTime(值).withZone(DateTimeZone.UTC)
返回
02-Oct-2013 06:04:26 AM
,但发送到MySQL时没有任何区别。我看不出其他地方有什么不对劲的地方,“不可靠”的结果才是正确的结果。它说,输入的日期是11:34:26,偏移量与IST时区相匹配(因此不是GMT!)。那么,问题显然出在JPA/JDBC方面。