【问题标题】:Java 8 - tz database time zonesJava 8 - tz 数据库时区
【发布时间】:2017-06-16 06:59:21
【问题描述】:

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

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

在短区域 ID 中是否有任何映射????

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

【问题讨论】:

  • 你能举个例子你想达到什么。
  • 您的示例代码中的dateTime 是什么?
  • 你的问题写的没有意义。最佳实践是在UTC 中完成大部分工作:您的日志记录、您的业务逻辑、您的数据存储以及您的数据交换/序列化。仅在业务问题需要或向用户展示时才使用时区。 UTC 值的基本 java.time 类是 Instant,如 Instant.now()。我相信我们在您最近的其他问题中对此进行了广泛的讨论。
  • 如果你想根据Europe/Paris的时区获得时间,不管你把它称为"Europe/Paris" or "FR" or ConstantHoldingClass.EUROPE_PARIS or ConstantHoldingClass.FR .这就是all“硬编码”。如果您“不想硬编码时区”,则必须将其设置为 your 程序的参数或配置选项。 (除非你想使用systemDefault(),这已经是一个环境选项)……
  • 一个想法是将Europe/Paris 放在一个属性文件中,您的应用程序会在启动时或第一次需要时区时读取该文件。这对您来说是否是一个好主意取决于您的需求、架构和开发周期等因素。变体包括将其放入数据库表中。

标签: java date datetime java-8 timezone


【解决方案1】:

时区经常被政治家重新定义。出现新区域。旧的被重命名(例如: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 代表潜在 时刻,而不是时间线上的特定点。 LocalDateTimeno 时区或偏移信息。因此,今年 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 中任何应用程序中的任何代码都可以随时更改此设置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    • 2011-10-01
    • 2010-09-17
    • 2018-11-15
    • 1970-01-01
    相关资源
    最近更新 更多