Closed abalkin closed 7 years ago
The calendar module currently relies on datetime for some calculations and is therefore restricted to years 1 through 9999. With exception to some public methods that are documented to return datetime.date instances, this dependence on the datetime module can be removed.
I don't think that's necessary. What's the use case for this?
And if we want to to this, wouldn't it be better to enhance datetime, so that this use case is supported too?
What's the use case for this?
Why did George Mallory climb Mount Everest? "Because it's there."
We have two open issues complaining about calendar behavior for years 1 and 9999: bpo-28253 and bpo-26650. There is also a closed issue bpo-15421 that attempted to fix an overflow in the year 9999 only to introduce a silent error.
I don't think there are use cases beyond educational (demonstrating calendar periodicity?) or testing, but since several users bothered to report edge case bugs, it is probably easier to remove the limits than to properly document them.
Fortunately, extending calendar to arbitrary years does not require climbing Mount Everest. Since the proleptic calendar repeats every 400 years, all we need (assuming bpo-28253 patch is applied) is to fix the weekday() function as follows:
def weekday(year, month, day):
"""Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
day (1-31)."""
+ if not 0 < year < 10000:
+ year = 2000 + year % 400
return datetime.date(year, month, day).weekday()
$ ./python.exe -mcalendar 40000 12
December 40000
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
$ ./python.exe -mcalendar -104 2
February -104
Mo Tu We Th Fr Sa Su
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29
We can also treat negative years "properly" so that year 1 is preceded by year -1, but I don't think there is any value in this.
I think data range is limited by year 1 due to ambiguity of year -1. Is it 1 B.D. or 2 years before year 1 (2 B.D.)?
Under my proposal year=-1 is 2 B.C. and year 0 is a leap year
$ ./python.exe -mcalendar 0 2
February 0
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29
This is the simplest extension and people who require other variants can make their own adjustments. (This suggests that we should probably bring weekday(), isleap(), etc. methods to the Calendar class to allow users to override them.)
Under my proposal year=-1 is 2 B.C. and year 0 is a leap year
We should refuse the temptation to guess (i.e. make up our own arbitrary interpretations). Instead, just document the known limits.
The proposed interpretation of nonpositive years is not arbitrary, it is a natural extension of the same formulas that we use for positive years. On the other hand 1-9999 limits are arbitrary. If we wanted to restrict calendars to 4-digit years we would limit years to the 1000-9999 range.
Note that interpreting year 0 as 1 BCE is not without a precedent. ISO 8601 standard does exactly that with the caveat that "values in the range [0000] through [1582] shall only be used by mutual agreement of the partners in information interchange." Furthermore, "an expanded year representation [±YYYYY] must have an agreed-upon number of extra year digits beyond the four-digit minimum, and it must be prefixed with a + or − sign instead of the more common AD/BC (or BCE/CE) notation; by convention 1 BC is labelled +0000, 2 BC is labeled -0001, and so on." See \https://en.wikipedia.org/wiki/ISO_8601#Years\ citing ISO 8601:2004 sections 3.4.2, 4.1.2.4 and Annex B.1.1.
Furthermore, documenting the existing limits is not an easy task. For example
>>> from calendar import *
>>> cal = TextCalendar(1)
>>> cal.prmonth(1, 1)
January 1
Tu We Th Fr Sa Su Mo
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
displays 6 days in December of 1 BCE as blanks. Similarly itermonthdays() and itermonthdays2() generators yield objects corresponding to these days. Are these days within the calendar limits or not?
Note that I am not proposing to extend the range of the datetime.date objects. Doing that would be a much more difficult task, but defining calendars for out of range years as those for year % 400 is trivial and much easier than to figure out and document the edge behaviors.
Okay, if this is specified by ISO 8601 standard, I think we can extend calendar below year 1. But the meaning of non-positive years should be documented. And maybe even provide a way to customize the representation of year (this is a separate issue).
Standard Unix utilities cal and ncal support only years in range 1..9999.
First time patch for CPython. I followed instructions given by belopolsky and added tests. Please critique.
Who can merge this patch ?
Alexander as core developer and the creator of this issue can merge the patch after making his review. But first we should make a decision whether it is worth to do at all. I'm +0 now. Raymond seems has objections.
The patch should include an update to documentation.
$ cal 9 1752
September 1752
Su Mo Tu We Th Fr Sa
1 2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
but
$ python3 -mcalendar 1752 9
September 1752
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30
We should explain that while the calendar module relies on datetime, it implements an infinite calendar with a period of 400 years.
A reference should be made to ISO 8601 for our treatment of nonpositive years.
Given ISO 8601 and the simplicity of this change, I don't think Raymond will insist that we continue imposing datetime-like limits, but I would like to give him a chance to renew his objection once the no-limits calendar is documented.
Hello Mark,
Would you be able to prepare a pull request on GitHub for your patch?
Thanks!
I submitted Mark's patch unchanged as PR 4109. If we don't hear from Mark, I will address my own comments and merge.
New changeset 66c88ce30ca2b23daa37038e1a3c0de98f241f50 by Alexander Belopolsky in branch 'master': Closes bpo-28281: Remove year (1-9999) limits on the weekday() function. (bpo-4109) https://github.com/python/cpython/commit/66c88ce30ca2b23daa37038e1a3c0de98f241f50
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: ```python assignee = 'https://github.com/abalkin' closed_at =
created_at =
labels = ['easy', 'type-feature', '3.7']
title = 'Remove year limits from calendar'
updated_at =
user = 'https://github.com/abalkin'
```
bugs.python.org fields:
```python
activity =
actor = 'belopolsky'
assignee = 'belopolsky'
closed = True
closed_date =
closer = 'belopolsky'
components = []
creation =
creator = 'belopolsky'
dependencies = ['28253']
files = ['44917']
hgrepos = []
issue_num = 28281
keywords = ['patch', 'easy']
message_count = 15.0
messages = ['277467', '277503', '277526', '277541', '277544', '277634', '277641', '277661', '277835', '279136', '279225', '279228', '304891', '304933', '305085']
nosy_count = 9.0
nosy_names = ['doerwalter', 'rhettinger', 'belopolsky', 'serhiy.storchaka', 'matrixise', 'xiang.zhang', 'Mariatta', 'golly', 'cheryl.sabella']
pr_nums = ['4109']
priority = 'normal'
resolution = 'fixed'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue28281'
versions = ['Python 3.7']
```