The parser generated by DateTimeFormatter.ofPattern
exhibits the following interesting behaviour which is preventing me from writing a pattern to parse a string
If you don't mind to use a 3rd-party-library then you might try my library Time4J whose newest version v4.18 can do what you wish:
import net.time4j.Month;
import net.time4j.range.CalendarMonth;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.PatternType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.text.ParseException;
import java.util.Locale;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(JUnit4.class)
public class CalendarMonthTest {
@Test
public void parse2() throws ParseException {
assertThat(
ChronoFormatter.ofPattern(
"yyyyMM'00'",
PatternType.CLDR,
Locale.ROOT,
CalendarMonth.chronology()
).parse("20150100"),
is(CalendarMonth.of(2015, Month.JANUARY)));
}
}
By the way, the links to the JDK-bug-log are not really related to your problem. Those issues only describe problems when applying adjacent digit parsing in context of fractional seconds. While that problem will be fixed with Java-9, your problem will not. Maybe you wish to open a new issue there? But I doubt that Oracle will treat it as bug. It is rather a new feature not supported until now by any library distributed by Oracle. Literals with (leading) digits are not expected in JSR-310 (aka java.time-package) to take part into adjacent-value-parsing (and in SimpleDateFormat
also not).
Side note: Time4J is not just an answer to this detail (digit literals) but generally offers better performance in parsing and can be used in parallel with JSR-310 due to a lot of conversion methods. For example: To achieve an instance of YearMonth
, just call calendarMonth.toTemporalAccessor()
on the parsed result.
DateTimePrinterParser
:I step debugged all the way through it, apparently you can not have digits as literals. Similar test codes proves this if you step debug all the way through to the DateTimeFormatterBuilder.parse()
method you can see what it is doing wrong.
Apparently the Value(YearOfEra,4,19,EXCEEDS_PAD)
parser consumes the 00
where they stop if those are not digits because it is looking for a number 4
to 19
digits long. The DateTimeFormatter
that is embedded in the DateTimeParseContext
is wrong.
If you put a non-digit character literal like xx
it works, digit literals don't.
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'00'");
System.out.println(sdf.parse("20150100"));
Exception in thread "main" java.text.ParseException: Unparseable date: "20150100" at java.text.DateFormat.parse(DateFormat.java:366)
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'00'");
System.out.println(dateTimeFormatter.parse("20150100", YearMonth::from));
Exception in thread "main" java.time.format.DateTimeParseException: Text '20150100' 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)
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'xx'");
System.out.println(sdf.parse("201501xx"));
Thu Jan 01 00:00:00 EST 2015
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'xx'");
System.out.println(dateTimeFormatter.parse("201501xx", YearMonth::from));
2015-01