DateTimeFormatter and SimpleDateFormat produce different strings [duplicate]

删除回忆录丶 提交于 2019-12-24 06:29:59

问题


This is not a duplicate as some people think. It is about two standard Java classes for formatting dates that produce different strings for the same value of milliseconds since the epoch.

For values of milliseconds since the epoch that occur before some point in the year 1883, SimpleDateFormat and DateTimeFormatter will produce different results. For reasons I don't understand, DateTimeFormatter will produce strings that differ from what I expect by almost four minutes.

This is important because I am changing some code to use DateTimeFormatter instead of SimpleDateFormat. Our input is always milliseconds since the epoch, and I need the values to be the same after I change the code.

The previous code would create a Date from the milliseconds, then use SimpleDateFormat to format it.

The new code creates an Instant from the milliseconds, then a ZonedDateTime from the Instant, then a DateTimeFormatter to format it.

Here's a test I wrote using JUnit4 and Hamcrest. The test finds the milliseconds since the epoch for May 13, 15:41:25, for each year starting at 2019 and working backwards one year at a time.

For each year, it formats the milliseconds using SimpleDateFormat and DateTimeFormatter then compares the results.

@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();
  }

Running this you can see it passes for all years from 2019 back to 1884. So for any given year you see output like this:

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

But once it gets to 1883 it inexplicably fails:

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"```

The hours and seconds are obviously wrong.

By the way, if I change the time zone to "UTC", then the test passes.


回答1:


According to https://www.timeanddate.com/time/change/usa/new-york?year=1883 (which was the first hit in a Google search for "1883 time adjustment"):

Nov 18, 1883 - Time Zone Change (LMT → EST)

When local standard time was about to reach Sunday, November 18, 1883, 12:03:58 pm clocks were turned backward 0:03:58 hours to Sunday, November 18, 1883, 12:00:00 noon local standard time instead.

3:58 matches the "almost four minutes" that you're seeing. I haven't tested this, but I bet that if you iterate through months and days in addition to years, it occurs at that date.

See Also

  • Why when year is less than 1884, it remove few milliseconds?
  • Python pytz timezone conversion returns values that differ from timezone offset for different dates
  • Why is subtracting these two times (in 1927) giving a strange result? — a classic answered by Jon Skeet; not the same issue, but the same kind of issue
  • The Times Reports on "the Day of Two Noons"


来源:https://stackoverflow.com/questions/56121081/datetimeformatter-and-simpledateformat-produce-different-strings

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!