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.
YearWeek
For 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:
YearMonth
For 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