I store in a database:
yyyyWww, that is 2023W10 is 10th week of 20232023M10 meaning October 2023I can use yyyy'W'ww and yyyy'M'MM formats in SimpleDateFormat.
DateTimeFormatter gives DateTimeParseException in these cases.  Seems DateTimeFormatter will only accept a complete date (year+month+day) with limited text (dash, slash, period)
What am I missing here?
I have tried yyyy'W'ww and yyyy'M'MM formats in dateTimeFormatter, both give parseexceptions.
In your specific cases, you could easily use string manipulation rather than DateTimeFormatter.
org.threeten.extra.YearWeek.parse( "2023W31".replace ( "W" , "-W" ) )
… and:
java.time.YearMonth.parse( "2023M07".replace( "M" , "-" ) )
Even better: Change your data to use the standard ISO 8601 format.
What am I missing here?
You are missing the appropriate class.
YearWeekFor your first case, a week of the year, use the org.threeten.extra.YearWeek class from the library ThreeTen-Extra. This library adds functionality to the java.time classes built into Java 8+, defined in JSR 310.
The YearWeek class uses standard  ISO 8601 formats when parsing/generating text. So you need not specify a formatting pattern.
Your input text complies with the abbreviated style of the ISO 8601 standard, but not the preferred expanded style. To comply with the expanded style, insert a hyphen, input.replace( "W" , "-W" ).
String input = "2023W31".replace ( "W" , "-W" );
YearWeek yearWeek = YearWeek.parse ( input );
System.out.println ( "yearWeek = " + yearWeek );
yearWeek = 2023-W31
Use this YearWeek class only if your definition of “week” agrees with the ISO 8601 standard definition:
YearMonthFor your second case, use the java.time.YearMonth class built into Java 8+.
The standard ISO 8601 format for a year and month is YYYY-MM. I recommend you use this standard format in your stored values in the database.
YearMonth yearMonth = YearMonth.parse( "2023-07" ) ;
If you insist on using your custom format, you might be able to define a DateTimeFormatter to specify your particular formatting pattern. But I would just replace the M with a hyphen.
YearMonth yearMonth = YearMonth.parse( "2023M07".replace( "M" , "-" ) ) ;
yearMonth = 2023-07
You will need to use a DateTimeFormatterBuilder to apply defaults along with your pattern.
A simple approach would be to declare all known formats ahead of time and loop through them. If you want to eliminate the looping, you could check if the string contains a "W" and call the week parser, or an "M" and call the month parser.
Please note that YYYY (upper-case) denotes a week-based year, vs yyyy (lower-case) an era (normal 365 day year).
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
class DateParsing {
    private static final DateTimeFormatter
        PARSE_WEEK = new DateTimeFormatterBuilder()
            .appendPattern("YYYY'W'ww")
            .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
            .toFormatter(),
        PARSE_MONTH = new DateTimeFormatterBuilder()
            .appendPattern("yyyy'M'MM")
            .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
            .toFormatter();
    private static final DateTimeFormatter[] PATTERNS = { PARSE_WEEK, PARSE_MONTH };
    
    public static LocalDate parseDate(String input) throws IllegalArgumentException {
        for (DateTimeFormatter pattern : PATTERNS) {
            try {
                return LocalDate.parse(input, pattern);
            } catch (DateTimeParseException e) {
                // System.err.println("Error: " + e.getMessage());
            }
        }
        throw new IllegalArgumentException(String.format("Unknown format: %s", input));
    }
    
    public static void main(String[] args) throws IllegalArgumentException {
        String[] timestamps = {
            "2023W10", // 10th week of 2023
            "2023M10"  // October 1, 2023
        };
        for (String timestamp : timestamps) {
            System.out.printf(">>> %s => %s%n", timestamp, parseDate(timestamp));
        }
    }
}
Output:
>>> 2023W10 => 2023-03-05
>>> 2023M10 => 2023-10-01
If you want to remove the loop and exception handling, you could check to see if the input string contains an "M" or "W" as explained above.
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
class DateParsing {
    private static final DateTimeFormatter
        PARSE_WEEK = new DateTimeFormatterBuilder()
            .appendPattern("YYYY'W'ww")
            .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
            .toFormatter(),
        PARSE_MONTH = new DateTimeFormatterBuilder()
            .appendPattern("yyyy'M'MM")
            .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
            .toFormatter();
    public static LocalDate parseDate(String input) throws DateTimeParseException, IllegalArgumentException {
        if (input.contains("M")) {
            return LocalDate.parse(input, PARSE_MONTH);
        }
        if (input.contains("W")) {
            return LocalDate.parse(input, PARSE_WEEK);
        }
        throw new IllegalArgumentException(String.format("Unknown format: %s", input));
    }
    
    public static void main(String[] args) throws DateTimeParseException, IllegalArgumentException {
        String[] timestamps = {
            "2023W10", // 10th week of 2023
            "2023M10"  // October 1, 2023
        };
        for (String timestamp : timestamps) {
            System.out.printf(">>> %s => %s%n", timestamp, parseDate(timestamp));
        }
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With