5

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?

Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
eidottermihi
  • 212
  • 1
  • 11

2 Answers2

4

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");
Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
  • Please mention the `+/-` prefix mentioned in the Javadoc (since the Javadoc doesn't say the year needs to be padded). – Kayaman May 20 '20 at 07:38
  • But the OP didn't understand it. Your answer shows how to easily make the loop work, but the source of the exception seemed to be lost, even though he quoted the exact part of the Javadoc. – Kayaman May 20 '20 at 07:39
  • 1
    This behavior (must be padded to 4digits) is really weird imo, but this way it works. – eidottermihi May 20 '20 at 07:53
2

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161