【问题标题】:Convert Calendar to Date - ignore timezone将日历转换为日期 - 忽略时区
【发布时间】:2018-12-17 16:35:10
【问题描述】:

我们从 3rd 方库收到一个 GregorianCalendar 对象。我们需要将其转换为 Date 以便在另一个 3rd 方库中使用。而且我们使用的是 Java 1.6,因此我们没有可用的新时间/即时类。

问题是调用 Calendar.getTime() 给出不同的日期,偏移(我认为)我们的时区。所以第二天提前了 8 小时。

如果没有这种转变,我们怎么能做到这一点?

更新:我们从 OData 调用中获取日期。返回的日期是员工的生日 (Northwind),因此不应该有时间。但它作为 GregorianCalendar 对象返回,时间为 1992-05-01 00:00:00。它显示 GMT 时区。

getTime() 返回“Thu Apr 30 18:00:00 MDT 1992”的日期 - 我在山区时区。

问题是我需要从日历对象中获取 1992 年 5 月 1 日的日期对象,而不是 1992 年 4 月 30 日。并且最好也与时间偏移匹配。

【问题讨论】:

  • Date 代表一个瞬间。它没有时区。
  • 所以calendar.get(Calendar.DATE) 不起作用? ‒ 提供 calendar 是您的 GregorianCalendar 实例。
  • Date#toString 是自 Date 的实例所代表的 unix 纪元以来的时间量的“人类可读”表示。 Date 对象本身没有“时区”或“偏移量”的概念,它们是“事后”应用的计算
  • 好吧,Calendar.getTime(),如 Java 文档所述,“返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。”那么,您是说您以某种方式从中得出日期,还是使用其他方法?另外,您希望哪个时区不会发生“转变”?
  • @GigiBayte2 Calendar.getTime 返回的 java.util.Date 对象代表 UTC 中的一个时刻,但其 Date::toString 方法令人困惑地返回 String 动态应用 JVM 的当前默认时区。因此,这个问题和 Stack Overflow 上的许多其他问题背后的困惑。

标签: java date calendar


【解决方案1】:

tl;博士

无班次

java.util.Date date = myGregorianCalendar.getTime() ;  // Same moment, same point on the timeline. `Date` is in UTC, `GregorianCalendar` may be in some other time zone.
String output = date.toString() ;  // This new string is a lie, dynamically applying the JVM’s current time zone while the `Date` is actually in UTC, always, by definition.

没有班次。调用GregorianCalendar.getTime 产生了java.util.Date。根据定义,Date 对象始终采用 UTC。 不幸的是,Date::toString 方法存在,在生成字符串的同时注入了 JVM 当前的默认时区。

请清楚DateString 是两个独立的不同对象。一个在 UTC 中保存一个时刻,另一个是调整到某个时区后那个时刻的文本表示。

GregorianCalendarDateString 都代表时间轴上的同一时刻、同一点,但挂钟时间不同。

为了清楚起见,使用 java.time

如果您使用现代的 java.time 类而不是使用旧类 DateCalendarGregorianCalendar 那样的混乱,日期时间处理会更加容易和清晰。

java.time

GregorianCalendar 类是 Java 8 及更高版本中内置的 java.time 类所取代的麻烦的旧日期时间类之一。在ThreeTen-Backport 项目中,大部分java.time 功能都被反向移植到Java 6 和Java 7。

使用添加到旧类的新方法,特别是GregorianCalendar::toZonedDateTime,将旧类转换为现代java.time。如果使用后端端口,请使用 DateTimeUtils 类。

ZonedDateTime zdt = DateTimeUtils.toZonedDateTime( myCalendar ) ;

ZonedDateTime 对象是 GregorianCalendar 的替代品。此类在概念上是 InstantUTC 中的时刻)与指定时区 ZoneId 对象的组合。

如果您想要UTC 中看到的相同时刻,请提取Instant

Instant instant = zdt.toInstant() ;

您可以从Instant 转换回java.util.Date,以兼容尚未更新到java.time的旧代码。

java.util.Date date = DateTimeUtils.toDate( instant ) ;  // Convert from modern `Instant` class to legacy `Date` class.

如果您只需要日期部分,没有时间和时区,请创建一个LocalDate 对象。

LocalDate ld = zdt.toLocalDate() ;

问题是调用 Calendar.getTime() 给出不同的日期,偏移(我认为)我们的时区。所以第二天提前了 8 小时。

如果没有这种转变,我们怎么能做到这一点?

getTime() 返回“Thu Apr 30 18:00:00 MDT 1992”的日期 - 我在山区时区。

你所看到的是一种幻觉。 GregorianCalendar::getTime 方法返回给您一个 java.util.Date 对象。然后,您在该 Date 对象上隐式调用了 toStringjava.util.Date::toString 方法有一个不幸的行为,即应用 JVM 的当前默认时区,同时生成一个字符串来表示其值。根据定义,Date 的值实际上是 UTC,始终是 UTC。 toString 方法造成了Date 包含时区的错觉,而实际上它没有


实际上,java.util.Date确实包含时区,但在其源代码的深处。用于equals 方法实现之类的东西。但是这个类没有 getter 或 setter,所以它对我们来说似乎是不可见的。在你的问题的背景下,是无关紧要的。

令人困惑?是的。这是避免这些可怕的旧日期时间类的许多原因之一。只使用 java.time 类。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 如果他们"从第三方库"接收GregorianCalendar对象并且他们需要"a Date来使用在另一个 3rd 方库" 中,添加 ThreeTen-Backport 只是为了进行转换就有点过分了。对不起,不是一个有用的答案。 -1
  • @Andreas (a) 使用 java.time 类使问题和解决方案更加清晰和明显。 (b) 这可能不是他们唯一处理日期时间的地方。
  • @Andreas 我们打算在新版本上升级到 Java 1.8,但是对于这个问题,我需要在旧版本的一个地方解决这个问题。所以是的,谢谢。
  • @DavidThielen FYI... ThreeTen-Backport 背后的想法是 API 与 java.time 几乎相同。您现在可以通过 Java 6 和 7 中的后端端口开始使用 java.time 功能。当您迁移到 Java 8 或更高版本时,您只需更改您的 import声明。
  • 既然您无论如何都要升级到 Java 8,我肯定会考虑使用 ThreeTen Backport,直到那时才有优势,因此您已经开始采用新的做事方式并携带更少的老式代码.当然,这取决于你。
【解决方案2】:

获取默认时区的Date值,调用setTimeZone()

GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
cal.clear();
cal.set(1992,4,1); // 1992-05-01 00:00:00 GMT

// "Fix" time zone
cal.setTimeZone(TimeZone.getDefault());

System.out.println(cal.getTime());

输出

Fri May 01 00:00:00 EDT 1992

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 2011-03-02
    • 2017-07-21
    • 2017-11-22
    • 2021-12-27
    • 1970-01-01
    相关资源
    最近更新 更多