My code was working just fine. Today suddenly I started getting this exception - org.threeten.bp.DateTimeException: Field DayOfMonth cannot be printed as the value 1872095944 max width is 2
This is my simple code :
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd - MM - uuuu");
String sDate = date.format(formatter);//EXCEPTION THROWN HERE
Why this problem suddenly?
EDIT
This seems to be an intermediate problem. It crashes sometimes and works fine on other times. No clues as to what is happening. A
It is less a formatting problem (here just a symptom) but rather a problem how an instance of LocalDateTime
is created. Root cause is simply that LocalDateTime.now()
seems to produce a day-of-month completely out of bounds in some rare cases. This problem is probably related to this issue on the issue tracker of threeten-bp.
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 .
Keep in mind that the method now()
must do an epoch conversion in the background and finally call LocalDate.ofEpochDay(...)
. So if your clock produces an unusal epoch value in millis since Unix epoch then this can affect now()
, too. And your formatter just fetches the day-of-month from your LocalDateTime
by effectively calling getDayOfMonth()
(really via field access in TemporalAccessor
). The source code in question:
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 }
Most interesting and suspicious is the fact that only the year is validated, but not the month or day-of-month. And indeed, have a look at this bizarre result containing four parts separated by minus-chars (???):
LocalDate d = LocalDate.ofEpochDay(9223371671611556645L);
System.out.println(d); // -999999999-02-0-30
System.out.println(d.getDayOfMonth()); // -30
Obviously the library code is broken for some exotic epoch-day-numbers which might be produced by your clock unfortunately. I have also tested the same code in Java-8, with the same wrong result.
Update:
The original code of LocalDate.ofEpochDay(long)
shown so far is definitely broken, also because of the fact that there is no check for numerical/arithmetic overflow. For example: An input like Long.MAX_VALUE
causes the expression epochDay + DAYS_0000_TO_1970
to overflow and to change the sign to negative. Similar, the input Long.MIN_VALUE
will finally overflow when using the expression 400 * zeroDay
. And I fear that this is not the only problem of shown code. For comparison: A correct implementation of the gregorian algorithm would rather look like in my own time library.
Side note:
By help of my library Time4J I have analyzed that given test input above would yield a year far out of bounds as defined in threeten-bp, too (range is -999999999 until +999999999):
PlainDate date = PlainDate.of(9223371671611556645L, EpochDays.UNIX);
// java.lang.IllegalArgumentException: Year out of range: 25252733927766555
I am not quite sure what you can do to solve the problem.
First thing is surely to log all inputs produced by your clock, relate them to the observed buggy behaviour of threeten-bp and to do some research why your clock goes sometimes mad.
About the bug in threeten-bp (and Java-8!), you can just hope that the threeten-bp-project team will soon fix it (or rather Oracle!). The input causing the problems is probably wrong anyway so you should best catch the exception and log it with the extra message that the clock is wrong (as root cause).
Are there any recent changes in the api. I don't see any u option in allowed pattern list.
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