I suspect the reason for the non-normalized year is to let y == wy in the Week class, so that e.g. Week(2008,1)->year_no() doesn't return 2007.
However, a lot more special cases are necessary in the YMD class to handle the non-normalized form correctly everywhere, so it still seems better to avoid it and override selected functions such as year_no in the Week class instead.
There are cases that are awkward regardless, datetime in particular:
Calendar.ISO.Week(2008,1)->datetime();
(1) Result: ([ /* 13 elements */ "day": 31, "fraction": 0.0, "hour": 0, "julian": 2454466, "minute": 0, "month": 12, "second": 0, "timezone": -3600, "unix": 1199055600, "week": 1, "week_day": 1, "year": 2008, "yearday": -1 ])
This looks right if one only index out "year", or "year" and "week", or perhaps "year" and "yearday". It looks very wrong if one index out "year", "month" and "day".
I can see only two reasonable ways to handle it: One is to define datetime to apply to the first moment of the period and let "year" be 2007 ("week" would then have to be 53 to compensate). The other is to compensate with an odd "day" instead, i.e. "month" would be 1 and "day" zero or negative. I'd prefer the first alternative.
Another thing regarding year spanning weeks is when they are converted and broken up:
Calendar.ISO.Day (Calendar.ISO.Week(2008,1))->years();
(1) Result: ({ /* 2 elements */ Year(2007), Year(2008) })
Calendar.ISO.Month (Calendar.ISO.Week(2008,1))->years();
(2) Result: ({ /* 2 elements */ Year(2007), Year(2008) })
Calendar.ISO.Year (Calendar.ISO.Week(2008,1))->years();
(3) Result: ({ /* 1 element */ Year(2007) })
Calendar.ISO.Week(2008,1)->years();
(4) Result: ({ /* 1 element */ Year(2008) })
Wouldn't it be more logical if results (3) and (4) also listed both years, as one gets when the week is converted to day or month ranges? (Result (3) is clearly buggy regardless.)