实现与 SimpleDateFormat 相等的 DateTimeFormatter Y10K 输出?

Achieve DateTimeFormatter Y10K output equal to SimpleDateFormat?

在为一些约会助手编写单元测试时,我偶然发现了 DateTimeFormatter 的一种特殊行为,我想了解如何解决。

输出年>9999时,总是在年号前加一个加号。一些快速代码来说明这一点:

LocalDate localDate = LocalDate.of(9999, 1, 1);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.set(9999, 0, 1, 12, 0 , 0);

// following assertion passes as both strings are "01-01-9999"
Assertions.assertEquals(
    new SimpleDateFormat("dd-MM-yyyy").format(cal.getTime()),
    localDate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"))
);


localDate = localDate.plusDays(365);
cal.add(Calendar.DAY_OF_MONTH, 365);

// following assertion passes (lengthy workaround using SimpleDateFormat)
Assertions.assertEquals(
    new SimpleDateFormat("dd-MM-yyyy").format(cal.getTime()),
    new SimpleDateFormat("dd-MM-yyyy").format(Timestamp.valueOf(localDate.atTime(LocalTime.MIDNIGHT)))
);

// following assertion fails:
// Expected : "01-01-10000"
// Actual   : "01-01-+10000"
Assertions.assertEquals(
    new SimpleDateFormat("dd-MM-yyyy").format(cal.getTime()),
    localDate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"))
);

现在 docs 指出年份模式:

If the count of letters is less than four (but not two), then the sign is only output for negative years as per SignStyle.NORMAL. Otherwise, the sign is output if the pad width is exceeded, as per SignStyle.EXCEEDS_PAD.

所以这给了一个提示,但我仍然一无所知:

如何使 DateTimeFormatter 输出与 SimpleDateFormat.format() 在我的示例中所做的 Y10K+ 日期完全相同的字符串(=无符号正数 >9999)?

ISO 8601 允许今年格式。来自 Wikipedia:

To represent years before 0000 or after 9999, the standard also permits the expansion of the year representation but only by prior agreement between the sender and the receiver. An expanded year representation [±YYYYY] must have an agreed-upon number of extra year digits beyond the four-digit minimum, and it must be prefixed with a + or − sign instead of the more common AD/BC (or CE/BCE) notation;

然而,由于它只允许“发送者和接收者之间的事先约定”,所以添加符号是 LocalDate.toString 的默认行为是很奇怪的。

根据 docs:

Year: The count of letters determines the minimum field width below which padding is used. If the count of letters is two, then a reduced two digit form is used. For printing, this outputs the rightmost two digits. For parsing, this will parse using the base value of 2000, resulting in a year within the range 2000 to 2099 inclusive. If the count of letters is less than four (but not two), then the sign is only output for negative years as per SignStyle.NORMAL. Otherwise, the sign is output if the pad width is exceeded, as per SignStyle.EXCEEDS_PAD.

因此,如果您不想要符号,可以使用 3 个“y”,或者只使用 1 个“y”,因为 3 和 1 都“小于四(但不是二)”。

此外,由于“y”表示“时代的年份”,因此不会有任何负数年份,因此您也不必担心它会输出负数年份的符号。

示例:

System.out.println(
    LocalDate.of(10000, 1, 1)
        .format(DateTimeFormatter.ofPattern("dd-MM-yyy"))
); // 01-01-10000

更一般地说,您可以使用 DateTimeFormatterBuilder 中的 appendValue(TemporalField, int, int, SignStyle) 方法指定符号样式。您可以指定 SignStyle.NEVER 使其从不输出符号。