One option is to exploit the TemporalAccessor interface. Nearly all of the date and time classes of java.time implement this interface and certainly those classes that you would use for your uncertain birthdays. Declare a variable a TemporalAccessor, and you may assign a Year or a LocalDate or some other type to it.
Some programmers would be tempted to use instanceof a lot with such a variable, which may be considered not so nice. Fortunately for many purposes it also isn’t necessary. The isSupported method of TemporalAccessor can be used in most situations.
For a demonstration:
// Example birthdays ordered from the best known to the completely unknown
TemporalAccessor[] birthdays = {
LocalDate.of(1955, Month.MARCH, 22),
YearMonth.of(1978, Month.MAY),
Year.of(1969),
IsoEra.CE
};
for (TemporalAccessor birthday : birthdays) {
String yearString = birthday.isSupported(ChronoField.YEAR)
? String.valueOf(birthday.get(ChronoField.YEAR))
: "N/A";
String monthString = birthday.isSupported(ChronoField.MONTH_OF_YEAR)
? Month.from(birthday).toString()
: "N/A";
String dateString = birthday.isSupported(ChronoField.DAY_OF_MONTH)
? String.valueOf(birthday.get(ChronoField.DAY_OF_MONTH))
: "N/A";
System.out.format("%-10s %4s %-9s %3s%n", birthday, yearString, monthString, dateString);
}
Output from this snippet is:
1955-03-22 1955 MARCH 22
1978-05 1978 MAY N/A
1969 1969 N/A N/A
CE N/A N/A N/A
For a nicer interface you may wrap the TemporalAccessor in a home-made class. Your class could have getters for Year and LocalDate, for example, which could return an empty Optional or null in the cases where there are not enough data. You can think of more convenience methods. You know your requirements best, so I am leaving the details of the design to yourself.