parseIsoWeekString function
Parses ISO week string "2026-W09" to the Monday of that week. Audited: 2026-06-12 11:26 EDT
Implementation
DateTime? parseIsoWeekString(String s) {
final RegExp re = RegExp(r'^(\d{4})-W(\d{2})$');
final RegExpMatch? m = re.firstMatch(s.trim());
if (m == null) return null;
final yearGroup = m.group(1);
final weekGroup = m.group(2);
if (yearGroup == null || weekGroup == null) return null;
final int? yearVal = int.tryParse(yearGroup);
final int? weekVal = int.tryParse(weekGroup);
if (yearVal == null || weekVal == null || weekVal < 1 || weekVal > 53) return null;
// ISO-8601: week 1 is always the week containing Jan 4 (equivalently, the week
// with the year's first Thursday). Anchoring on Jan 4 sidesteps the edge case
// where Jan 1 falls in the final week of the previous year.
final DateTime jan4 = DateTime(yearVal, 1, 4);
final int jan4Weekday = jan4.weekday;
// Step back to that week's Monday (weekday Mon=1), then add whole weeks.
// Day overflow past month/year end is handled by DateTime's normalization.
final DateTime monday = DateTime(yearVal, 1, 4 - (jan4Weekday - 1));
final DateTime targetMonday = DateTime(monday.year, monday.month, monday.day + (weekVal - 1) * 7);
// Reject a week number the year does not have (e.g. 2025-W53 — 2025 has only
// 52 ISO weeks). The ISO week-YEAR of any week is the calendar year of that
// week's Thursday; if the Thursday is not in the requested year, the week is
// not part of it. A previous year-of-Monday check missed 2025-W53, whose
// Monday 2025-12-29 stays in 2025 but whose Thursday lands in 2026.
final DateTime thursday = DateTime(targetMonday.year, targetMonday.month, targetMonday.day + 3);
if (thursday.year != yearVal) return null;
return targetMonday;
}