1. 前缀0可选
@Test
@DisplayName("前缀0可选")
public void test_for_optional_prefix_zero() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('/')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
.appendLiteral('/')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
assertEquals("2025-01-05", LocalDate.parse("2025/1/5", formatter).toString());
assertEquals("2025-01-05", LocalDate.parse("2025/01/05", formatter).toString());
assertEquals("2025-12-05", LocalDate.parse("2025/12/5", formatter).toString());
assertEquals("2025-12-05", LocalDate.parse("2025/12/05", formatter).toString());
assertEquals("2025-12-25", LocalDate.parse("2025/12/25", formatter).toString());
}
2. ResolverStyle的几种模式
@Test
@DisplayName("STRICT模式 严格模式")
public void test_for_strict() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('/')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
.appendLiteral('/')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
assertThrows(DateTimeException.class, () -> {
LocalDate.parse("2025/02/29", formatter);
LocalDate.parse("2025/13/01", formatter);
LocalDate.parse("2025/01/32", formatter);
});
}
@Test
@DisplayName("SMART模式 智能模式,默认值")
public void test_for_smart() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('/')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
.appendLiteral('/')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.toFormatter()
.withResolverStyle(ResolverStyle.SMART);
assertEquals("2025-02-28", LocalDate.parse("2025/02/29", formatter).toString());
assertEquals("2025-04-30", LocalDate.parse("2025/04/31", formatter).toString());
assertThrows(DateTimeException.class, () -> {
LocalDate.parse("2025/01/32", formatter);
LocalDate.parse("2025/13/01", formatter);
});
}
@Test
@DisplayName("LENIENT模式 宽松模式")
public void test_for_lenient() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('/')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
.appendLiteral('/')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.toFormatter()
.withResolverStyle(ResolverStyle.LENIENT);
assertEquals("2025-03-01", LocalDate.parse("2025/02/29", formatter).toString());
assertEquals("2026-01-01", LocalDate.parse("2025/13/01", formatter).toString());
assertEquals("2025-02-01", LocalDate.parse("2025/01/32", formatter).toString());
}
3. 时间字段可选
@Test
@DisplayName("时间字段可选")
public void test_for_optional_time() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('/')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
.appendLiteral('/')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.optionalStart()
.appendLiteral(' ')
.appendValue(ChronoField.HOUR_OF_DAY, 1, 2, SignStyle.NORMAL)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 1, 2, SignStyle.NORMAL)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 1, 2, SignStyle.NORMAL)
.optionalEnd()
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
assertEquals("2025-01-05T01:05:08", LocalDateTime.parse("2025/1/5 1:5:8", formatter).toString());
assertEquals("2025-12-05T23:05:59", LocalDateTime.parse("2025/12/5 23:5:59", formatter).toString());
assertEquals("2025-09-30T00:00", LocalDateTime.parse("2025/9/30", formatter).toString());
assertThrows(DateTimeException.class, () -> {
LocalDateTime.parse("2025/03/28 8:00", formatter);
});
}