I'm fixing a whole set of bugs regarding weeks spanning year ends (the week year, wy, didn't get propagated correctly in all cases when converting between months, weeks, days etc).
A tricky case is this:
Calendar.ISO.Week(2008,1)->set_size(Calendar.ISO.Day());
(1) Result: Day(Mon 31 Dec 2008)
Calendar.ISO.Week(2008,1)->set_size(Calendar.ISO.Day())->format_ymd();
(2) Result: "2007-12-31"
Calendar.ISO.Week(2008,1)->set_size(Calendar.ISO.Day())->year_no();
(3) Result: 2008
(2007-12-31 is the Monday of the first week of 2008.) Note the bogus year in result (1). format_ymd however handles it correctly due to a special check of the year day, yd:
sprintf("%04d-%02d-%02d",((yd < 1)?y-1:y),m,md)
Fixing _sprintf to do the same thing means putting the same special check into YMD.year_name.
But what about year_no that returns 2008? Although the whole week belongs to 2008, I expect to get 2007 for the first day of it. Looking at it that way it seems better to ensure yd > 0 always, so that the year is normalized. However, there is obviously an intention behind the current behavior when yd can be <= 0, so that's why I ask.
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.)
Continuing my Calendar monologue..
This checkin in YMD.pike:
revision 1.16 date: 2006/01/17 15:58:29; author: mbaehr; state: Exp; lines: +1 -1 fix off by one error
is not a mere "fix" but a compatibility problem (the patch was also incomplete but that's beside the matter). Prior to this change, YMD.year and YMD.years were 0 based, i.e. [0..number_of_years()-1]. With it they become 1 based like all the other similar functions (YMD.day etc). That is imho good, but the compatibility issue is obvious.
I made a simple attempt to make 7.6 compatibility wrappers but it failed, perhaps due to the resolver tricks in the Calendar module. Shall we back out this again until someone manages to get working compat?
You can put the entire Calendar module in the compat directory and remove files until it doesn't work anymore :)
I've now fixed the week vs year/day problems that showed up in the testsuite.
Unfortunately it breaks a lot of other things. I have a more thorough fix in the making, so just take it easy.. ;)
While I was working on this I reevaluated some of the test cases I checked in. They tried to let a week "belong" to only one year, so e.g.
Calendar.ISO.Year (Calendar.ISO.Week (2008, 1))
would return only year 2008 and not a range 2007..2008. That turned out to lead to many strange inconsistencies, so I've ditched that idea.
Fixing this to behave consistently introduces some compat issues though, so I need to get something working in the compat tree. Unfortunately it's not anywhere near as simple as just copying the whole thing, like Nilsson suggested. :P
pike-devel@lists.lysator.liu.se