Java 8 - tz数据库时区

时间:2017-06-16 06:59:22

标签: java date datetime java-8 timezone

我必须在不同的时区使用Java 8日期/时间。例如:

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

和时间帧是到dateTimes之间的实例

但我不想对时区进行硬编码,这总是一种不好的做法

我无法在Java API中找到任何常量来表示不同的时区,如https://en.m.wikipedia.org/wiki/List_of_tz_database_time_zones

是否存在短区ID中的映射?

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

1 个答案:

答案 0 :(得分:8)

时间区域经常被政治家重新定义。出现新区域。旧的重命名(例如: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代表潜在的时刻,而不是时间轴上的特定点。 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 as某些外部资源中的字符串。

将您的字符串传递给ZoneId.of

ZoneId z = ZoneId.of( someStringOfZoneName ) ;

人们常用的可能存储机制:

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

您可以要求JVM的当前默认区域:ZoneId.systemDefault。但请注意,这可以随时由该JVM中的任何应用程序中的任何代码进行更改。

相关问题