JSR310 Year.parse() throws DateTimeParseException with values < 1000

174 Views Asked by At

I'm trying to parse Year String values in the range from 0 to 1000 with java.time.Year.parse(), however parsing fails with java.time.format.DateTimeParseException: Text '999' could not be parsed at index 0.

The javadoc of Year.parse states:

Obtains an instance of Year from a text string such as 2007. 
The string must represent a valid year. Years outside the range 0000 to 9999 
must be prefixed by the plus or minus symbol.

Example test to reproduce this issue:

@Test
public void parse_year() {
   for (int i = 2000; i >= 0; i--) {
      System.out.println("Parsing year: " + i);
      Year.parse(String.valueOf(i));
   }
}

The test throws the exception when year 999 is reached:

Parsing year: 1003
Parsing year: 1002
Parsing year: 1001
Parsing year: 1000
Parsing year: 999
java.time.format.DateTimeParseException: Text '999' could not be parsed at index 0
    at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
    at java.time.Year.parse(Year.java:292)
    at java.time.Year.parse(Year.java:277)
[...]

What am I doing wrong?

2

There are 2 best solutions below

3
Scary Wombat On BEST ANSWER

needs to be padded to 4 digits

Year.parse(String.format("%04d", i));

Parsing year: 2000
Parsing year: ....
Parsing year: 4
Parsing year: 3
Parsing year: 2
Parsing year: 1
Parsing year: 0

If you wanted to parse before year 0, you could use

Year.parse("-0001");
0
Anonymous On

You have found a very peculiar inconsistency in java.time.

First, for the behaviour you have observed, I consider that justified. The classes of java.time generally parse the most common variants of ISO 8601 format. The ISO 8601 format for a year is (from Wikipedia, link at the bottom):

ISO 8601 prescribes, as a minimum, a four-digit year [YYYY] to avoid the year 2000 problem. It therefore represents years from 0000 to 9999, year 0000 being equal to 1 BC and all others AD.

So even though the documentation of Year.parse() that you quote isn’t very clear about it, the requirement of minimum 4 digits is taken from ISO 8601, certainly on purpose.

The other classes of java.time have the same requirement.

    LocalDate.parse("999-12-31");
Exception in thread "main" java.time.format.DateTimeParseException: Text '999-12-31' could not be parsed at index 0

So so far java.time is consistent.

To me the surprise came when trying the Year.toString method. The toString methods of java.time classes also generally print ISO 8601 format.

    System.out.println(Year.of(999).toString());

Output is:

999

So Year prints a format that does not conform with ISO 8601 and still more surprising, a format that it cannot parse back using its one-arg parse method. This I had not expected from any java.time class.

If you want to parse years with fewer than 4 digits, just use a formatter:

    DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("u");
    System.out.println(Year.parse("999", yearFormatter));
    System.out.println(Year.parse("0", yearFormatter));
999
0

Link