JDK-8293146 : Strict DateTimeFormatter fails to report an invalid week 53
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.time
  • Affected Version: 8,11,17,18,19,20
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2022-08-29
  • Updated: 2022-09-12
  • Resolved: 2022-09-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 20
20 b14Fixed
Description
ADDITIONAL SYSTEM INFORMATION :
macOS Sierra 10.12.6
openjdk 20-ea 2023-03-21
OpenJDK Runtime Environment (build 20-ea+12-790)
OpenJDK 64-Bit Server VM (build 20-ea+12-790, mixed mode, sharing)
SHELL=/bin/bash
HOSTTYPE=x86_64
OSTYPE=darwin16
MACHTYPE=x86_64-apple-darwin16

The same undesired and unexpected behaviour has also been observed on Java 8, Java 11 and Java 17.

A DESCRIPTION OF THE PROBLEM :
We are using a DateTimeFormatter for parsing week-based-year and week number. Using resolver style ResolverStyle.STRICT. Trying to parse week number 53 in a week-based year having only 52 weeks.
Expected and desired behaviour: Since the week number is invalid, Java should throw a DateTimeParseException.
Observed behaviour: Java parses as though the string had said week 52.

The undesired behaviour is observed both with ISO and non-ISO week schemes, in fact with all available locales in Java 17.
This bug report comes out of the following Stack Overflow question: https://stackoverflow.com/questions/73498610/java-week-to-date-conversion-for-us-calendar-non-iso8601

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the included Java program.
Also online here: https://rextester.com/NIDEB77126


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
DateTimeParseException from the line LocalDate date = LocalDate.parse(weekDateString, localizedWeekDateFormatter);
ACTUAL -
Locale:         it_CH
Week fields:    WeekFields[MONDAY,4]
String:         2022.53.1
Parsed:         2022-12-26
Formatted back: 2022.52.1

Locale:         en_SL
Week fields:    WeekFields[MONDAY,1]
String:         2022.53.1
Parsed:         2022-12-19
Formatted back: 2022.52.1

Locale:         teo_KE
Week fields:    WeekFields[SUNDAY,1]
String:         2021.53.1
Parsed:         2021-12-19
Formatted back: 2021.52.1

As expected: java.time.format.DateTimeParseException: Text '2021-W53-1' could not be parsed: Invalid value for WeekOfWeekBasedYear (valid values 1 - 52): 53

---------- BEGIN SOURCE ----------
import java.util.Locale;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.WeekFields;

class Rextester
{  
    public static void main(String args[])
    {
		// ISO weeks locale
		Locale iso = Locale.forLanguageTag("it-CH");
		testAndPrint(iso, "2022.53.1");

		// Non-ISO weeks locale
		Locale nonIso = Locale.forLanguageTag("en-SL");
		testAndPrint(nonIso, "2022.53.1");

		// Another non-ISO weeks locale
		Locale anotherNonIso = Locale.forLanguageTag("teo-KE");
		testAndPrint(anotherNonIso, "2021.53.1");
		
		try {
			// The following line gives the expected exception
			// java.time.format.DateTimeParseException: Text '2021-W53-1' could not be parsed: Invalid value for WeekOfWeekBasedYear (valid values 1 - 52): 53
			LocalDate.parse("2021-W53-1", DateTimeFormatter.ISO_WEEK_DATE);
		} catch (DateTimeParseException dtpe) {
			System.out.println("As expected: " + dtpe);
		}
    }

	private static void testAndPrint(Locale localeInQuestion, String weekDateString) {
		DateTimeFormatter localizedWeekDateFormatter 
				= DateTimeFormatter.ofPattern("YYYY.ww.e", localeInQuestion)
						.withResolverStyle(ResolverStyle.STRICT);
		// We are expecting a DateTimeParseException from the following line but not getting any.
		LocalDate date = LocalDate.parse(weekDateString, localizedWeekDateFormatter);
		
		System.out.format("Locale:         %s%n", localeInQuestion);
		System.out.format("Week fields:    %s%n", WeekFields.of(localeInQuestion));
		System.out.format("String:         %s%n", weekDateString);
		System.out.format("Parsed:         %s%n", date);
		System.out.format("Formatted back: %s%n", date.format(localizedWeekDateFormatter));
		System.out.println();
	}

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
One may format the obtained date back into a string and compare with the string originally parsed. If they differ, the string did not represent the date parsed. Then throw the desired exception (or take other desired action).

FREQUENCY : always



Comments
Changeset: 32c7b628 Author: Naoto Sato <naoto@openjdk.org> Date: 2022-09-07 18:33:37 +0000 URL: https://git.openjdk.org/jdk/commit/32c7b6283daf6f3876ff62693d5a0cb7c4af4232
07-09-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/10184 Date: 2022-09-06 17:30:27 +0000
06-09-2022

Using `WeekFields` instead of `IsoFields` may be OK, because Iso restricts firstDayOfWeek and minimalDaysInFirstWeek to Mon/4. So DTF.ofPattern() may result in different fdow/mdfw. However, still WeekFields' maximum week handling seems incorrect, as in: ``` jshell> var f = DateTimeFormatter.ofPattern("YYYY-'W'ww-e", Locale.UK).withResolverStyle(ResolverStyle.STRICT) f ==> Localized(WeekBasedYear,4,19,EXCEEDS_PAD)'-''W'Lo ... )'-'Localized(DayOfWeek,1) jshell> LocalDate.parse("2021-W52-1", f) $193 ==> 2021-12-27 jshell> LocalDate.parse("2021-W53-1", f) $194 ==> 2021-12-27 ``` whereas ``` jshell> LocalDate.parse("2021-W52-1", DateTimeFormatter.ISO_WEEK_DATE) $195 ==> 2021-12-27 jshell> LocalDate.parse("2021-W53-1", DateTimeFormatter.ISO_WEEK_DATE) | Exception java.time.format.DateTimeParseException: Text '2021-W53-1' could not be parsed: Invalid value for WeekOfWeekBasedYear (valid values 1 - 52): 53 | at DateTimeFormatter.createError (DateTimeFormatter.java:2023) | at DateTimeFormatter.parse (DateTimeFormatter.java:1958) | at LocalDate.parse (LocalDate.java:430) | at (#196:1) | Caused by: java.time.DateTimeException: Invalid value for WeekOfWeekBasedYear (valid values 1 - 52): 53 | at ValueRange.checkValidValue (ValueRange.java:319) | at IsoFields$Field$3.resolve (IsoFields.java:504) | at IsoFields$Field$3.resolve (IsoFields.java:429) | at Parsed.resolveFields (Parsed.java:283) | at Parsed.resolve (Parsed.java:259) | at DateTimeParseContext.toResolved (DateTimeParseContext.java:331) | at DateTimeFormatter.parseResolved0 (DateTimeFormatter.java:2058) | at DateTimeFormatter.parse (DateTimeFormatter.java:1954) | ... ```
01-09-2022

While `DateTimeFormatter.ISO_WEEK_DATE` calls `resolve()` method in `IsoFields.WEEK_OF_WEEK_BASED_YEAR` where it correctly validates 52/53 max value, `DateTimeFormatter.ofPattern(...)` uses `WeekFields.weekOfWeekBasedYear()` where it simply compares the parsed week with max value (=53) which is inconsistent with `IsoFields`.
31-08-2022

The observations on Windows 10: JDK 8: Failed, DateTimeParseException observed. JDK 11: Failed. JDK 17: Failed. JDK 18: Failed. JDK 19ea+32: Failed. JDK 20ea+4: Failed.
31-08-2022