java – 引发了奇怪的org.threeten.bp.DateTimeException?

我的代码运行得很好.今天我突然开始得到这个异常 – org.threeten.bp.DateTimeException:无法打印字段DayOfMonth,因为值1872095944最大宽度为2

这是我的简单代码:

LocalDateTime date = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd - MM - uuuu");
    String sDate = date.format(formatter);//EXCEPTION THROWN HERE

为什么这个问题突然?
编辑
这似乎是一个中间问题.它有时崩溃,其他时候工作正常.没有关于发生了什么的线索.一个

最佳答案 它不是一个格式化问题(这里只是一个症状),而是一个如何创建LocalDateTime实例的问题.根本原因很简单,在一些罕见的情况下,LocalDateTime.now()似乎会产生一个完全超出界限的日期.这个问题可能与这个问题跟踪器上的这个 issue有关.

LocalDate.ofEpochDay(x) sometimes returns a wrong or illegal result
instead of throwing an exception for large values of x . For
instance, LocalDate.ofEpochDay(9223371671611556645L) returns a date
with a negative value for d.getDayOfMonth() instead of throwing a
DateTimeException .

请记住,now()方法必须在后台执行epoch转换,最后调用LocalDate.ofEpochDay(…).因此,如果你的时钟在Unix纪元以来以毫秒为单位产生一个不寻常的纪元值,那么这也会影响now().并且您的格式化程序只需通过有效调用getDayOfMonth()(实际上通过TemporalAccessor中的字段访问)从LocalDateTime获取日期.有问题的源代码:

281     public static LocalDate ofEpochDay(long epochDay) { 
282         long zeroDay = epochDay + DAYS_0000_TO_1970; 
283         // find the march-based year 
284         zeroDay -= 60;  // adjust to 0000-03-01 so leap day is at end of four year cycle 
285         long adjust = 0; 
286         if (zeroDay < 0) { 
287             // adjust negative years to positive for calculation 
288             long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1; 
289             adjust = adjustCycles * 400; 
290             zeroDay += -adjustCycles * DAYS_PER_CYCLE; 
291         } 
292         long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE; 
293         long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400); 
294         if (doyEst < 0) { 
295             // fix estimate 
296             yearEst--; 
297             doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400); 
298         } 
299         yearEst += adjust;  // reset any negative year 
300         int marchDoy0 = (int) doyEst; 
301 

302         // convert march-based values back to january-based 
303         int marchMonth0 = (marchDoy0 * 5 + 2) / 153; 
304         int month = (marchMonth0 + 2) % 12 + 1; 
305         int dom = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1; 
306         yearEst += marchMonth0 / 10; 
307 

308         // check year now we are certain it is correct 
309         int year = YEAR.checkValidIntValue(yearEst); 
310         return new LocalDate(year, month, dom); 
311     } 

最有趣和最可疑的是,只有一年被验证,而不是月份或日期.事实上,看看这个奇怪的结果,包含由减号(???)分隔的四个部分:

LocalDate d = LocalDate.ofEpochDay(9223371671611556645L);
System.out.println(d); // -999999999-02-0-30
System.out.println(d.getDayOfMonth()); // -30

很明显,库代码会因某些奇特的时代数而被破坏,这可能是由您的时钟产生的.我也在Java-8中测试了相同的代码,结果相同.

更新:

到目前为止显示的LocalDate.ofEpochDay(long)的原始代码肯定会被破坏,这也是因为没有检查数值/算术溢出的事实.例如:像Long.MAX_VALUE这样的输入会导致表达式epochDay DAYS_0000_TO_1970溢出并将符号更改为负数.类似地,当使用表达式400 * zeroDay时,输入Long.MIN_VALUE最终会溢出.我担心这不是显示代码的唯一问题.为了比较:格里高利算法的正确实现更像是在my own time library.

边注:

在我的库Time4J的帮助下,我已经分析过上面给出的测试输入会产生一年远超出界限,如在threeten-bp中定义的那样(范围是-999999999直到999999999):

PlainDate date = PlainDate.of(9223371671611556645L, EpochDays.UNIX);
// java.lang.IllegalArgumentException: Year out of range: 25252733927766555

我不太清楚你能做些什么来解决这个问题.

首先肯定要记录你的时钟产生的所有输入,将它们与三十二节的观察到的错误行为联系起来,并做一些研究,为什么你的时钟有时会疯狂.

关于threeten-bp(和Java-8!)中的错误,你可以希望三个bp项目团队很快就能修复它(或者更确切地说是Oracle!).导致问题的输入无论如何都可能是错误的,因此您应该最好地捕获异常并使用时钟错误的额外消息(作为根本原因)记录它.

点赞