-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
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
Closes issue bpo-5288: Allow tzinfo objects with sub-minute offsets. #2896
Merged
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a1ce3d8
Closes issue bpo-5288: Allow tzinfo objects with sub-minute offsets.
abalkin 6b03831
bpo-5288: Implemented %z formatting of sub-minute offsets.
abalkin 97d8d5b
bpo-5288: Removed mentions of the whole minute limitation on TZ offsets.
abalkin d63e40a
bpo-5288: Removed one more mention of the whole minute limitation.
abalkin 1ed6be2
Fix a formatting error in the docs
abalkin 1b7677d
Addressed review comments.
abalkin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -206,10 +206,16 @@ def _wrap_strftime(object, format, timetuple): | |
if offset.days < 0: | ||
offset = -offset | ||
sign = '-' | ||
h, m = divmod(offset, timedelta(hours=1)) | ||
assert not m % timedelta(minutes=1), "whole minute" | ||
m //= timedelta(minutes=1) | ||
zreplace = '%c%02d%02d' % (sign, h, m) | ||
h, rest = divmod(offset, timedelta(hours=1)) | ||
m, rest = divmod(rest, timedelta(minutes=1)) | ||
s = rest.seconds | ||
u = offset.microseconds | ||
if u: | ||
zreplace = '%c%02d%02d%02d.%06d' % (sign, h, m, s, u) | ||
elif s: | ||
zreplace = '%c%02d%02d%02d' % (sign, h, m, s) | ||
else: | ||
zreplace = '%c%02d%02d' % (sign, h, m) | ||
assert '%' not in zreplace | ||
newformat.append(zreplace) | ||
elif ch == 'Z': | ||
|
@@ -241,7 +247,7 @@ def _check_tzname(name): | |
# offset is what it returned. | ||
# If offset isn't None or timedelta, raises TypeError. | ||
# If offset is None, returns None. | ||
# Else offset is checked for being in range, and a whole # of minutes. | ||
# Else offset is checked for being in range. | ||
# If it is, its integer value is returned. Else ValueError is raised. | ||
def _check_utc_offset(name, offset): | ||
assert name in ("utcoffset", "dst") | ||
|
@@ -250,9 +256,6 @@ def _check_utc_offset(name, offset): | |
if not isinstance(offset, timedelta): | ||
raise TypeError("tzinfo.%s() must return None " | ||
"or timedelta, not '%s'" % (name, type(offset))) | ||
if offset.microseconds: | ||
raise ValueError("tzinfo.%s() must return a whole number " | ||
"of seconds, got %s" % (name, offset)) | ||
if not -timedelta(1) < offset < timedelta(1): | ||
raise ValueError("%s()=%s, must be strictly between " | ||
"-timedelta(hours=24) and timedelta(hours=24)" % | ||
|
@@ -960,11 +963,11 @@ def tzname(self, dt): | |
raise NotImplementedError("tzinfo subclass must override tzname()") | ||
|
||
def utcoffset(self, dt): | ||
"datetime -> minutes east of UTC (negative for west of UTC)" | ||
"datetime -> timedelta, positive for east of UTC, negative for west of UTC" | ||
raise NotImplementedError("tzinfo subclass must override utcoffset()") | ||
|
||
def dst(self, dt): | ||
"""datetime -> DST offset in minutes east of UTC. | ||
"""datetime -> DST offset as timedelta, positive for east of UTC. | ||
|
||
Return 0 if DST not in effect. utcoffset() must include the DST | ||
offset. | ||
|
@@ -1262,8 +1265,8 @@ def __format__(self, fmt): | |
# Timezone functions | ||
|
||
def utcoffset(self): | ||
"""Return the timezone offset in minutes east of UTC (negative west of | ||
UTC).""" | ||
"""Return the timezone offset as timedelta, positive east of UTC | ||
(negative west of UTC).""" | ||
if self._tzinfo is None: | ||
return None | ||
offset = self._tzinfo.utcoffset(None) | ||
|
@@ -1284,8 +1287,8 @@ def tzname(self): | |
return name | ||
|
||
def dst(self): | ||
"""Return 0 if DST is not in effect, or the DST offset (in minutes | ||
eastward) if DST is in effect. | ||
"""Return 0 if DST is not in effect, or the DST offset (as timedelta | ||
positive eastward) if DST is in effect. | ||
|
||
This is purely informational; the DST offset has already been added to | ||
the UTC offset returned by utcoffset() if applicable, so there's no | ||
|
@@ -1714,7 +1717,7 @@ def strptime(cls, date_string, format): | |
return _strptime._strptime_datetime(cls, date_string, format) | ||
|
||
def utcoffset(self): | ||
"""Return the timezone offset in minutes east of UTC (negative west of | ||
"""Return the timezone offset as timedelta positive east of UTC (negative west of | ||
UTC).""" | ||
if self._tzinfo is None: | ||
return None | ||
|
@@ -1736,8 +1739,8 @@ def tzname(self): | |
return name | ||
|
||
def dst(self): | ||
"""Return 0 if DST is not in effect, or the DST offset (in minutes | ||
eastward) if DST is in effect. | ||
"""Return 0 if DST is not in effect, or the DST offset (as timedelta | ||
positive eastward) if DST is in effect. | ||
|
||
This is purely informational; the DST offset has already been added to | ||
the UTC offset returned by utcoffset() if applicable, so there's no | ||
|
@@ -1962,9 +1965,6 @@ def __new__(cls, offset, name=_Omitted): | |
raise ValueError("offset must be a timedelta " | ||
"strictly between -timedelta(hours=24) and " | ||
"timedelta(hours=24).") | ||
if (offset.microseconds != 0 or offset.seconds % 60 != 0): | ||
raise ValueError("offset must be a timedelta " | ||
"representing a whole number of minutes") | ||
return cls._create(offset, name) | ||
|
||
@classmethod | ||
|
@@ -2053,7 +2053,15 @@ def _name_from_offset(delta): | |
else: | ||
sign = '+' | ||
hours, rest = divmod(delta, timedelta(hours=1)) | ||
minutes = rest // timedelta(minutes=1) | ||
minutes, rest = divmod(rest, timedelta(minutes=1)) | ||
seconds = rest.seconds | ||
microseconds = rest.microseconds | ||
if microseconds: | ||
return 'UTC{}{:02d}:{:02d}:{:02d}.{:06d}'.format(sign, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may use f-string here ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
hours, minutes, seconds, microseconds) | ||
if seconds: | ||
return 'UTC{}{:02d}:{:02d}:{:02d}'.format(sign, hours, | ||
minutes, seconds) | ||
return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) | ||
|
||
timezone.utc = timezone._create(timedelta(0)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
Misc/NEWS.d/next/Library/2017-07-26-13-18-29.bpo-5288.o_xEGj.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Support tzinfo objects with sub-minute offsets. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to be more explicit: mention that optional support for seconds was added, since microseconds are not supported here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, microseconds are supported here. Will larigy.