尴尬的 Java 日期创建行为

问题描述 投票:0回答:4

当我尝试创建两个日期时,我刚刚发现了 Java 的 Date 类的一个非常奇怪的行为:

Date startDate = new Date(1282863600000L);
System.out.println(startDate);

Date endDate = new Date(1321919999000L);
System.out.println(endDate);

输出分别为:

2010 年 8 月 27 日星期五 00:00:00 BST
2011 年 11 月 21 日星期一 23:59:59 GMT

有人见过这样的事情吗?两个日期都以相同的方式初始化,但打印时第一个显示为 BST,后者显示为 GMT?

我试图找到对此的解释,但我没有。有人可以帮助我吗?

提前致谢!

java date timezone
4个回答
5
投票

这是有记录的行为。

来自

Date.toString()

将此日期对象转换为以下形式的字符串:

 dow mon dd hh:mm:ss zzz yyyy

zzz
是时区(可能反映夏令时)。标准时区缩写包括那些由方法解析识别的缩写。如果时区信息不可用,则
zzz
为空 - 也就是说,它根本不包含任何字符。

您使用的区域设置使用英国夏令时,并创建一个适用夏令时规则的日期。这将是本地用户当时期望的日期形式。


1
投票

对我来说这段代码的输出是

2010 年欧洲夏令时间 8 月 27 日星期五 01:00:00
欧洲中部时间 2011 年 11 月 22 日星期二 00:59:59

确切的结果取决于您系统上使用的默认 Java 语言环境。

区别在于CEST是欧洲中部夏令时间,而CET是欧洲中部时间(即不是夏令时)。

您似乎在英国语言环境中运行(

en_GB
或类似语言),因此您的输出分别显示英国夏令时间格林威治标准时间

您指定的第一个日期属于各自的夏季时间,而第二个日期则不然。因此 Java 会为每个区域/时间组合选择适当的时区。


1
投票

在尝试不同的

long
值之后,我得到了这个:

Date startDate1 = new Date(1284245999999L);
Date startDate2 = new Date(1284246000000L);
System.out.println(startDate1);
System.out.println(startDate2);

Date endDate = new Date(1321919999000L);
System.out.println(endDate);

输出是:

Sun Sep 12 01:59:59 IDT 2010
Sun Sep 12 01:00:00 IST 2010 <-- Long value is greater, but due to DST changes, actual time is one hour earlier
Tue Nov 22 01:59:59 IST 2011

请注意,将 long 从

1
增加
1284245999999L
1284246000000L
会让我们“回到过去”,因为从标准时间过渡到夏令时。
这就是 Java 时间计算的行为方式 - 自 1970 年 1 月 1 日以来的毫秒数不变,但它表示的时间基于时区。


0
投票

为什么输出中包含 BST 和 GMT?

  1. java.util.Date
    不保存时区信息。它存储自 UTC 1970 年 1 月 1 日午夜以来的毫秒数,并且
    Date#toString
    将 JVM 的默认时区应用于它以形成输出文本。由于您的 JVM 将欧洲/伦敦设置为时区,因此
    Date#toString
    使用相同的时区。
  2. 八月属于欧洲/伦敦时区的DST,DST 期间的时间称为英国夏令时 (BST)。 11 月位于欧洲/伦敦时区的 DST 之外,DST 以外的时间称为格林威治标准时间 (GMT)。检查 thisthis 链接以了解更多信息。

java.time

2014 年 3 月,Java 8 引入了现代的

java.time
日期时间 API,取代了容易出错的旧版
java.util
日期时间 API
。任何新代码都应使用
java.time
API。

java.time.ZonedDateTime
保存时区信息以及日期和时间。以下是其文档的摘录:

ISO-8601 日历系统中带有时区的日期时间,例如

2007-12-03T10:15:30+01:00 Europe/Paris

它最好的一点是它会根据 DST 自动调整区域偏移,如下面的演示所示。

为了避免任何误导性的输出,

ZonedDateTime
要求您在将
ZoneId
转换为
Instant
时指定
ZonedDateTime

演示:

class Main {
    public static void main(String[] args) {

        ZoneId zoneId = ZoneId.of("Europe/London");
        
        ZonedDateTime zdt1 = Instant.ofEpochMilli(1282863600000L)
                .atZone(zoneId);
        ZonedDateTime zdt2 = Instant.ofEpochMilli(1321919999000L)
                .atZone(zoneId);

        System.out.println(zdt1);
        System.out.println(zdt2);
    }
}    

输出:

2010-08-27T00:00+01:00[Europe/London]
2011-11-21T23:59:59Z[Europe/London]

Z
表示
+00:00
的时区偏移量,即 UTC 的时区偏移量。

在线演示

Trail:日期时间了解有关现代日期时间 API 的更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.