Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(11)

Delta Between Two Patch Sets: Lib/datetime.py

Issue 15873: "datetime" cannot parse ISO 8601 dates and times
Left Patch Set: Created 3 years, 8 months ago
Right Patch Set: Created 3 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | Lib/test/datetimetester.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 """Concrete date/time and related types. 1 """Concrete date/time and related types.
2 2
3 See http://www.iana.org/time-zones/repository/tz-link.html for 3 See http://www.iana.org/time-zones/repository/tz-link.html for
4 time zone and DST data sources. 4 time zone and DST data sources.
5 """ 5 """
6 6
7 import time as _time 7 import time as _time
8 import math as _math 8 import math as _math
9 import re 9 import re
10 10
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 greater_than_half = r > b if b > 0 else r < b 328 greater_than_half = r > b if b > 0 else r < b
329 if greater_than_half or r == b and q % 2 == 1: 329 if greater_than_half or r == b and q % 2 == 1:
330 q += 1 330 q += 1
331 331
332 return q 332 return q
333 333
334 334
335 def _parse_isotime(cls, isostring): 335 def _parse_isotime(cls, isostring):
336 match = cls._isore.match(isostring) 336 match = cls._isore.match(isostring)
337 if not match: 337 if not match:
338 raise ValueError("invalid RFC 3339 %s string: %r" % (cls.__name__, isost ring)) 338 raise ValueError("invalid RFC 3339 %s string: %r"
SilentGhost 2016/08/05 13:22:36 Please, wrap this line to 80 chars.
deronnax 2016/08/05 14:09:51 done
339 % (cls.__name__, isostring))
339 kw = match.groupdict() 340 kw = match.groupdict()
340 tzinfo = kw.pop('tzinfo', None) 341 tzinfo = kw.pop('tzinfo', None)
341 if tzinfo == 'Z' or tzinfo == 'z': 342 if tzinfo == 'Z' or tzinfo == 'z':
342 tzinfo = timezone.utc 343 tzinfo = timezone.utc
343 elif tzinfo is not None: 344 elif tzinfo is not None:
344 offset_hours, _, offset_mins = tzinfo[1:].partition(':') 345 offset_hours, _, offset_mins = tzinfo[1:].partition(':')
345 offset = timedelta(hours=int(offset_hours), minutes=int(offset_mins)) 346 offset = timedelta(hours=int(offset_hours), minutes=int(offset_mins))
346 if tzinfo[0] == '-': 347 if tzinfo[0] == '-':
347 offset = -offset 348 offset = -offset
348 tzinfo = timezone(offset) 349 tzinfo = timezone(offset)
349 us = kw.pop('microsecond', None) 350 us = kw.pop('microsecond', None)
350 kw = {k: int(v) for k, v in kw.items() if v is not None} 351 kw = {k: int(v) for k, v in kw.items()}
SilentGhost 2016/08/05 13:22:36 It shouldn't be possible that v would be None at t
deronnax 2016/08/05 14:09:51 you're right
351 if us: 352 if us:
352 us = round(float(us), 6) 353 us = round(float(us), 6)
353 kw['microsecond'] = int(us * 1e6) 354 kw['microsecond'] = int(us * 1e6)
354 if tzinfo: 355 if tzinfo:
355 kw['tzinfo'] = tzinfo 356 kw['tzinfo'] = tzinfo
356 return cls(**kw) 357 return cls(**kw)
357 358
358 359
359 class timedelta: 360 class timedelta:
360 """Represent the difference between two datetime objects. 361 """Represent the difference between two datetime objects.
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
671 return (self._days, self._seconds, self._microseconds) 672 return (self._days, self._seconds, self._microseconds)
672 673
673 def __reduce__(self): 674 def __reduce__(self):
674 return (self.__class__, self._getstate()) 675 return (self.__class__, self._getstate())
675 676
676 timedelta.min = timedelta(-999999999) 677 timedelta.min = timedelta(-999999999)
677 timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, 678 timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
678 microseconds=999999) 679 microseconds=999999)
679 timedelta.resolution = timedelta(microseconds=1) 680 timedelta.resolution = timedelta(microseconds=1)
680 681
681 _DATE_RE = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', re.AS CII)
682
683 class date: 682 class date:
684 """Concrete date type. 683 """Concrete date type.
685 684
686 Constructors: 685 Constructors:
687 686
688 __new__() 687 __new__()
689 fromtimestamp() 688 fromtimestamp()
690 today() 689 today()
691 fromordinal() 690 fromordinal()
692 691
(...skipping 10 matching lines...) Expand all
703 weekday() 702 weekday()
704 isoweekday(), isocalendar(), isoformat() 703 isoweekday(), isocalendar(), isoformat()
705 ctime() 704 ctime()
706 strftime() 705 strftime()
707 706
708 Properties (readonly): 707 Properties (readonly):
709 year, month, day 708 year, month, day
710 """ 709 """
711 __slots__ = '_year', '_month', '_day', '_hashcode' 710 __slots__ = '_year', '_month', '_day', '_hashcode'
712 711
713 _isore = _DATE_RE 712 _isore = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', re. ASCII)
714 713
715 def __new__(cls, year, month=None, day=None): 714 def __new__(cls, year, month=None, day=None):
716 """Constructor. 715 """Constructor.
717 716
718 Arguments: 717 Arguments:
719 718
720 year, month, day (required, base 1) 719 year, month, day (required, base 1)
721 """ 720 """
722 if month is None and isinstance(year, bytes) and len(year) == 4 and \ 721 if month is None and isinstance(year, bytes) and len(year) == 4 and \
723 1 <= year[2] <= 12: 722 1 <= year[2] <= 12:
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
1048 state = getstate() 1047 state = getstate()
1049 else: 1048 else:
1050 state = getattr(self, "__dict__", None) or None 1049 state = getattr(self, "__dict__", None) or None
1051 if state is None: 1050 if state is None:
1052 return (self.__class__, args) 1051 return (self.__class__, args)
1053 else: 1052 else:
1054 return (self.__class__, args, state) 1053 return (self.__class__, args, state)
1055 1054
1056 _tzinfo_class = tzinfo 1055 _tzinfo_class = tzinfo
1057 1056
1058 _TIME_RE = re.compile(r'(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})'
1059 r'(?P<microsecond>\.\d*)?(?P<tzinfo>Z|([+-]\d{2}:\d{2}))?$' ,
SilentGhost 2016/08/05 13:22:36 The same issue as in alternative patch: \d* matche
deronnax 2016/08/05 14:09:51 Oh yeah, well seen. Fixed. I added a test to check
1060 re.ASCII|re.IGNORECASE)
1061 1057
1062 class time: 1058 class time:
1063 """Time with time zone. 1059 """Time with time zone.
1064 1060
1065 Constructors: 1061 Constructors:
1066 1062
1067 __new__() 1063 __new__()
1068 1064
1069 Operators: 1065 Operators:
1070 1066
1071 __repr__, __str__ 1067 __repr__, __str__
1072 __eq__, __le__, __lt__, __ge__, __gt__, __hash__ 1068 __eq__, __le__, __lt__, __ge__, __gt__, __hash__
1073 1069
1074 Methods: 1070 Methods:
1075 1071
1076 strftime() 1072 strftime()
1077 isoformat() 1073 isoformat()
1078 utcoffset() 1074 utcoffset()
1079 tzname() 1075 tzname()
1080 dst() 1076 dst()
1081 1077
1082 Properties (readonly): 1078 Properties (readonly):
1083 hour, minute, second, microsecond, tzinfo, fold 1079 hour, minute, second, microsecond, tzinfo, fold
1084 """ 1080 """
1085 __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hash code', '_fold' 1081 __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hash code', '_fold'
1086 1082
1087 _isore = _TIME_RE 1083 _isore = re.compile(r'(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})'
1084 r'(?P<microsecond>\.\d+)?(?P<tzinfo>Z|[+-]\d{2}:\d{2})?$ ',
1085 re.ASCII|re.IGNORECASE)
1086
1088 1087
1089 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): 1088 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):
1090 """Constructor. 1089 """Constructor.
1091 1090
1092 Arguments: 1091 Arguments:
1093 1092
1094 hour, minute (required) 1093 hour, minute (required)
1095 second, microsecond (default to zero) 1094 second, microsecond (default to zero)
1096 tzinfo (default to None) 1095 tzinfo (default to None)
1097 fold (keyword only, default to True) 1096 fold (keyword only, default to True)
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after
1405 time.resolution = timedelta(microseconds=1) 1404 time.resolution = timedelta(microseconds=1)
1406 1405
1407 class datetime(date): 1406 class datetime(date):
1408 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo] ]]]]) 1407 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo] ]]]])
1409 1408
1410 The year, month and day arguments are required. tzinfo may be None, or an 1409 The year, month and day arguments are required. tzinfo may be None, or an
1411 instance of a tzinfo subclass. The remaining arguments may be ints. 1410 instance of a tzinfo subclass. The remaining arguments may be ints.
1412 """ 1411 """
1413 __slots__ = date.__slots__ + time.__slots__ 1412 __slots__ = date.__slots__ + time.__slots__
1414 1413
1415 _isore = re.compile(_DATE_RE.pattern[:-1] + r'[T ]' + _TIME_RE.pattern, 1414 _isore = re.compile(date._isore.pattern[:-1] + r'[T ]' +
SilentGhost 2016/08/05 13:22:36 Can't you reach regexes as class attributes of dat
deronnax 2016/08/05 14:09:51 It was to somewhat reduce coupling, but that was p
1416 re.ASCII|re.IGNORECASE) 1415 time._isore.pattern, re.ASCII|re.IGNORECASE)
1417 1416
1418 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, 1417 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1419 microsecond=0, tzinfo=None, *, fold=0): 1418 microsecond=0, tzinfo=None, *, fold=0):
1420 if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12: 1419 if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12:
1421 # Pickle support 1420 # Pickle support
1422 self = object.__new__(cls) 1421 self = object.__new__(cls)
1423 self.__setstate(year, month) 1422 self.__setstate(year, month)
1424 self._hashcode = -1 1423 self._hashcode = -1
1425 return self 1424 return self
1426 year, month, day = _check_date_fields(year, month, day) 1425 year, month, day = _check_date_fields(year, month, day)
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1539 if not isinstance(date, _date_class): 1538 if not isinstance(date, _date_class):
1540 raise TypeError("date argument must be a date instance") 1539 raise TypeError("date argument must be a date instance")
1541 if not isinstance(time, _time_class): 1540 if not isinstance(time, _time_class):
1542 raise TypeError("time argument must be a time instance") 1541 raise TypeError("time argument must be a time instance")
1543 if tzinfo is True: 1542 if tzinfo is True:
1544 tzinfo = time.tzinfo 1543 tzinfo = time.tzinfo
1545 return cls(date.year, date.month, date.day, 1544 return cls(date.year, date.month, date.day,
1546 time.hour, time.minute, time.second, time.microsecond, 1545 time.hour, time.minute, time.second, time.microsecond,
1547 tzinfo, fold=time.fold) 1546 tzinfo, fold=time.fold)
1548 1547
1549 @classmethod
1550 def fromisoformat(cls, date_string):
SilentGhost 2016/08/05 13:22:36 Since datetime is a subclass of date, I think this
deronnax 2016/08/05 14:09:51 you're right
1551 """Constructs a datetime from an RFC 3339 string, a strict subset of ISO 8601
1552
1553 Microseconds are rounded to 6 digits.
1554 Raises ValueError if string is ill formatted or invalid
1555 """
1556 return _parse_isotime(cls, date_string)
1557
1558 def timetuple(self): 1548 def timetuple(self):
1559 "Return local time tuple compatible with time.localtime()." 1549 "Return local time tuple compatible with time.localtime()."
1560 dst = self.dst() 1550 dst = self.dst()
1561 if dst is None: 1551 if dst is None:
1562 dst = -1 1552 dst = -1
1563 elif dst: 1553 elif dst:
1564 dst = 1 1554 dst = 1
1565 else: 1555 else:
1566 dst = 0 1556 dst = 0
1567 return _build_struct_time(self.year, self.month, self.day, 1557 return _build_struct_time(self.year, self.month, self.day,
(...skipping 544 matching lines...) Expand 10 before | Expand all | Expand 10 after
2112 sign = '+' 2102 sign = '+'
2113 hours, rest = divmod(delta, timedelta(hours=1)) 2103 hours, rest = divmod(delta, timedelta(hours=1))
2114 minutes = rest // timedelta(minutes=1) 2104 minutes = rest // timedelta(minutes=1)
2115 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) 2105 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
2116 2106
2117 timezone.utc = timezone._create(timedelta(0)) 2107 timezone.utc = timezone._create(timedelta(0))
2118 timezone.min = timezone._create(timezone._minoffset) 2108 timezone.min = timezone._create(timezone._minoffset)
2119 timezone.max = timezone._create(timezone._maxoffset) 2109 timezone.max = timezone._create(timezone._maxoffset)
2120 _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) 2110 _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
2121 2111
2122 del _DATE_RE
2123 del _TIME_RE
2124
2125 # Some time zone algebra. For a datetime x, let 2112 # Some time zone algebra. For a datetime x, let
2126 # x.n = x stripped of its timezone -- its naive time. 2113 # x.n = x stripped of its timezone -- its naive time.
2127 # x.o = x.utcoffset(), and assuming that doesn't raise an exception or 2114 # x.o = x.utcoffset(), and assuming that doesn't raise an exception or
2128 # return None 2115 # return None
2129 # x.d = x.dst(), and assuming that doesn't raise an exception or 2116 # x.d = x.dst(), and assuming that doesn't raise an exception or
2130 # return None 2117 # return None
2131 # x.s = x's standard offset, x.o - x.d 2118 # x.s = x's standard offset, x.o - x.d
2132 # 2119 #
2133 # Now some derived rules, where k is a duration (timedelta). 2120 # Now some derived rules, where k is a duration (timedelta).
2134 # 2121 #
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
2330 _check_date_fields, _check_int_field, _check_time_fields, 2317 _check_date_fields, _check_int_field, _check_time_fields,
2331 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror, 2318 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
2332 _date_class, _days_before_month, _days_before_year, _days_in_month, 2319 _date_class, _days_before_month, _days_before_year, _days_in_month,
2333 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd, 2320 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd,
2334 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord) 2321 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord)
2335 # XXX Since import * above excludes names that start with _, 2322 # XXX Since import * above excludes names that start with _,
2336 # docstring does not get overwritten. In the future, it may be 2323 # docstring does not get overwritten. In the future, it may be
2337 # appropriate to maintain a single module level docstring and 2324 # appropriate to maintain a single module level docstring and
2338 # remove the following line. 2325 # remove the following line.
2339 from _datetime import __doc__ 2326 from _datetime import __doc__
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+