我在 Linux 上运行的嵌入式 Java 应用程序应该允许用户通过 GUI 更改时区。我的应用程序在 OSGI 容器中运行(请参阅下面为什么我认为这是相关的)并且在使用新时区之前不需要重新启动。
从我的 Java/OSGi 应用程序中持久设置时区的推荐方法是什么?
我可以想到以下方法,为此我列出了一些优点和缺点。我错过了什么吗?有什么推荐?:
- 从应用程序中,更改底层操作系统时区,另外
TimeZone.setDefault(...)
用于当前运行的 JVM 并更新所有Clock
持有旧 TZ 的实例(因此需要某种事件)。缺点:这种方法依赖于操作系统并且级别很低,我也想保持操作系统时钟 UTC。优点:操作系统负责存储 TZ,下次启动应用程序时 TZ 会立即正确。 - 从应用程序中,更改
-Duser.timezone=...
用于启动的参数。缺点:非常丑陋,甚至更低级别,但允许将操作系统时钟保留为 UTC,同时让应用程序以正确的 TZ 启动。还需要Clock
在更改时更新实例。 - 不要触摸操作系统,只在启动的早期
TimeZone.setDefault(...)
使用和调用它。这将需要一个单独的持久性(首选项)来保存它。同样,在当前运行的 JVM 中,所有引用旧 TZ 的实例都需要在更改时更新(需要事件)。在 OSGi 容器中运行时,无法保证包的启动顺序,因此我无法确定默认 TZ 在使用之前是否已设置。我如何保证这一点?此外,JSR310 明确建议不要在 Clock 中使用“默认 TZ”。Clock
- 根本不使用“默认”时区,使用单独的全局变量,并在每次
Instant
和LocalXXX
值之间转换时,显式传递时区。这摆脱了需要一个事件来更新Clock
实例。但是我们需要注意不要使用LocalDate.now(clock)
,因为这使用了时钟的 TZ (不再正确)。如何在 OSGi 中有这个全局变量?使用ConfigAdmin
?如何使我无法控制的代码行为正确(例如记录时间戳)?
编辑:为了摆脱更新的需要Clock
,我可以使用一个始终检查默认时区的时钟,但这从性能 POV 来看似乎不是最理想的:
public class DefaultZoneClock extends Clock {
private final Clock ref = Clock.systemUTC();
@Override
public ZoneId getZone() {
return ZoneId.systemDefault(); // probed on each request
}
@Override
public Clock withZone(ZoneId zone) {
return ref.withZone(zone);
}
@Override
public Instant instant() {
return ref.instant();
}
}
这是一个好主意吗?
编辑 2:
关于我上面的性能问题:它们显然是不合理的。当你调用时LocalDate.now()
,内部SytemClock
会构建一个新的,ZoneID
它通过在 Map 中搜索来设置当前 - 这与使用我上面的代码完全相同DefaultZoneClock
,不同之处在于使用我的代码我可以注入任何其他代码Clock
进行测试。(所有客户端代码都将使用LocalDate.now(clock)
)
下面的答案建议不要更改 JVM TimeZone,而是在必要时根据用户定义的 TimeZone 进行转换,这意味着我必须注意不要使用调用 TimeZone 的 java.time 方法Clock
,例如。如果我需要LocaTime
使用
// OK, TimeZone is set explicitely from user data
LocalTime t = clock.instant().atZone(myUserZoneID).toLocalTime();
// Not OK, uses the Clock's internal TimeZone which may not have been set or updated
LocalTime t2 = LocalTime.now(clock);