3

我们有一个 java 程序,它在启动时使用 -Duser.timezone 参数:

-Duser.timezone="CET"

这是必要的,因为我们不能依赖服务器的内部时钟(管理员玩弄,设置错误的时区等等,不要问 :-( ) 并且前端的时区与后端匹配很重要。

由于该程序没有定期重新启动(它是一个服务器),我想知道当我们从冬季切换到夏季时会发生什么?它会自动切换还是我们必须重新启动服务器?

谢谢和问候, 亚历克斯

编辑:java可能会通过每次调用使用日期的函数来确定正确的时区。但是java也有可能在启动时确定正确的时区。

4

3 回答 3

2

Java 中的时间只是一个简单的 long 值(自 1970 年以来的毫秒数),没有任何有关时区的信息。并且还在内部将日期/时间存储为自 1970 年以来的毫秒数java.util.Datejava.sql.Date但使用 UTC 时区。

当您格式化输出的日期/时间或从字符串解析日期/时间时,时区就会发挥作用。-Duser.timezone届时将使用您设置的时区信息。

所以它应该可以工作,并且一个小测试也显示了它:

public static void main(String[] args) {
    Calendar c = Calendar.getInstance();
    c.set(2013, 2, 30, 23, 0, 0);
    long start = c.getTimeInMillis();
    long oneHour = 1000 * 60 * 60;
    long t = start;
    for (long i = 0; i < 5; i++) {
        System.out.println(new Date(t));            
        t = t + oneHour;
    }
}

使用-Duser.timezone=GMT它将打印:(无开关)

Sat Mar 30 23:00:00 GMT 2013
Sun Mar 31 00:00:00 GMT 2013
Sun Mar 31 01:00:00 GMT 2013
Sun Mar 31 02:00:00 GMT 2013
Sun Mar 31 03:00:00 GMT 2013

使用-Duser.timezone=CET它将打印:(凌晨 2 点切换)

Sat Mar 30 23:00:00 CET 2013
Sun Mar 31 00:00:00 CET 2013
Sun Mar 31 01:00:00 CET 2013
Sun Mar 31 03:00:00 CEST 2013
Sun Mar 31 04:00:00 CEST 2013

使用-Duser.timezone=EET它将打印:(东欧时间,CET 后一小时)

Sat Mar 30 23:00:00 EET 2013
Sun Mar 31 00:00:00 EET 2013
Sun Mar 31 01:00:00 EET 2013
Sun Mar 31 02:00:00 EET 2013
Sun Mar 31 04:00:00 EEST 2013
于 2013-02-20T14:00:08.043 回答
0

我很确定设置会user.timezone设置默认时区,就像TimeZone.setDefault(). 因此,它不会针对夏令时或其他更改进行更新。

如果您真的必须这样做,我的建议是向您的服务器创建一个自定义请求,该请求可以通过调用来更改时区TimeZone.setDefault()。这样,您可以在需要时更改它,而无需重新启动服务器。显然,这个请求需要得到保护,以便只有管理员才能执行它。

于 2013-02-20T13:57:50.707 回答
0

CET是一个伪区

永远不要使用 2-4 个字母的缩写,例如ESTIST因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

Continent/Region

以、或等格式指定适当的时区名称Continent/RegionAmerica/MontrealAfrica/CasablancaPacific/Auckland

也许CET您实际上是指时区,例如Europe/Luxembourgor Europe/Gibraltar

ZoneId zoneId = ZoneId.of( "Europe/Gibralter" ) ;

该类TimeZone现在是遗留的,是与 Java 的最早版本捆绑在一起的可怕日期时间类的一部分。只使用现代的java.time类。

为 DST 调整专有名称

由于该程序没有定期重新启动(它是一个服务器),我想知道当我们从冬季切换到夏季时会发生什么?

如果您使用如上所示的正确名称,并且在您的 JVM 安装中保持tzdata数据文件是最新的,那么在使用ZonedDateTime.

ZonedDateTime zdt = ZonedDateTime.now() ;  // Omitting the optional `ZoneId` argument means the JVM’s current default time zone is implicitly applied, looking up the current offset in use at this moment for the JVM's current default time zone.

让我们看看在 2020 年春季(即 3 月 29 日凌晨 2 点)夏令时切换期间会发生什么。我们得到的时间是凌晨 2 点前的一分钟。然后我们加一分钟。请注意,由于挂钟时间的 DST“提前”调整,一天中的时间如何从凌晨 1:59 跳转到凌晨 3 点而不是凌晨 2 点。

ZoneId z = ZoneId.of( "Europe/Gibraltar" ) ;
ZonedDateTime zdt = ZonedDateTime.of( 2020 , 3 , 29 , 1 , 59 , 0 , 0 , z ) ;
ZonedDateTime zdtMinuteLater = zdt.plusMinutes( 1 ) ;  // Notice how the time-of-day jumps to 3 AM rather than 2 AM.

转储到控制台。

System.out.println( "zdt.toString(): " + zdt ) ;
System.out.println( "zdtMinuteLater.toString(): " + zdtMinuteLater ) ;

请参阅在 IdeOne.com 上实时运行的代码

zdt.toString(): 2020-03-29T01:59+01:00[欧洲/直布罗陀]

zdtMinuteLater.toString(): 2020-03-29T03:00+02:00[欧洲/直布罗陀]

不要依赖默认值

您可以编写代码以不依赖 JVM 当前的默认时区。

前面显示的代码行 ( ZonedDateTime.now())不是我推荐的。该行省略了可选ZoneId参数。相反,我建议始终通过您想要/预期的时区。

ZoneId zoneId = ZoneId.of( "Europe/Gibralter" ) ;
ZonedDateTime zdtGibralter = ZonedDateTime.now( zoneId ) ;
于 2020-01-28T03:53:41.040 回答