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

Side by Side Diff: Lib/datetime.py

Issue 15873: "datetime" cannot parse ISO 8601 dates and times
Patch Set: Created 3 years, 6 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:
View unified diff | Download patch
« no previous file with comments | « no previous file | Lib/test/datetimetester.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 10
10 def _cmp(x, y): 11 def _cmp(x, y):
11 return 0 if x == y else 1 if x > y else -1 12 return 0 if x == y else 1 if x > y else -1
12 13
13 MINYEAR = 1 14 MINYEAR = 1
14 MAXYEAR = 9999 15 MAXYEAR = 9999
15 _MAXORDINAL = 3652059 # date.max.toordinal() 16 _MAXORDINAL = 3652059 # date.max.toordinal()
16 17
17 # Utility functions, adapted from Python's Demo/classes/Dates.py, which 18 # Utility functions, adapted from Python's Demo/classes/Dates.py, which
18 # also assumes the current Gregorian calendar indefinitely extended in 19 # also assumes the current Gregorian calendar indefinitely extended in
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 q, r = divmod(a, b) 323 q, r = divmod(a, b)
323 # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. 324 # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
324 # The expression r / b > 0.5 is equivalent to 2 * r > b if b is 325 # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
325 # positive, 2 * r < b if b negative. 326 # positive, 2 * r < b if b negative.
326 r *= 2 327 r *= 2
327 greater_than_half = r > b if b > 0 else r < b 328 greater_than_half = r > b if b > 0 else r < b
328 if greater_than_half or r == b and q % 2 == 1: 329 if greater_than_half or r == b and q % 2 == 1:
329 q += 1 330 q += 1
330 331
331 return q 332 return q
333
334
335 def _parse_isotime(cls, isostring):
336 match = cls._isore.match(isostring)
337 if not match:
338 raise ValueError("invalid RFC 3339 %s string: %r" % (cls.__name__, isost ring))
SilentGhost 2016/08/05 13:22:36 Please, wrap this line to 80 chars.
deronnax 2016/08/05 14:09:51 done
339 kw = match.groupdict()
340 tzinfo = kw.pop('tzinfo', None)
341 if tzinfo == 'Z' or tzinfo == 'z':
342 tzinfo = timezone.utc
343 elif tzinfo is not None:
344 offset_hours, _, offset_mins = tzinfo[1:].partition(':')
345 offset = timedelta(hours=int(offset_hours), minutes=int(offset_mins))
346 if tzinfo[0] == '-':
347 offset = -offset
348 tzinfo = timezone(offset)
349 us = kw.pop('microsecond', None)
350 kw = {k: int(v) for k, v in kw.items() if v is not None}
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 us = round(float(us), 6)
353 kw['microsecond'] = int(us * 1e6)
354 if tzinfo:
355 kw['tzinfo'] = tzinfo
356 return cls(**kw)
332 357
333 358
334 class timedelta: 359 class timedelta:
335 """Represent the difference between two datetime objects. 360 """Represent the difference between two datetime objects.
336 361
337 Supported operators: 362 Supported operators:
338 363
339 - add, subtract timedelta 364 - add, subtract timedelta
340 - unary plus, minus, abs 365 - unary plus, minus, abs
341 - compare to timedelta 366 - compare to timedelta
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
645 def _getstate(self): 670 def _getstate(self):
646 return (self._days, self._seconds, self._microseconds) 671 return (self._days, self._seconds, self._microseconds)
647 672
648 def __reduce__(self): 673 def __reduce__(self):
649 return (self.__class__, self._getstate()) 674 return (self.__class__, self._getstate())
650 675
651 timedelta.min = timedelta(-999999999) 676 timedelta.min = timedelta(-999999999)
652 timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, 677 timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
653 microseconds=999999) 678 microseconds=999999)
654 timedelta.resolution = timedelta(microseconds=1) 679 timedelta.resolution = timedelta(microseconds=1)
680
681 _DATE_RE = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', re.AS CII)
655 682
656 class date: 683 class date:
657 """Concrete date type. 684 """Concrete date type.
658 685
659 Constructors: 686 Constructors:
660 687
661 __new__() 688 __new__()
662 fromtimestamp() 689 fromtimestamp()
663 today() 690 today()
664 fromordinal() 691 fromordinal()
(...skipping 10 matching lines...) Expand all
675 toordinal() 702 toordinal()
676 weekday() 703 weekday()
677 isoweekday(), isocalendar(), isoformat() 704 isoweekday(), isocalendar(), isoformat()
678 ctime() 705 ctime()
679 strftime() 706 strftime()
680 707
681 Properties (readonly): 708 Properties (readonly):
682 year, month, day 709 year, month, day
683 """ 710 """
684 __slots__ = '_year', '_month', '_day', '_hashcode' 711 __slots__ = '_year', '_month', '_day', '_hashcode'
712
713 _isore = _DATE_RE
685 714
686 def __new__(cls, year, month=None, day=None): 715 def __new__(cls, year, month=None, day=None):
687 """Constructor. 716 """Constructor.
688 717
689 Arguments: 718 Arguments:
690 719
691 year, month, day (required, base 1) 720 year, month, day (required, base 1)
692 """ 721 """
693 if month is None and isinstance(year, bytes) and len(year) == 4 and \ 722 if month is None and isinstance(year, bytes) and len(year) == 4 and \
694 1 <= year[2] <= 12: 723 1 <= year[2] <= 12:
(...skipping 26 matching lines...) Expand all
721 750
722 @classmethod 751 @classmethod
723 def fromordinal(cls, n): 752 def fromordinal(cls, n):
724 """Construct a date from a proleptic Gregorian ordinal. 753 """Construct a date from a proleptic Gregorian ordinal.
725 754
726 January 1 of year 1 is day 1. Only the year, month and day are 755 January 1 of year 1 is day 1. Only the year, month and day are
727 non-zero in the result. 756 non-zero in the result.
728 """ 757 """
729 y, m, d = _ord2ymd(n) 758 y, m, d = _ord2ymd(n)
730 return cls(y, m, d) 759 return cls(y, m, d)
760
761 @classmethod
762 def fromisoformat(cls, date_string):
763 """Constructs a date from an RFC 3339 string, a strict subset of ISO 860 1
764
765 Raises ValueError in case of ill-formatted or invalid string.
766 """
767 return _parse_isotime(cls, date_string)
731 768
732 # Conversions to string 769 # Conversions to string
733 770
734 def __repr__(self): 771 def __repr__(self):
735 """Convert to formal string, for repr(). 772 """Convert to formal string, for repr().
736 773
737 >>> dt = datetime(2010, 1, 1) 774 >>> dt = datetime(2010, 1, 1)
738 >>> repr(dt) 775 >>> repr(dt)
739 'datetime.datetime(2010, 1, 1, 0, 0)' 776 'datetime.datetime(2010, 1, 1, 0, 0)'
740 777
(...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after
1010 if getstate: 1047 if getstate:
1011 state = getstate() 1048 state = getstate()
1012 else: 1049 else:
1013 state = getattr(self, "__dict__", None) or None 1050 state = getattr(self, "__dict__", None) or None
1014 if state is None: 1051 if state is None:
1015 return (self.__class__, args) 1052 return (self.__class__, args)
1016 else: 1053 else:
1017 return (self.__class__, args, state) 1054 return (self.__class__, args, state)
1018 1055
1019 _tzinfo_class = tzinfo 1056 _tzinfo_class = tzinfo
1057
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)
1020 1061
1021 class time: 1062 class time:
1022 """Time with time zone. 1063 """Time with time zone.
1023 1064
1024 Constructors: 1065 Constructors:
1025 1066
1026 __new__() 1067 __new__()
1027 1068
1028 Operators: 1069 Operators:
1029 1070
1030 __repr__, __str__ 1071 __repr__, __str__
1031 __eq__, __le__, __lt__, __ge__, __gt__, __hash__ 1072 __eq__, __le__, __lt__, __ge__, __gt__, __hash__
1032 1073
1033 Methods: 1074 Methods:
1034 1075
1035 strftime() 1076 strftime()
1036 isoformat() 1077 isoformat()
1037 utcoffset() 1078 utcoffset()
1038 tzname() 1079 tzname()
1039 dst() 1080 dst()
1040 1081
1041 Properties (readonly): 1082 Properties (readonly):
1042 hour, minute, second, microsecond, tzinfo, fold 1083 hour, minute, second, microsecond, tzinfo, fold
1043 """ 1084 """
1044 __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hash code', '_fold' 1085 __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hash code', '_fold'
1086
1087 _isore = _TIME_RE
1045 1088
1046 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): 1089 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):
1047 """Constructor. 1090 """Constructor.
1048 1091
1049 Arguments: 1092 Arguments:
1050 1093
1051 hour, minute (required) 1094 hour, minute (required)
1052 second, microsecond (default to zero) 1095 second, microsecond (default to zero)
1053 tzinfo (default to None) 1096 tzinfo (default to None)
1054 fold (keyword only, default to True) 1097 fold (keyword only, default to True)
1055 """ 1098 """
1056 if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24: 1099 if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24:
1057 # Pickle support 1100 # Pickle support
1058 self = object.__new__(cls) 1101 self = object.__new__(cls)
1059 self.__setstate(hour, minute or None) 1102 self.__setstate(hour, minute or None)
1060 self._hashcode = -1 1103 self._hashcode = -1
1061 return self 1104 return self
1062 hour, minute, second, microsecond = _check_time_fields( 1105 hour, minute, second, microsecond = _check_time_fields(
1063 hour, minute, second, microsecond) 1106 hour, minute, second, microsecond)
1064 _check_tzinfo_arg(tzinfo) 1107 _check_tzinfo_arg(tzinfo)
1065 self = object.__new__(cls) 1108 self = object.__new__(cls)
1066 self._hour = hour 1109 self._hour = hour
1067 self._minute = minute 1110 self._minute = minute
1068 self._second = second 1111 self._second = second
1069 self._microsecond = microsecond 1112 self._microsecond = microsecond
1070 self._tzinfo = tzinfo 1113 self._tzinfo = tzinfo
1071 self._hashcode = -1 1114 self._hashcode = -1
1072 self._fold = fold 1115 self._fold = fold
1073 return self 1116 return self
1117
1118 @classmethod
1119 def fromisoformat(cls, time_string):
1120 """Constructs a time from an RFC 3339 string, a strict subset of ISO 860 1
1121
1122 Microseconds are rounded to 6 digits.
1123 Raises ValueError in case of ill-formatted or invalid string.
1124 """
1125 return _parse_isotime(cls, time_string)
1074 1126
1075 # Read-only field accessors 1127 # Read-only field accessors
1076 @property 1128 @property
1077 def hour(self): 1129 def hour(self):
1078 """hour (0-23)""" 1130 """hour (0-23)"""
1079 return self._hour 1131 return self._hour
1080 1132
1081 @property 1133 @property
1082 def minute(self): 1134 def minute(self):
1083 """minute (0-59)""" 1135 """minute (0-59)"""
(...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after
1352 time.max = time(23, 59, 59, 999999) 1404 time.max = time(23, 59, 59, 999999)
1353 time.resolution = timedelta(microseconds=1) 1405 time.resolution = timedelta(microseconds=1)
1354 1406
1355 class datetime(date): 1407 class datetime(date):
1356 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo] ]]]]) 1408 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo] ]]]])
1357 1409
1358 The year, month and day arguments are required. tzinfo may be None, or an 1410 The year, month and day arguments are required. tzinfo may be None, or an
1359 instance of a tzinfo subclass. The remaining arguments may be ints. 1411 instance of a tzinfo subclass. The remaining arguments may be ints.
1360 """ 1412 """
1361 __slots__ = date.__slots__ + time.__slots__ 1413 __slots__ = date.__slots__ + time.__slots__
1414
1415 _isore = re.compile(_DATE_RE.pattern[:-1] + r'[T ]' + _TIME_RE.pattern,
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)
1362 1417
1363 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, 1418 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1364 microsecond=0, tzinfo=None, *, fold=0): 1419 microsecond=0, tzinfo=None, *, fold=0):
1365 if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12: 1420 if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12:
1366 # Pickle support 1421 # Pickle support
1367 self = object.__new__(cls) 1422 self = object.__new__(cls)
1368 self.__setstate(year, month) 1423 self.__setstate(year, month)
1369 self._hashcode = -1 1424 self._hashcode = -1
1370 return self 1425 return self
1371 year, month, day = _check_date_fields(year, month, day) 1426 year, month, day = _check_date_fields(year, month, day)
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
1483 "Construct a datetime from a given date and a given time." 1538 "Construct a datetime from a given date and a given time."
1484 if not isinstance(date, _date_class): 1539 if not isinstance(date, _date_class):
1485 raise TypeError("date argument must be a date instance") 1540 raise TypeError("date argument must be a date instance")
1486 if not isinstance(time, _time_class): 1541 if not isinstance(time, _time_class):
1487 raise TypeError("time argument must be a time instance") 1542 raise TypeError("time argument must be a time instance")
1488 if tzinfo is True: 1543 if tzinfo is True:
1489 tzinfo = time.tzinfo 1544 tzinfo = time.tzinfo
1490 return cls(date.year, date.month, date.day, 1545 return cls(date.year, date.month, date.day,
1491 time.hour, time.minute, time.second, time.microsecond, 1546 time.hour, time.minute, time.second, time.microsecond,
1492 tzinfo, fold=time.fold) 1547 tzinfo, fold=time.fold)
1548
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)
1493 1557
1494 def timetuple(self): 1558 def timetuple(self):
1495 "Return local time tuple compatible with time.localtime()." 1559 "Return local time tuple compatible with time.localtime()."
1496 dst = self.dst() 1560 dst = self.dst()
1497 if dst is None: 1561 if dst is None:
1498 dst = -1 1562 dst = -1
1499 elif dst: 1563 elif dst:
1500 dst = 1 1564 dst = 1
1501 else: 1565 else:
1502 dst = 0 1566 dst = 0
(...skipping 544 matching lines...) Expand 10 before | Expand all | Expand 10 after
2047 else: 2111 else:
2048 sign = '+' 2112 sign = '+'
2049 hours, rest = divmod(delta, timedelta(hours=1)) 2113 hours, rest = divmod(delta, timedelta(hours=1))
2050 minutes = rest // timedelta(minutes=1) 2114 minutes = rest // timedelta(minutes=1)
2051 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) 2115 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
2052 2116
2053 timezone.utc = timezone._create(timedelta(0)) 2117 timezone.utc = timezone._create(timedelta(0))
2054 timezone.min = timezone._create(timezone._minoffset) 2118 timezone.min = timezone._create(timezone._minoffset)
2055 timezone.max = timezone._create(timezone._maxoffset) 2119 timezone.max = timezone._create(timezone._maxoffset)
2056 _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) 2120 _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
2121
2122 del _DATE_RE
2123 del _TIME_RE
2057 2124
2058 # Some time zone algebra. For a datetime x, let 2125 # Some time zone algebra. For a datetime x, let
2059 # x.n = x stripped of its timezone -- its naive time. 2126 # x.n = x stripped of its timezone -- its naive time.
2060 # x.o = x.utcoffset(), and assuming that doesn't raise an exception or 2127 # x.o = x.utcoffset(), and assuming that doesn't raise an exception or
2061 # return None 2128 # return None
2062 # x.d = x.dst(), and assuming that doesn't raise an exception or 2129 # x.d = x.dst(), and assuming that doesn't raise an exception or
2063 # return None 2130 # return None
2064 # x.s = x's standard offset, x.o - x.d 2131 # x.s = x's standard offset, x.o - x.d
2065 # 2132 #
2066 # Now some derived rules, where k is a duration (timedelta). 2133 # Now some derived rules, where k is a duration (timedelta).
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
2245 # 2312 #
2246 # In any case, it's clear that the default fromutc() is strong enough to handle 2313 # In any case, it's clear that the default fromutc() is strong enough to handle
2247 # "almost all" time zones: so long as the standard offset is invariant, it 2314 # "almost all" time zones: so long as the standard offset is invariant, it
2248 # doesn't matter if daylight time transition points change from year to year, or 2315 # doesn't matter if daylight time transition points change from year to year, or
2249 # if daylight time is skipped in some years; it doesn't matter how large or 2316 # if daylight time is skipped in some years; it doesn't matter how large or
2250 # small dst() may get within its bounds; and it doesn't even matter if some 2317 # small dst() may get within its bounds; and it doesn't even matter if some
2251 # perverse time zone returns a negative dst()). So a breaking case must be 2318 # perverse time zone returns a negative dst()). So a breaking case must be
2252 # pretty bizarre, and a tzinfo subclass can override fromutc() if it is. 2319 # pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
2253 2320
2254 try: 2321 try:
2322 raise ImportError
2255 from _datetime import * 2323 from _datetime import *
2256 except ImportError: 2324 except ImportError:
2257 pass 2325 pass
2258 else: 2326 else:
2259 # Clean up unused names 2327 # Clean up unused names
2260 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y, 2328 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y,
2261 _DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time, 2329 _DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time,
2262 _check_date_fields, _check_int_field, _check_time_fields, 2330 _check_date_fields, _check_int_field, _check_time_fields,
2263 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror, 2331 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
2264 _date_class, _days_before_month, _days_before_year, _days_in_month, 2332 _date_class, _days_before_month, _days_before_year, _days_in_month,
2265 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd, 2333 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd,
2266 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord) 2334 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord)
2267 # XXX Since import * above excludes names that start with _, 2335 # XXX Since import * above excludes names that start with _,
2268 # docstring does not get overwritten. In the future, it may be 2336 # docstring does not get overwritten. In the future, it may be
2269 # appropriate to maintain a single module level docstring and 2337 # appropriate to maintain a single module level docstring and
2270 # remove the following line. 2338 # remove the following line.
2271 from _datetime import __doc__ 2339 from _datetime import __doc__
OLDNEW
« no previous file with comments | « no previous file | Lib/test/datetimetester.py » ('j') | no next file with comments »

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