如果 LocalDateTime 在 0000-00-00 00:00:00 之前,转换为 Timestamp 时,Timestamp 年份是错误的,例如 LocalDateTime 中的 -1996 将转换为 Timestamp 中的 -1997。 -1996是闰年,-1997不是闰年,所以你不能将Timestamp中的-1997-02-29转换为LocalDateTime
LocalDateTime localDateTime = LocalDateTime.of(-1996,2,29,10,10,10,100);
Timestamp timestamp = Timestamp.valueOf(localDateTime); // timestamp is B.C.E. 1997-02-29T10:10:10.000
LocalDateTime localDateTime1 = timestamp.toLocalDateTime(); // throw exception[ Invalid date 'February 29' as '1997' is not a leap year]
LocalDateTime 中的-1996 将转换为 Timestamp 中的 -1997
这并没有错。
LocalDateTime
和 Timestamp
的“年”含义不同。
A
LocalDateTime
“年份”值为 1 表示公元 1 年,值为 0 表示公元前 1 年,依此类推,因此值 -1996 代表公元前 1997 年。
A
Timestamp
“年份”是一个数字,加上 1900 就可以得到实际的年份(请参阅 java.util.Date
的文档),尽管它的许多 API 会自动为您加/减 1900。更糟糕的是,加上 1900 得到的年份总是正数,所以这有点像“纪元之年”。
当您打印
Timestamp
时,会自动添加 1900 以显示 1997 年。该数字不是负数,但 Timestamp
实际上代表 BC 中的某个时刻。
因此,转换 到
Timestamp
是正确的,但是当将 从 Timestamp
转换为 LocalDateTime
时,toLocalDateTime
方法只是从 Timestamp
获取年份并加上 1900,然后将其用作LocalDateTime
年。所以你实际上得到了一个 LocalDateTime
代表公元中的日期。由于公元 1997 年不是闰年,转换失败。
另请参阅 toLocalDateTime
的
来源。
可以说,这可以被视为
toLocalDateTime
中的一个错误,但我不认为Timestamp
首先被设计来代表公元前的日期(它可以代表那些时间的瞬间) ).
我建议您应该完全停止使用
Timestamp
。如果您想代表某个时刻,请使用 Instant
。
要进行此往返,您需要检查
Timestamp
是否在 AD 1 之前,如果是,则使用 -(getYear() + 1899)
作为 LocalDateTime
的年份。
这是它的外观草图:
// getAD1EpochMillis() is a method you should write to get the epoch millis
// of AD 1 January 1st midnight, in the system timezone.
// You should use the old APIs to do this, because timezones in java.time behaves differently.
if (timestamp.getTime() < getAD1EpochMillis()) {
return LocalDateTime.of(
-(timestamp.getYear() + 1899),
timestamp.getMonth() + 1,
timestamp.getDate(),
timestamp.getHours(),
timestamp.getMinutes(),
timestamp.getSeconds(),
timestamp.getNanos()
);
} else {
return timestamp.toLocalDateTime();
}
“AD 1 之前”的含义取决于时区,但
java.time
API 计算时区的方式与旧 API 不同(例如,java.time
在时区标准化之前考虑了当地平均时间)。这也是为什么你不能只对往返执行此操作:
timestamp.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
所有这些都表明,旧的 API 非常不擅长处理历史日期。请迁移至
java.time
。