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

Side by Side Diff: Lib/datetime.py

Issue 15873: "datetime" cannot parse ISO 8601 dates and times
Patch Set: Created 4 years 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') | Lib/test/datetimetester.py » ('J')
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 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 140
140 # Now the year and month are correct, and n is the offset from the 141 # Now the year and month are correct, and n is the offset from the
141 # start of that month: we're done! 142 # start of that month: we're done!
142 return year, month, n+1 143 return year, month, n+1
143 144
144 # Month and day names. For localized versions, see the calendar module. 145 # Month and day names. For localized versions, see the calendar module.
145 _MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", 146 _MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
146 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 147 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
147 _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 148 _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
148 149
150 datetime_re = re.compile(
Martin Panter 2016/02/16 04:21:50 IMO this would be better moved closer to where it
151 r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})'
152 r'[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})'
153 r'(?::(?P<second>\d{1,2})(?:(?P<microsecond>\.\d{1,7})\d*)?)?'
Martin Panter 2016/02/16 04:21:50 If we want to round half to even, reading one extr
154 r'(?P<tzinfo>Z|[+-]\d{2}(?::?\d{2})?)?$'
155 )
Martin Panter 2016/02/16 05:50:03 Should probably add case-insensitive flag to allow
149 156
150 def _build_struct_time(y, m, d, hh, mm, ss, dstflag): 157 def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
151 wday = (_ymd2ord(y, m, d) + 6) % 7 158 wday = (_ymd2ord(y, m, d) + 6) % 7
152 dnum = _days_before_month(y, m) + d 159 dnum = _days_before_month(y, m) + d
153 return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) 160 return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
154 161
155 def _format_time(hh, mm, ss, us): 162 def _format_time(hh, mm, ss, us):
156 # Skip trailing microseconds when us==0. 163 # Skip trailing microseconds when us==0.
157 result = "%02d:%02d:%02d" % (hh, mm, ss) 164 result = "%02d:%02d:%02d" % (hh, mm, ss)
158 if us: 165 if us:
(...skipping 1233 matching lines...) Expand 10 before | Expand all | Expand 10 after
1392 """Construct a datetime from a POSIX timestamp (like time.time()). 1399 """Construct a datetime from a POSIX timestamp (like time.time()).
1393 1400
1394 A timezone info object may be passed in as well. 1401 A timezone info object may be passed in as well.
1395 """ 1402 """
1396 _check_tzinfo_arg(tz) 1403 _check_tzinfo_arg(tz)
1397 1404
1398 result = cls._fromtimestamp(t, tz is not None, tz) 1405 result = cls._fromtimestamp(t, tz is not None, tz)
1399 if tz is not None: 1406 if tz is not None:
1400 result = tz.fromutc(result) 1407 result = tz.fromutc(result)
1401 return result 1408 return result
1409
1410 @classmethod
1411 def fromisoformat(cls, value):
1412 """Parses a string and return a datetime.datetime.
1413
1414 This function supports time zone offsets. When the input contains one,
1415 the output uses a timezone with a fixed offset from UTC.
1416 Microseconds values after 6 digits are discared.
Martin Panter 2016/02/16 04:21:50 Spelling: discarded. But it seems we are changing
1417
1418 Raises ValueError if the input is not well formatted or not a valid date time.
1419 """
1420 match = datetime_re.match(value)
1421 if not match:
1422 raise ValueError('invalid {} iso8601 string : {}'.format(cls.__name_ _, value))
Martin Panter 2016/02/16 04:21:50 This could be a bit confusing if the string was ac
1423 kw = match.groupdict()
1424 tzinfo = kw.pop('tzinfo')
1425 if tzinfo == 'Z':
1426 tzinfo = timezone.utc
1427 elif tzinfo is not None:
1428 offset_mins = int(tzinfo[-2:]) if len(tzinfo) > 3 else 0
1429 offset_hours = int(tzinfo[1:3])
1430 offset = timedelta(hours=offset_hours, minutes=offset_mins)
1431 if tzinfo[0] == '-':
1432 offset = -offset
1433 tzinfo = timezone(offset)
1434 us = kw.pop('microsecond', None)
1435 kw = {k: int(v) for k, v in kw.items() if v is not None}
1436 if us:
1437 us = round(float(us), 6)
1438 kw['microsecond'] = int(us * 1e6)
haypo 2016/02/15 17:29:59 I suggest: kw['microsecond'] = round(float(us) /
deronnax 2016/08/05 14:09:51 Doesn't work, make the tests fails, microseconds a
1439 kw['tzinfo'] = tzinfo
1440 return cls(**kw)
1402 1441
1403 @classmethod 1442 @classmethod
1404 def utcfromtimestamp(cls, t): 1443 def utcfromtimestamp(cls, t):
1405 """Construct a naive UTC datetime from a POSIX timestamp.""" 1444 """Construct a naive UTC datetime from a POSIX timestamp."""
1406 return cls._fromtimestamp(t, True, None) 1445 return cls._fromtimestamp(t, True, None)
1407 1446
1408 @classmethod 1447 @classmethod
1409 def now(cls, tz=None): 1448 def now(cls, tz=None):
1410 "Construct a datetime from time.time() and optional time zone info." 1449 "Construct a datetime from time.time() and optional time zone info."
1411 t = _time.time() 1450 t = _time.time()
(...skipping 725 matching lines...) Expand 10 before | Expand all | Expand 10 after
2137 _check_date_fields, _check_int_field, _check_time_fields, 2176 _check_date_fields, _check_int_field, _check_time_fields,
2138 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror, 2177 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
2139 _date_class, _days_before_month, _days_before_year, _days_in_month, 2178 _date_class, _days_before_month, _days_before_year, _days_in_month,
2140 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd, 2179 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd,
2141 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord) 2180 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord)
2142 # XXX Since import * above excludes names that start with _, 2181 # XXX Since import * above excludes names that start with _,
2143 # docstring does not get overwritten. In the future, it may be 2182 # docstring does not get overwritten. In the future, it may be
2144 # appropriate to maintain a single module level docstring and 2183 # appropriate to maintain a single module level docstring and
2145 # remove the following line. 2184 # remove the following line.
2146 from _datetime import __doc__ 2185 from _datetime import __doc__
OLDNEW
« no previous file with comments | « no previous file | Lib/test/datetimetester.py » ('j') | Lib/test/datetimetester.py » ('J')

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