How to process yyyy-mm and yyyy in java8 using DateTimeFormatter

社会主义新天地 提交于 2021-01-29 05:48:26

问题


Am using SimpleDateFormat to format or validate the dates, but I would like to make it thread-safe by using java 8 DateTimeFormatter. I am having trouble to achieve some requirement.

My application will accept only three types of formats. "yyyy-MM-dd", "yyyy-MM", "yyyy"

Existing Code gives me desired output:
/*simple date format to process yyyy-MM-dd format
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd")
/*simple date format to process yyyy-MM format
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM")

/*simple date format to process yyyy format
SimpleDateFormat simpleDateFormat3 = new SimpleDateFormat("yyyy")

/* to parse input
simpleDateFormat.parse(input)
/* to format
simpleDateFormat.format(simpleDateFormat1)

Here is the input and expected output:

  input             expected
'2018-03-19'       '2018-03-19'
'2018-03'          '2018-03'
'2018'             '2018'
'2017-02-54'       '2017-02'
'2016-13-19'       '2016'
  1. How can I achieve same result in java 8 DateTimeFormaenter code heretter?

    /* java 8 date time formatter DateTimeFormatter dateTimeFormatter = new DateTimeFormatter("yyyy-MM-dd")

The above snippet works when all year and month and date values are correct. Any help would be highly appreciated.


回答1:


Am using SimpleDateFormat to format or validate the dates

Never use SimpleDateFormat.

The terrible date-time classes bundled with the earliest versions of Java were years ago supplanted by the modern java.time classes defined in JSR 310.

thread-safe by using java 8 DateTimeFormatter

Yes, unlike the legacy date-time classes, the java.time classes use immutable objects and are thread-safe by design.

Here is the input and expected output:

Some of your inputs could be detected simply by their length.

// Ten-digits long, assume ISO 8601 date.
LocalDate ld = LocalDate.parse( "2018-03-19" ) ;

// Seven digits long, assume ISO 8601 year-month.
YearMonth ym = YearMonth.parse( "2018-03" ) ;

// Four digits, assume year.
Year y = Year.parse( "2018" ) ;

Notice that the above inputs all comply with ISO 8601. The java.time classes use ISO 8601 formats by default when parsing/generating strings. So no need to specify a formatting pattern. And therefore no need for an explicit DateTimeFormatter object.

'2017-02-54' '2017-02'

This example puzzles me. If you mean "When encountering a date with invalid day-of-month, just use the year and month while ignoring the day", I suppose you might be able to do that. Look into "lenient" mode on a DateTimeFormatter. Perhaps use DateTimeFormatterBuilder to build a flexible DateTimeFormatter. But frankly, I would reject such data as faulty inputs. It should be the job of the publisher of the data to produce reliable data, not the job of the consumer to guess the intention behind faulty data.

input expected

'2016-13-19' '2016'

Again, trying to guess the valid parts of invalid inputs in a dangerous game I would not play. If the month and day are invalid, how do you know the year is valid? Even worse, if the publisher of this data can emit such erroneous data, how do you know an apparently valid 2018-03-19 input is actually correct? If month 13 is a mistake, how do know an input with month of 03 is not a mistake too?

Teach the publisher of this problematic data about the ISO 8601 standard, and ask them to fix their bugs.




回答2:


Like Basil Bourque in the other answer I am not necessarily convinced that what you are asking for is also what will serve you the best. In any case, as a small supplement to that good answer I would like to present an approach to handling the last two cases, the invalid dates, the way you said.

For validation of the format I am first parsing the strings without validating the numbers. This will reject strings that are not in any of your three formats, for example 2016-03-19T12:00 or 2016 and some nonsense. The DateTimeFormatter.parseUnresolved method takes care of this part. In case of a parsing error, this method sets an error index in the ParsePosition object that we passed to it and returns null (so does not throw any exception). So I check whether null is returned.

private static DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("uuuu-MM")
        .withResolverStyle(ResolverStyle.STRICT);
private static DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("uuuu")
        .withResolverStyle(ResolverStyle.STRICT);

public static Temporal parse(String input) {
    // First try the three formats, uuuu-MM-dd, uuuu-MM, uuuu, in turn without resolving
    TemporalAccessor parsed = null;
    for (DateTimeFormatter formatter : Arrays.asList(DateTimeFormatter.ISO_LOCAL_DATE,
            yearMonthFormatter, yearFormatter)) {
        ParsePosition position = new ParsePosition(0);
        TemporalAccessor parseAttempt = formatter.parseUnresolved(input, position);
        if (parseAttempt != null && position.getIndex() == input.length()) {
            // Success; exit loop
            parsed = parseAttempt;
            break;
        }
    }
    if (parsed == null) { // didn’t match any of the three formats
        throw new IllegalArgumentException("Invalid format: " + input);
    }

    // Try resolving to either LocalDate, YearMonth or Year
    try {
        return LocalDate.of(parsed.get(ChronoField.YEAR),
                parsed.get(ChronoField.MONTH_OF_YEAR),
                parsed.get(ChronoField.DAY_OF_MONTH));
    } catch (DateTimeException dteRLd) {
        try {
            return YearMonth.of(parsed.get(ChronoField.YEAR),
                    parsed.get(ChronoField.MONTH_OF_YEAR));
        } catch (DateTimeException dteRYm) {
            return Year.of(parsed.get(ChronoField.YEAR));
        }
    }
}

Let’s try your examples:

    String[] inputs = {
            "2018-03-19",
            "2018-03",
            "2018",
            "2017-02-54",
            "2016-13-19"
    };

    for (String input : inputs) {
        Temporal parsed = parse(input);
        System.out.format("%-10s  %-10s  %s%n", input, parsed, parsed.getClass().getName());
    }

Output is:

2018-03-19  2018-03-19  java.time.LocalDate
2018-03     2018-03     java.time.YearMonth
2018        2018        java.time.Year
2017-02-54  2017-02     java.time.YearMonth
2016-13-19  2016        java.time.Year


来源:https://stackoverflow.com/questions/57893772/how-to-process-yyyy-mm-and-yyyy-in-java8-using-datetimeformatter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!