3
votes

I am using YearQuarter from the ThreeTen-Extra library to represent a particular quarter within a given year.

I am using a custom format rather than the default format, e.g. "3Q2018" instead of "2018-Q3" for the third quarter of 2018. When parsing a value through YearQuarter.parse(text, formatter), I want values with leading zeros for the quarter to be rejected (via the standard DateTimeParseException).

My first attempt was to use DateTimeFormatter.ofPattern("Q'Q'yy"). However, this accepts strings with arbitrarily many leading zeros in front of the quarter. For instance, neither "02Q2007" nor "002Q2007" are rejected by this formatter.

This does seem consistent with how the single character patterns are handled for other temporal types. For instance, these successfully parse without error:

YearMonth.parse("012-2018", DateTimeFormatter.ofPattern("M-yyyy"))
LocalTime.parse("001:00:01", DateTimeFormatter.ofPattern("H:mm:ss"))
LocalTime.parse("01:00:012", DateTimeFormatter.ofPattern("HH:mm:s"))

However, these reject the input with a DateTimeParseException:

YearMonth.parse("012-2018", DateTimeFormatter.ofPattern("MM-yyyy"))
LocalTime.parse("001:00:01", DateTimeFormatter.ofPattern("HH:mm:ss"))
LocalTime.parse("01:00:012", DateTimeFormatter.ofPattern("HH:mm:ss"))

How can I construct a DateTimeFormatter to parse a YearQuarter in my desired format while rejecting values with more than one digit for the quarter? Ideally, I'd like to do this as a pattern string, as those are easier for humans to read and are much easier externalize as textual properties.

2

2 Answers

0
votes

I have not discovered a way to do this as a pattern string, but the following will programmatically construct a DateTimeFormatter with the desired format, rejecting 0-padded quarters:

import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;

[...]

new DateTimeFormatterBuilder()
    .appendValue(IsoFields.QUARTER_OF_YEAR, 1)
    .appendLiteral('Q')
    .appendValue(ChronoField.YEAR, 4)
    .toFormatter();
0
votes

If you insist on using a pattern string then I fear there is no way within the context of DateTimeFormatter, but I have successfully tested my lib Time4J if it provides the desired behaviour:

    ChronoFormatter<CalendarQuarter> f =
        ChronoFormatter.ofPattern(
            "Q'Q'uuuu", PatternType.CLDR, Locale.ROOT, CalendarQuarter.chronology());
    CalendarQuarter cq = f.parse("3Q2018");
    System.out.println(cq); // 2018-Q3
    f.parse("13Q2018"); // exception
    f.parse("03Q2018"); // exception

An object of type CalendarQuarter corresponds to YearQuarter so you will find similar methods to change it to a gregorian date, for example:

    CalendarQuarter cq = f.parse("3Q2018");
    LocalDate ld = cq.atEndOfQuarter().toTemporalAccessor();
    System.out.println(ld); // 2018-09-30