New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
calendar.prcal(9999) output has a problem #72440
Comments
hi Below is my env: import calendar
calendar.prcal(9999) partial output: but,when I use [print (calendar.month(9999,12))] to confirm, it`s OK . please help me to confirm. |
Thank you for your report lijp. This is interesting issue. It is not specific for Windows. itermonthdates() yields shortened sequence of dates at the end of maximal year. The formatted row of day numbers is centered. Since the number of days is less than expected, additional spaces are added at the left. |
calendar internally relies on datetime and uses datetime.date. datetime.date can't represent date more than 9999.12.31 so some logic is broken for it(normally the week list should be of length 7, date outside the month will be (0, x) pair but this is not the case for 9999.12). This won't affect prmonth since it doesn't do center formatting. We can change to that behaviour to fix this. calendar_pryear_9999 tires to fix this. |
Html calendar formats the last week differently for years 9982 and 9999. Year 9982: Year 9999: I think this issue should be fixed on lower level than text calendar formatter. |
Something like this? diff -r 15f82b64eee0 Lib/calendar.py
--- a/Lib/calendar.py Thu Sep 22 17:11:53 2016 -0700
+++ b/Lib/calendar.py Fri Sep 23 16:27:03 2016 +0800
@@ -181,6 +181,9 @@
yield (0, date.weekday())
else:
yield (date.day, date.weekday())
+ if year == 9999 and month == 12 and date.day == 31:
+ yield (0, 6)
+ yield (0, 7)
def itermonthdays(self, year, month):
""" Is there a more elegant way? I considered this but didn't quite like it so abandoned it. |
This is not just not elegant, but it doesn't work if firstweekday is not 0. The lowest level in the calendar module is itermonthdates(). But the problem is that dates outside supported range can't be represented. While the output for December of the year 9999 looks badly formatted, calendar doesn't work at all with January of the year 1 if firstweekday is not 0: >>> import calendar
>>> calendar.setfirstweekday(6)
>>> calendar.prmonth(1, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/serhiy/py/cpython-3.6/Lib/calendar.py", line 318, in prmonth
print(self.formatmonth(theyear, themonth, w, l), end=' ')
File "/home/serhiy/py/cpython-3.6/Lib/calendar.py", line 331, in formatmonth
for week in self.monthdays2calendar(theyear, themonth):
File "/home/serhiy/py/cpython-3.6/Lib/calendar.py", line 211, in monthdays2calendar
days = list(self.itermonthdays2(year, month))
File "/home/serhiy/py/cpython-3.6/Lib/calendar.py", line 179, in itermonthdays2
for date in self.itermonthdates(year, month):
File "/home/serhiy/py/cpython-3.6/Lib/calendar.py", line 162, in itermonthdates
date -= datetime.timedelta(days=days)
OverflowError: date value out of range |
How about the approach in calendar_prcal_9999_demo.patch? If it's not bad I can add tests then. |
Having additional tests is always nice. After writing tests we can search whether there is other solution. AFAIK the dummy data needs also the day attribute. |
The problem with year 1 was reported in bpo-26650. |
I would leave itermonthdates() alone and just fix itermonthdays2() (and itermonthdays() for consistency) as Xiang suggested. The fix can be implemented by breaking on date.month != month and adding something like for wd in range(date.weekday(), 7):
yield (0, wd) after the existing for loop. |
On the second thought, I don't see why itermonthdays2() and itermonthdays() need to use itermonthdates() at all. It looks like it is easy to implement those using monthrange() and some simple integer arithmetics. |
itermonthdates() is documented public method. We should do something with it. Maybe emitting dummy data instances is the simplest way to solve this issue. |
Note that the stop on date.max behavior was introduced in bpo-15421. |
The current documentation is an impossibility: "The iterator will yield datetime.date values and will always iterate through complete weeks, so it will yield dates outside the specified The current implementation deals with this impossibility differently for months (9999, 12) and (1, 1). In the first case, the iterators stops on an out of bounds date: >>> list(calendar.Calendar().itermonthdates(9999, 12))[-1]
datetime.date(9999, 12, 31)
>>> list(calendar.Calendar().itermonthdates(9999, 12))[-1].weekday()
4 but in the second, it raises the OverflowError: >>> next(calendar.Calendar(1).itermonthdates(1, 1))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "calendar.py", line 160, in itermonthdates
date -= datetime.timedelta(days=days)
OverflowError: date value out of range Returning dummy instances instead of datetime.date in these cases will only make debugging harder for the users of .itermonthdates(). Sooner or later they would want to do something the returned value that the dummy won't support. If you are willing to sacrifice the "will yield datetime.date values" for "will always iterate through complete weeks", I would make it yield None for out of bounds values and require the caller to deal with this possibility right away. A better solution would be to simply raise OverflowError whenever the range of itermonthdates() does not fit within [date.min, date.max]. |
Yes, the current documentation is an impossibility (unless we remove all date limits that is much harder issue). Raisin OverflowError will make the implementation of itermonthdays2() and itermonthdays() more complex. Yielding dummy instances or None requires rewriting user code (less with a dummy instance if we are lucky). I agree that in long perspective yielding None looks better. |
I am attaching the proposed reimplementation of itermonthdays2() and itermonthdays(). It does not use itermonthdates() and is not that complicated. It passes test_calendar, but I did not test it further than that. I would rather not mess up with itermonthdates(), particularly in a bugfix release. We can postpone the discussion of a better way to handle date over/underflow in itermonthdates() until 3.7. |
I don't think this is too hard. I think the original implementation did not have date limits. I've opened a separate issue for this. See bpo-28281. |
bpo-28253-3.diff uses itertools.repeat(). |
Patch LGTM.
Before finally find a better way, can we at least make the two extreme cases behaviours consistent? Both emitting an exception or a shorter list. |
The patch LGTM except tests. But we should at least document the behavior of itermonthdates(), monthdatescalendar() and yeardatescalendar() at corner cases. |
What are your issues with the tests? Did you see the -4 patch?
I would rather not. At least not before we make under and overflow behavior consistent. Frankly, I don't see why anyone would want to use these iterators. |
Something went wrong with bpo-28253-4.diff. I'll investigate and replace. |
bpo-28253-4.diff should be good now. |
New changeset c439bce36bf2 by Alexander Belopolsky in branch '3.5': New changeset cd384c4b441a by Alexander Belopolsky in branch '3.6': New changeset bc285a9ecc58 by Alexander Belopolsky in branch 'default': |
New changeset 7efba48299e9 by Alexander Belopolsky in branch 'default': New changeset 55f11196c949 by Alexander Belopolsky in branch '3.5': New changeset 1f1a085f533f by Alexander Belopolsky in branch '3.6': |
New changeset f2aff898f7c8 by Alexander Belopolsky in branch '2.7': |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: