在Java中,某些日期无法正确转换为特定时区午夜的历元时间戳

在Java中,某些日期无法正确转换为特定时区午夜的历元时间戳,java,timestamp,epoch,zone,Java,Timestamp,Epoch,Zone,这个Java代码,以字符串形式给出日期,应该在CET区域的午夜打印同一日期的历元时间戳(假设我不在同一区域) 如果我运行上述程序,我将得到打印: Epoch timestamp = 802389600000 我可以在这里验证它是否正确: 现在,这适用于大多数日期。然而,也有一些奇怪的日期,如“1975-09-19”,在那里它不起作用。事实上,它生成180313200000作为时间戳,给出凌晨1点而不是午夜: 你能解释一下原因吗?我遗漏了什么?关于夏令时,您的约会示例之间似乎存在差异。 如

这个Java代码,以字符串形式给出日期,应该在CET区域的午夜打印同一日期的历元时间戳(假设我不在同一区域)

如果我运行上述程序,我将得到打印:

 Epoch timestamp = 802389600000
我可以在这里验证它是否正确:

现在,这适用于大多数日期。然而,也有一些奇怪的日期,如“1975-09-19”,在那里它不起作用。事实上,它生成180313200000作为时间戳,给出凌晨1点而不是午夜:


你能解释一下原因吗?我遗漏了什么?

关于夏令时,您的约会示例之间似乎存在差异。
如果我使用
java.time
(从java 8开始就应该一直使用),我会得到不同偏移量的结果:

  • “+02:00”
    用于
    “1995-06-06”
  • “+01:00”
    对于
    “1975-09-19”
这就是我得到结果的原因:

public static void main(String[] args) {
    // provide two sample dates
    String workingDateStr = "1995-06-06";
    String failingDateStr = "1975-09-19";
    // and a formatter that parses the format
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    // then parse them to date objects that don't know about time or zone
    LocalDate workingDate = LocalDate.parse(workingDateStr, dtf);
    LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
    /*
     *  then create an objects that are aware of time and zone
     *  by using the parsed dates, adding a time of 00:00:00 and a zone
     */
    ZonedDateTime workingZdt = ZonedDateTime.of(workingDate, LocalTime.MIN, ZoneId.of("CET"));
    ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));

    // finally, print different representations of the results
    System.out.println(workingZdt + " ——> " + workingZdt.toInstant().toEpochMilli());
    System.out.println(failingZdt + " ——> " + failingZdt.toInstant().toEpochMilli());
}
输出:

1995-06-06T00:00+02:00[CET]->802389600000
1975-09-19T00:00+01:00[CET]->180313200000
这意味着您最好使用特定偏移,而不是分区

这一问题可能是由于马耳他采用夏令时的时间,请查看以下代码及其输出:

public static void main(String[] args) {
    // provide two sample dates
    String failingDateStr = "1975-09-19";
    // and a formatter that parses the format
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    // then parse them to date objects that don't know about time or zone
    LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
    /*
     *  then create an objects that are aware of time and zone
     *  by using the parsed dates, adding a time of 00:00:00 and a zone
     */
    ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));

    // add some years to 1975 and...
    for (int year = 0; year < 4; year++) {
        // ... print the different representations of the result
        System.out.println(failingZdt.plusYears(year) + " ——> " 
                + failingZdt.plusYears(year).toInstant().toEpochMilli());
    }
}
此输出表示1977年的介绍。。。正确吗?

时区差异 您的Java代码使用CET,而CET并不是真正的时区(例如,因为大部分使用CET的区域在一年中的大部分时间都使用CEST)。Java将CET翻译成欧洲/巴黎。1975年,法国和巴黎没有使用夏季时间(DST)。它于1976年3月重新引入

您与历元转换器的链接指定马耳他时区(欧洲/马耳他)。马耳他在1975年确实使用了夏季时间:该年4月20日至9月21日在CEST

这就解释了结果的差异

在Java代码中 如果你想要时间:

    String dateStr = "1975-09-19";

    long epochTimestamp = 
            LocalDate 
            .parse(dateStr)                            
            .atStartOfDay(ZoneId.of("Europe/Malta"))
            .toInstant()
            .toEpochMilli();

    System.out.println("Epoch timestamp = " + epochTimestamp);
这张照片是:

历元时间戳=18030960000

您链接到的epoch converter很高兴同意:

转换结果(180309600) 180309600转换为1975年9月19日星期五00:00:00(上午)in 时区欧洲/马耳他(CEST)偏移量(与格林威治的差异 时间/GMT)为+02:00或7200秒。这个日期是在白天 节省时间

在Java中,一定要使用Java.time,这是现代Java日期和时间API,用于日期和时间工作。与旧的日期和时间类(如
SimpleDateFormat
时区
日期
日历
)相比,使用它要好得多。此外,将小时数等设置为0也不是获取一天中第一个时刻的正确方法。在有些情况下,夏季从一天的开始开始,因此一天的第一个时刻是01:00:00。Java知道这一点,因此,
atStartOfDay
方法将为您提供一天中正确的forst时刻

无论是使用过时类还是现代类,始终以区域/城市格式指定时区,例如Europe/Paris或Europe/Malta。三个、四个和五个字母的时区缩写常常模棱两可,而且往往不是真正的时区,因此不值得依赖

链接
  • 解释如何使用java.time

您实际看到了什么错误?什么“不起作用”?这不是一个错误,但时间戳不正确,你可以通过我发布的链接验证它。该网站将欧洲/马耳他列为CEST时区,即GMT+2,但你的代码使用CET,即GMT+1。你确定你的时区是正确的吗?“CET”实际上并不是一个时区,就像“BST”、“EST”等一样。我强烈建议您使用实时时区ID(例如欧洲/马耳他),而不是“半时区”的缩写。我建议您不要使用
SimpleDateFormat
Date
Calendar
。这些类设计得很糟糕,而且早已过时,第一个类尤其令人讨厌。而是使用
ZoneDateTime
Instant
,这两种方法都来自。
    String dateStr = "1975-09-19";

    long epochTimestamp = 
            LocalDate 
            .parse(dateStr)                            
            .atStartOfDay(ZoneId.of("Europe/Malta"))
            .toInstant()
            .toEpochMilli();

    System.out.println("Epoch timestamp = " + epochTimestamp);