DateTimeFormatter和SimpleDateFormat产生不同的字符串

Rob*_*man 1 java java.time

这不是某些人认为的重复。它是关于用于格式化日期的两个标准Java类,这些类在自时期以来以相同的毫秒值生成不同的字符串。

对于自1883年某个时间点之前的纪元以来的毫秒值,SimpleDateFormat和DateTimeFormatter将产生不同的结果。由于我不明白的原因,DateTimeFormatter会产生与我期望的字符串相差将近四分钟的字符串。

这很重要,因为我正在更改一些代码以使用DateTimeFormatter而不是SimpleDateFormat。我们的输入始终是从纪元以来的毫秒数,在更改代码后,我需要将值设置为相同。

先前的代码将以毫秒为单位创建一个Date,然后使用SimpleDateFormat对其进行格式化。

新代码从毫秒创建一个Instant,然后从Instant创建一个ZonedDateTime,然后创建一个DateTimeFormatter对其进行格式化。

这是我使用JUnit4和Hamcrest编写的测试。该测试查找自5月13日15:41:25的纪元以来的毫秒数,该时间为2019年开始的每一年,并且每次倒退一年。

对于每年,它使用SimpleDateFormat和DateTimeFormatter格式化毫秒格式,然后比较结果。

@Test
  public void testSimpleDateFormatVersusDateTimeFormatter() throws Exception {
    String formatString = "EEE MMM dd HH:mm:ss zzz yyyy";
    String timeZoneCode = "America/New_York";

    ZoneId zoneId = ZoneId.of(timeZoneCode);

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatString);
    simpleDateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneCode));

    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatString);

    for (int year = 0; year < 200; year++) {

      long millis = getMillisSinceEpoch(2019 - year, 5, 13, 15, 41, 25, timeZoneCode);

      System.out.printf("%s%n", new Date(millis));

      // Format using a DateTimeFormatter;
      Instant instant = Instant.ofEpochMilli(millis);
      ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId);
      String dateTimeFormatterString = dateTimeFormatter.format(zonedDateTime);

      // Format using a SimpleDateFormat
      Date date = new Date(millis);
      String simpleDateFormatString = simpleDateFormat.format(date);

      System.out.println("dateTimeFormatterString = " + dateTimeFormatterString);
      System.out.println("simpleDateFormatString = " + simpleDateFormatString);
      System.out.println();

      assertThat(simpleDateFormatString, equalTo(dateTimeFormatterString));
    }
  }

  private long getMillisSinceEpoch(int year, int month, int dayOfMonth, int hours, int minutes, int seconds, String timeZoneId) {
    TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
    Calendar calendar = Calendar.getInstance(timeZone);
    calendar.set(Calendar.YEAR, year);
    calendar.set(Calendar.MONTH, month-1);
    calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
    calendar.set(Calendar.HOUR_OF_DAY, hours);
    calendar.set(Calendar.MINUTE, minutes);
    calendar.set(Calendar.SECOND, seconds);
    return calendar.getTimeInMillis();
  }
Run Code Online (Sandbox Code Playgroud)

运行此命令,您可以看到它从2019年开始一直回溯到1884年。因此,对于任何给定的年份,您都将看到如下输出:

Mon May 13 12:41:25 PST 1895
dateTimeFormatterString = Mon May 13 15:41:25 EST 1895
simpleDateFormatString = Mon May 13 15:41:25 EST 1895
Run Code Online (Sandbox Code Playgroud)

但是一旦到1883年,它就会莫名其妙地失败:

Sun May 13 12:41:25 PST 1883
dateTimeFormatterString = Sun May 13 15:45:23 EST 1883
simpleDateFormatString = Sun May 13 15:41:25 EST 1883


java.lang.AssertionError: 
Expected: "Sun May 13 15:45:23 EST 1883"
     but: was "Sun May 13 15:41:25 EST 1883"```
Run Code Online (Sandbox Code Playgroud)

小时和秒显然是错误的。

顺便说一句,如果我将时区更改为“ UTC”,则测试通过。

Jos*_*lor 8

根据https://www.timeanddate.com/time/change/usa/new-york?year=1883(这是Google搜索“ 1883时间调整”中的第一个命中):

1883年11月18日-时区更改(LMT?EST)

当当地标准时间即将到1883年11月18日(星期日)时,时钟将向后转换为当地标准时间1883年11月18日(星期日)0:03:58小时,到1883年11月18日星期日。

3:58匹配您所看到的“近四分钟”。我尚未对此进行测试,但是我敢打赌,如果您除了迭代数年又迭代数月之日,它会在该日期发生。

也可以看看