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 当前的默认时区。
请清楚Date 和String 是两个独立的不同对象。一个在 UTC 中保存一个时刻,另一个是调整到某个时区后那个时刻的文本表示。
GregorianCalendar、Date 和 String 都代表时间轴上的同一时刻、同一点,但挂钟时间不同。
为了清楚起见,使用 java.time
如果您使用现代的 java.time 类而不是使用旧类 Date、Calendar 和 GregorianCalendar 那样的混乱,日期时间处理会更加容易和清晰。
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 的替代品。此类在概念上是 Instant(UTC 中的时刻)与指定时区 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 对象上隐式调用了 toString。 java.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.Date、Calendar 和 SimpleDateFormat。
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 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。