DateTimeFormatter和SimpleDateFormat产生不同的字符串 [英] DateTimeFormatter and SimpleDateFormat produce different strings
问题描述
这不是某些人认为的重复.这是关于用于格式化日期的两个标准Java类,这些类在自时期以来以相同的毫秒值生成不同的字符串.
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.
对于自1883年某个时间点之前的纪元以来的毫秒值,SimpleDateFormat和DateTimeFormatter将产生不同的结果.由于我不明白的原因,DateTimeFormatter会生成与我期望的字符串相差将近四分钟的字符串.
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.
这很重要,因为我正在更改一些代码以使用DateTimeFormatter而不是SimpleDateFormat.我们的输入始终是从纪元以来的毫秒数.更改代码后,我需要将值设置为相同.
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.
以前的代码将以毫秒为单位创建日期,然后使用SimpleDateFormat对其进行格式化.
The previous code would create a Date from the milliseconds, then use SimpleDateFormat to format it.
新代码从毫秒创建一个Instant,然后从Instant创建一个ZonedDateTime,然后创建一个DateTimeFormatter对其进行格式化.
The new code creates an Instant from the milliseconds, then a ZonedDateTime from the Instant, then a DateTimeFormatter to format it.
这是我使用JUnit4和Hamcrest编写的测试.该测试查找自5月13日15:41:25的纪元以来的毫秒数,该时间为从2019年开始的每年一次,并且每次倒退一年.
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.
每年,它使用SimpleDateFormat和DateTimeFormatter格式化毫秒格式,然后比较结果.
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();
}
运行此命令,您可以看到它从2019年开始一直回溯到1884年.因此,对于任何给定的年份,您都将看到类似以下的输出:
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
但是一旦到达1883,它就会莫名其妙地失败:
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.
顺便说一句,如果我将时区更改为"UTC",则测试通过.
By the way, if I change the time zone to "UTC", then the test passes.
推荐答案
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"):
1883年11月18日-时区更改(LMT→EST)
即将达到当地标准时间 1883年11月18日,星期日,下午12:03:58,时钟向后调到0:03:58 相反,1883年11月18日,星期日,当地标准时间中午12:00:00.
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匹配几乎四分钟"您所看到的. 我尚未对此进行测试,但是我敢打赌,如果您除了迭代数年又迭代数月之日,它会在该日期发生.
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.
- 为什么每年小于1884,会删除几毫秒?
- Python pytz时区转换返回的值不同日期的时区偏移量不同
- 为什么将这两次相减(在1927年)会得出奇怪的结果?-乔恩·斯凯特(Jon Skeet)回答的经典之作;不是相同的问题,而是相同的种类
- 《泰晤士报》报道两点正午"
- 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"
这篇关于DateTimeFormatter和SimpleDateFormat产生不同的字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!