2017-06-16 49 views
0

我必须在不同时区使用Java 8日期/时间。例如:Java 8 - tz数据库时区

LocalDateTime dateTime = LocalDateTime.of(2017, Month.JUNE, 1, 13, 39); 
Instant instant = dateTime.atZone(ZoneId.of("Europe/Paris")).toInstant(); 

和时间框架是,以日期时间

但我不想硬编码时区之间的一个实例,这始终是一个不好的做法

我做不到找到Java API表示类似https://en.m.wikipedia.org/wiki/List_of_tz_database_time_zones

的diffenent时区中的任何常量是否有任何映射,因为它是在短期区域ID ????

https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#of-java.lang.String-java.util.Map-

+1

你能举个例子说明你想达到什么目的。 – SubOptimal

+0

示例代码中的dateTime是什么? –

+1

你的问题没有写作的意义。最佳做法是在[UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time)中完成大部分任务:您的日志记录,业务逻辑,数据存储和数据交换/序列化。仅在业务问题需要或向用户呈现时才使用时区。与Instant.now()类似,UTC值的基本java.time类是'Instant'。我相信我们在最近的其他问题中广泛地介绍了这一点。 –

回答

6

时间区是政治家们经常重新定义。出现新区域。旧的改名(例如:Asia/Kolkata)。有些人确定实际上并不明确,最后指向另一人(例如:America/Montreal)。这就是名称 - 每个区域内的偏移量也经常会被政客们修改为夏令时(DST)等异常情况,或者决定完全下载DST或决定全年停留DST或决定改变抵消15分钟做出一些政治声明,如区分你的邻国。所以没有简单的永久列表。

Java拥有tzdata时区数据库的副本。如果您关心的任何区域发生更改,则需要在Java安装中更新此tzdata。 Oracle在其实施中为这件事提供了一个工具;我不知道其他人。同样,你也应该更新主机操作系统中的tzdata以及其他工具,比如你的数据库,如Postgres

至于Java中的ZoneId对象引用,你可以定义一些常量。 java.time类是线程安全的。所以你可以保持一个实例作为一个常量。

public class TimeUtils { 
    static public ZoneId ZONEID_EUROPE_PARIS = ZoneId.of("Europe/Paris") ; 
    static public ZoneId ZONEID_ASIA_KOLKATA = ZoneId.of("Asia/Kolkata") ; 
} 

你有LocalDateTime代表潜在的时刻,而不是在时间轴上的特定点。 A LocalDateTime没有时区或偏移量信息。所以中午在今年6月1日的LocalDateTime可能意味着很多不同的时刻,与第一中午在Kiribati在时区具有比UTC的偏移14小时发生。孟加拉国的中午晚些时候来临,而在法国巴黎的中午还有几个小时过去了。所以一个LocalDateTime没有真正的意义,直到你申请一个时区的上下文。

LocalDateTime noon1June2017Anywhere = LocalDateTime.of(2017 , Month.JUNE , 1 , 12 , 0); 

使用那些你需要的常量ZoneId

ZonedDateTime noon1June2017EuropeParis = noon1June2017Anywhere.atZone(TimeUtils.ZONEID_EUROPE_PARIS) ; 
ZonedDateTime noon1June2017AsiaKolkata = noon1June2017Anywhere.atZone(TimeUtils.ZONEID_ASIA_KOLKATA) ; 

注意noon1June2017EuropeParisnoon1June2017AsiaKolkata两个不同时刻,在时间轴上的不同点。中午在印度发生得比在法国早得多。

让我们来看看在UTC这两个数值为Instant对象。这两个Instant对象不等于作为加尔各答一个比一个巴黎早几个小时。

Instant instantNoon1June2017EuropeParis = noon1June2017EuropeParis.toInstant() ; // Extract the same moment but in UTC zone. 
Instant instantNoon1June2017AsiaKolkata = noon1June2017AsiaKolkata.toInstant() ; // Extract the same moment but in UTC zone. 

外部化

如果您的问题的意图是外部的应用是什么区的决定,这样你就可以改变这样的选择,而无需再次重新编译源代码,只是存储区的字符串名称,如Europe/Paris作为一些外部资源中的字符串。

将您的字符串传递给ZoneId.of。乡亲常用

ZoneId z = ZoneId.of(someStringOfZoneName) ; 

可能的存储机制:

  • 存放在一个文件中的文本。
  • 将文本存储在数据库行中以供应用程序检索。
  • 将文本作为条目存储在JNDI工具中(LDAP服务器,Servlet容器中的配置文件等)(请参阅Tutorial
  • 从Web服务进行查询。 (请参阅Tutorial
  • 询问用户的偏好,并将其存储在变量(如类的成员)中,或存储在Servlet环境存储字符串中作为上下文对象或会话对象的属性。您可以通过调用ZoneId.getAvailableZoneIds向用户提供所有区域的选择列表。

您可以询问JVM的当前默认区域:ZoneId.systemDefault。但要小心,这可以随时通过该JVM中任何应用程序中的任何代码进行更改。