请注意,在旧版 API 中,year-of-era 和 year 之间没有区别。年份,0 实际上是1 BC。月份 0 和日期 0 是无效值,但 SimpleDateFormat 不会引发异常,而是错误地解析它们。
月份转换为11的原因:
SimpleDateFormat 将文本中的月份数字减少了1,因为java.util.Date 是基于0。换句话说,月份1 被SimpleDateFormat 解析为0,即月份Jan 对应java.util.Date。同样,月份,0 被SimpleDateFormat 解析为-1。现在,java.util.Date 处理消极月份如下:
month = CalendarUtils.mod(month, 12);
CalendarUtils#mod 的定义如下:
public static final int mod(int x, int y) {
return (x - y * floorDivide(x, y));
}
public static final int floorDivide(int n, int d) {
return ((n >= 0) ?
(n / d) : (((n + 1) / d) - 1));
}
因此,CalendarUtils.mod(-1, 12) 返回11。
java.util.Date和SimpleDateFormat充满了这样的惊喜。建议完全停止使用,转用modern date-time API。
现代日期时间 API:
现代日期时间 API 分别使用 y 和 u 区分 year-of-era 和 year。
y 指定时代(时代指定为AD 或BC)并且始终为正数,而u 指定年份 这是一个带符号 (+/-) 的数字。
通常,我们不使用+ 符号来写入正数,但我们总是使用- 符号指定负数。同一规则适用于 年。只要您打算使用时代的一年,AD,y 和u 都会给您相同的数字。但是,当您使用时代的年份时,您会得到不同的数字,BC e.g. 时代,1 BC被指定为年,0; 时代,2 BC被指定为年,-1等等。
您可以通过以下演示更好地理解它:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class Testing {
public static void main(String[] args) {
System.out.println(LocalDate.of(-1, 1, 1).format(DateTimeFormatter.ofPattern("u M d")));
System.out.println(LocalDate.of(-1, 1, 1).format(DateTimeFormatter.ofPattern("y M d")));
System.out.println(LocalDate.of(-1, 1, 1).format(DateTimeFormatter.ofPattern("yG M d")));
System.out.println();
System.out.println(LocalDate.of(0, 1, 1).format(DateTimeFormatter.ofPattern("u M d")));
System.out.println(LocalDate.of(0, 1, 1).format(DateTimeFormatter.ofPattern("y M d")));
System.out.println(LocalDate.of(0, 1, 1).format(DateTimeFormatter.ofPattern("yG M d")));
System.out.println();
System.out.println(LocalDate.of(1, 1, 1).format(DateTimeFormatter.ofPattern("u M d")));
System.out.println(LocalDate.of(1, 1, 1).format(DateTimeFormatter.ofPattern("y M d")));
System.out.println(LocalDate.of(1, 1, 1).format(DateTimeFormatter.ofPattern("yG M d")));
}
}
输出:
-1 1 1
2 1 1
2BC 1 1
0 1 1
1 1 1
1BC 1 1
1 1 1
1 1 1
1AD 1 1
现代日期时间 API 如何处理 0000:00:00 00:00:00?
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
class Main {
public static void main(String[] args) {
DateTimeFormatter parser = DateTimeFormatter.ofPattern("uuuu:MM:dd HH:mm:ss")
.withZone(ZoneOffset.UTC)
.withLocale(Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse("0000:00:00 00:00:00", parser);
}
}
输出:
Exception in thread "main" java.time.format.DateTimeParseException: Text '0000:00:00 00:00:00' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 0
....
与DateTimeFormatter#withResolverStyle(ResolverStyle.LENIENT):
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ENGLISH)
.withResolverStyle(ResolverStyle.LENIENT);
String str = "0000-00-00 00:00:00";
LocalDateTime ldt = LocalDateTime.parse(str, dtf);
System.out.println(ldt);
}
}
输出:
-0001-11-30T00:00