Странный org.threeten.bp.DateTimeException выброшен?
Мой код работал просто отлично. Сегодня внезапно я начал получать это исключение - org.threeten.bp.DateTimeException: Field DayOfMonth cannot be printed as the value 1872095944 max width is 2
Это мой простой код:
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd - MM - uuuu");
String sDate = date.format(formatter);//EXCEPTION THROWN HERE
Почему эта проблема вдруг?
РЕДАКТИРОВАТЬ
Это кажется промежуточной проблемой. Он иногда падает и отлично работает в другое время. Никаких подсказок о том, что происходит.
2 ответа
Это не проблема форматирования (здесь только симптом), а скорее проблема того, как экземпляр LocalDateTime
создано. Основная причина заключается в том, что просто LocalDateTime.now()
в некоторых редких случаях кажется, что день месяца полностью выходит за рамки. Эта проблема, вероятно, связана с этой проблемой в системе отслеживания проблем Threeten-BP.
LocalDate.ofEpochDay (x) иногда возвращает неправильный или недопустимый результат вместо того, чтобы генерировать исключение для больших значений x. Например, LocalDate.ofEpochDay(9223371671611556645L) возвращает дату с отрицательным значением для d.getDayOfMonth() вместо генерирования исключения DateTimeException .
Имейте в виду, что метод now()
должен сделать преобразование эпохи в фоновом режиме и, наконец, вызвать LocalDate.ofEpochDay(...)
, Так что, если ваши часы выдают необычное значение эпохи в миллис, начиная с эпохи Unix, то это может повлиять now()
, тоже. И ваш форматтер просто выбирает день месяца из вашего LocalDateTime
эффективно звоня getDayOfMonth()
(действительно через полевой доступ в TemporalAccessor
). Рассматриваемый исходный код:
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
переполнить и изменить знак на отрицательный. Похоже, вход Long.MIN_VALUE
наконец переполнится при использовании выражения 400 * zeroDay
, И я боюсь, что это не единственная проблема показанного кода. Для сравнения: правильная реализация григорианского алгоритма скорее будет выглядеть в моей собственной библиотеке времени.
Примечание:
С помощью моей библиотеки Time4J я проанализировал, что приведенные выше тестовые данные также дадут год, выходящий за пределы, как определено в threeten-bp (диапазон от -999999999 до +999999999):
PlainDate date = PlainDate.of(9223371671611556645L, EpochDays.UNIX);
// java.lang.IllegalArgumentException: Year out of range: 25252733927766555
Я не совсем уверен, что вы можете сделать, чтобы решить эту проблему.
Во-первых, обязательно зарегистрируйте все входные данные, произведенные вашими часами, свяжите их с наблюдаемым ошибочным поведением threeten-bp и проведите некоторое исследование, почему ваши часы иногда схожу с ума.
Что касается ошибки в threeten-bp (и Java-8!), Вы можете просто надеяться, что команда threeten-bp-project скоро ее исправит (точнее, Oracle!). Входные данные, вызывающие проблемы, вероятно, в любом случае неверны, поэтому лучше всего перехватить исключение и записать его с дополнительным сообщением о том, что часы неверны (как первопричина).
Есть ли последние изменения в API. Я не вижу никакой опции в списке разрешенных паттернов.
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era number/text 1; 01; AD; Anno
Domini
y year year 2004; 04
D day-of-year number 189
M month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q quarter-of-year number/text 3; 03; Q3
Y week-based-year year 1996; 96
w week-of-year number 27
W week-of-month number 27
e localized day-of-week number 2; Tue; Tuesday; T
E day-of-week number/text 2; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
{} reserved for future use