【问题标题】:Convert joda.time.DateTime to java.sql.Date and retain time zone将 joda.time.DateTime 转换为 java.sql.Date 并保留时区
【发布时间】:2012-08-13 16:07:33
【问题描述】:

我有以下场景:

  • 返回 Calendar 对象的 Swing 控件
  • 中级 DateTime 对象,我用来进行繁重的日期/时间操作 (joda)
  • 仅接受 java.sql.Date 对象的数据库连接 (OraclePreparedStatement)

我的问题是 CalendarDateTime 对象正确地以 GMT 显示日期(我想要),但是当我转换为 java.sql.Date 以便发送到数据库时,日期被转换为当地时区。

例如:

  • CalendarDateTime 是 2012-08-13T23:59:59.000Z(正确的 GMT)
  • 结果 java.sql.Date 是 2012-08-14(不正确的本地 UTC+2 日期)

下面是我用来进行转换的代码。

DateTime dateGmt = new DateTime(calendarGmt.getTimeInMillis(), DateTimeZone.UTC);
java.sql.Date sqlDate = new java.sql.Date(dateGmt.getMillis());

我不知道如何在保留正确时区的同时创建java.sql.Date 对象。我也完全有可能进行了不正确的转换。

【问题讨论】:

    标签: java date jdbc jodatime


    【解决方案1】:

    浪费时间

    一个问题可能是 java.sql.Date 应该是……

    通过将与实例关联的特定时区中的小时、分钟、秒和毫秒设置为零来实现“标准化”。

    ...根据the documentation。这意味着,日期时间的时间部分正在从您的 java.util.Date 或 Joda-Time DateTime 对象中清除。

    无时区

    正如correct answer by Gilbert Le Blanc 所述,java.util.Date 和 java.sql.Date 在内部都没有时区的概念。它们存储自 Unix epoch 以来的毫秒数。

    这些类带来了一个讨厌的技巧:它们的toString 方法将您的JVM 的默认时区应用于字符串的呈现。非常混乱。 Date 对象没有时区,但当显示为字符串时,您会看到时区。

    如果您的 java.util.Date 对象包含自纪元(1970 年开始)以来的 1344902399000L 毫秒数,则表示 UTC/GMT 中的 2012-08-13T23:59:59.000Z。但是,如果您的 JVM 认为自己在夏令时 (DST) 生效的法国,您会看到比 UTC/GMT 早 2 小时:2012-08-14T01:59:59.000+02:00 以该类的糟糕字符串格式描述。同一时刻在不同时区具有不同的月份含义(13 对 14),墙上的时钟超过午夜。

    乔达救援时间

    Joda-Time 2.4 库在这里可以提供帮助。将 java.sql.Date 或 java.util.Date 对象与 UTC time zone object 一起传递给 DateTime 构造函数,以清楚地了解您正在努力处理的值。

    java.util.Date date = new java.util.Date( 1390276603054L );
    DateTime dateTimeUtc = new DateTime( date, DateTimeZone.UTC );
    System.out.println( "dateTimeUtc: " + dateTimeUtc );
    

    运行时……

    2014-01-21T03:56:43.054Z
    

    将另一个方向从 Joda-Time 转换为 java.util.Date…

    java.util.Date date = myDateTime.toDate();
    

    将另一个方向从 Joda-Time 转换为 java.sql.Date…

    java.sql.Date date = new java.sql.Date( myDateTime.getMillis() );
    

    更新 – java.time

    Joda-Time 项目现在处于维护模式,团队建议迁移到 java.time 类。

    java.util.Date 的等效项是 InstantInstant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

    Instant instant = Instant.ofEpochMilli( 1390276603054L );
    

    应用时区ZoneId,生成类似于java.util.Calendar 和Joda-Time DateTimeZonedDateTime

    ZoneId z = ZoneId.of( "Europe/Kaliningrad" );
    ZonedDateTime zdt = instant.atZone( z );
    

    现在提取一个仅限日期的值,即ZonedDateTime 的日期部分,作为LocalDateLocalDate 类表示没有时间和时区的仅日期值。所以LocalDate 就是java.sql.Date 所伪装的:一个仅限日期的值。

    LocalDate localDate = zdt.toLocalDate() ;
    

    在 JDBC 4.2 及更高版本中,您可以通过 PreparedStatement::setObjectResultSet::getObject 直接将 java.time 类型与兼容的驱动程序一起使用。

    myPreparedStatement.setObject( … , localDate );
    

    ……和……

    LocalDate ld = myResultSet.getObject( … , LocalDate.class );
    

    对于较旧的不兼容驱动程序,通过使用添加到旧类的新方法,将 java.sql.Date 对象与 LocalDate 进行简单转换:toLocalDatevalueOf( LocalDate )

    【讨论】:

    • 返回的路是什么?是:new java.sql.Date(jodaDateTimeValue.withZone(DateTimeZone.UTC).getMillis())
    • @thomas.mc.work 不,从 Joda-Time DateTime 对象到 java.util.Date 比这容易得多……只需调用 toDate 方法。像这样:java.util.Date date = myDateTime.toDate();。对于 java.**sql**.Date,您很接近但不需要 UTC,因为 Millis 始终是 UTC。所以:java.sql.Date date = new java.sql.Date( myDateTime.getMillis() );
    • 但是当我以这种方式将数据库中的 java.sql.Date 转换为值为“2014-02-01”时,我得到“2014-01-31T23:00:00.000Z”(我的时间区域是 CET)。当我使用 CET 时区时,它是正确的:“2014-02-01T00:00:00.000Z”。这是为什么呢?
    • @thomas.mc.work (a) 避免使用诸如“CET”之类的三字母时区代码。用户proper time zone name。 (b) 将字符串解析为 DateTime 时,传递用于构建时间的时区,否则将使用 JVM 的默认值。示例:DateTime dateTime = new DateTime( "2014-02-01", DateTimeZone.forID( "Europe/Berlin" ) );2014-02-01T00:00:00.000+01:00 (c) 德国比 UTC 早 1 小时。因此,汉诺威当天 00:00:00 的第一时刻 UTC 前一天的 23:00:00。领先于 UTC 意味着倒退到 UTC。
    【解决方案2】:

    java.sql.Date 的内部表示是自 1970 年 1 月 1 日 00:00:00.000 GMT 以来经过的毫秒数。

    您确定您不是在查看toString 问题吗?方法toGMTString()虽然已经贬值了,但是依然存在。

    【讨论】:

    • 这可能是问题所在,因为我在 java.util.Date.toString() 和它的时区信息丢失方面发生了疯狂。我会测试一些东西并报告回来。
    【解决方案3】:

    我猜你可能需要在配置文件中添加TIMEZONE=GMT

    在 web 应用程序中,这是在 web.xml 中定义的

    <context-param> <param-name>javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</p‌​aram-name> <param-value>true</param-value> </context-param>
    

    问候

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      • 2017-07-13
      • 2013-01-27
      • 2016-02-14
      • 1970-01-01
      • 1970-01-01
      • 2018-07-15
      相关资源
      最近更新 更多