在Java中构造没有时区的日历

Posted

技术标签:

【中文标题】在Java中构造没有时区的日历【英文标题】:Construct Calendar without time zone in Java 【发布时间】:2018-12-18 09:58:33 【问题描述】:

我想在 Java 中构造没有时区的日期。我试过这个忽略时区:

Calendar time = Calendar.getInstance();
time.clear(Calendar.ZONE_OFFSET);

System.out.println("time = " + time.getTime());

但这对我不起作用。它返回“2018-07-11 03:00:00”而不是“2018-07-10 15:00:00”。

请有任何想法。

【问题讨论】:

目标是什么?如何“不工作”? 尝试使用LocalDate 对象而不是日历 @azro 我编辑了我的帖子。谢谢 我承认我不完全理解the documentation of clear(int):“这意味着isSet(field) 将返回false,并且日期和时间计算会将字段视为从未设置过。 Calendar 实现类可以使用字段的特定默认值进行日期和时间计算。”但在我看来,你应该得到一个带有默认区域偏移量的Calendar,而不是没有任何偏移量的Calendar 无论如何,我仍然希望getTime 返回代表当前时间的Date。它确实如此。请注意,Date 从来没有时区。 【参考方案1】:

tl;博士

LocalDateTime.now( 
    ZoneId.of( "America/Montreal" ) 
) 
.toString()

2018-01-23T12:34:56.123456

如果要删除分钟和秒,请截断。

LocalDateTime.now( 
    ZoneId.of( "America/Montreal" ) 
)
.truncatedTo( ChronoUnit.HOURS ) 
.toString()

2018-01-23T12:00:00

详情

Calendar 类现在是遗留的,被现代的 java.time 类所取代。此外,Calendar 始终带有时区,因此无法满足您的需求。

java.time 中,以“Local...”命名的三个类故意缺少任何时区或与UTC 偏移的概念。 local 一词表示任何 地区或每个 地区,而不是特定地区。

LocalDate

仅日期值,缺少时间和时区。

LocalTime

时间值,缺少日期和时区。仅限于一个 24 小时周期。

LocalDateTime

带有时间的日期,缺少时区。

代表片刻吗,是时间线上的一个点。表示大约 26-27 小时范围内的潜在时刻(不同时区使用的不同偏移量)。 LocalDateTime 没有实际意义,直到您应用时区或与 UTC 的偏移量来确定特定时刻。

当前时刻

使用Instant 类捕捉UTC 中的当前时刻。此类使用纳秒级的分辨率,但当前时刻可能不是那么精细,这取决于您的 JVM 实现、主机操作系统和主机计算机硬件时钟的限制。

Instant instant = Instant.now() ; // Capture current moment in UTC. 

如果您想通过特定地区的人们使用的点击墙壁时间的镜头来查看当前时刻,请应用时区。申请ZoneId 以获得ZonedDateTime

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

要仅使用 ZonedDateTime 中的日期和时间,但放弃时区,请生成 LocalDateTime 对象。

LocalDateTime ldt = zdt.toLocalDateTime() ;  // Extract the date and time-of-day but omit the time zone. 

作为快捷方式,您可以跳过分区对象InstantZonedDateTime。您仍然需要一个时区,ZoneId,因为对于任何给定的时刻,日期和时间在全球各地都因时区而异。 ZoneId 用于确定在该地区人们使用的挂钟时间中看到的当前时刻。一旦被捕获,ZoneId 就会被生成的LocalDateTime 对象遗忘。如果您想保留该区域,请记住该区域,您将使用 ZonedDateTime 而不是 LocalDateTime

LocalDateTime ldt = LocalDateTime.now( z ) ;

如果您想要 JVM 在运行时的当前默认时区,请将调用返回的 ZoneId 传递给 ZoneId.systemDefault。请注意,该 JVM 中任何应用程序的任何线程中的任何代码都可以随时更改默认值。

如果您想要 UTC 而不是某个时区,请传递来自 ZoneIdZoneOffset.UTCZoneOffset 子类的常量。

LocalDateTime.now( ZoneOffset.UTC )  // Capture the current date and time of day as seen in UTC (an offset of zero), and then discard that offset/zone info. 

关于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 类?

Java SE 8Java SE 9Java SE 10 及更高版本 内置。 标准 Java API 的一部分,带有捆绑实现。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能都在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

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

【讨论】:

【参考方案2】:

正如cricket_007 提到的,您想查看LocalDate 类。这是一个没有时区的日期。

它具有获取当前时间并将其转换为分区日期或日期时间的方法。这是 Java 中“新”时间 API 的一部分,它的设计理念是要求您考虑数据的含义,因为时间和日期是 incredibly hard to get right。

我们是在谈论持续时间吗?使用Duration。

您是否希望使用ZonedDateTime 向用户显示时间,但不要指望那里的值是连续的,因为日光时移等原因。

我们是在讨论给定时间只有一个值的线性时间吗?你想要一个Instant。

我们的想法是设计 API,这样您就不能在不考虑这些事情的情况下使用不同的数据类型。如果您想阅读更多内容,可以从Joda Time 开始,它是今天java.time 的前身

【讨论】:

以上是关于在Java中构造没有时区的日历的主要内容,如果未能解决你的问题,请参考以下文章

JDK8时间工具类

如何使用 Java 处理日历时区?

如何编写带有时区但没有时间组件的ISO 8601日期

《java核心技术 卷1 基础知识》二

java中的XMLGregorianCalendar,没有时区

ICS 错误的时区出现在 Google 日历中