| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -436,6 +436,21 @@ Other constructors, all class methods: | |||
| 436 | 436 | d``. | |
| 437 | 437 | ||
| 438 | 438 | ||
| 439 | + .. classmethod:: date.fromisoformat(date_string) | ||
| 440 | + | ||
| 441 | + Return a :class:`date` corresponding to a *date_string* in the format emitted | ||
| 442 | + by :meth:`date.isoformat`. Specifically, this function supports strings in | ||
| 443 | + the format(s) ``YYYY-MM-DD``. | ||
| 444 | + | ||
| 445 | + .. caution:: | ||
| 446 | + | ||
| 447 | + This does not support parsing arbitrary ISO 8601 strings - it is only intended | ||
| 448 | + as the inverse operation of :meth:`date.isoformat`. | ||
| 449 | + | ||
| 450 | + .. versionadded:: 3.7 | ||
| 451 | + | ||
| 452 | + | ||
| 453 | + | ||
| 439 | 454 | Class attributes: | |
| 440 | 455 | ||
| 441 | 456 | .. attribute:: date.min | |
@@ -819,6 +834,21 @@ Other constructors, all class methods: | |||
| 819 | 834 | Added the *tzinfo* argument. | |
| 820 | 835 | ||
| 821 | 836 | ||
| 837 | + .. classmethod:: datetime.fromisoformat(date_string) | ||
| 838 | + | ||
| 839 | + Return a :class:`datetime` corresponding to a *date_string* in one of the | ||
| 840 | + formats emitted by :meth:`date.isoformat` and :meth:`datetime.isoformat`. | ||
| 841 | + Specifically, this function supports strings in the format(s) | ||
| 842 | + ``YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]``, | ||
| 843 | + where ``*`` can match any single character. | ||
| 844 | + | ||
| 845 | + .. caution:: | ||
| 846 | + | ||
| 847 | + This does not support parsing arbitrary ISO 8601 strings - it is only intended | ||
| 848 | + as the inverse operation of :meth:`datetime.isoformat`. | ||
| 849 | + | ||
| 850 | + .. versionadded:: 3.7 | ||
| 851 | + | ||
| 822 | 852 | .. classmethod:: datetime.strptime(date_string, format) | |
| 823 | 853 | ||
| 824 | 854 | Return a :class:`.datetime` corresponding to *date_string*, parsed according to | |
@@ -1486,6 +1516,23 @@ In boolean contexts, a :class:`.time` object is always considered to be true. | |||
| 1486 | 1516 | error-prone and has been removed in Python 3.5. See :issue:`13936` for full | |
| 1487 | 1517 | details. | |
| 1488 | 1518 | ||
| 1519 | + | ||
| 1520 | + Other constructor: | ||
| 1521 | + | ||
| 1522 | + .. classmethod:: time.fromisoformat(time_string) | ||
| 1523 | + | ||
| 1524 | + Return a :class:`time` corresponding to a *time_string* in one of the | ||
| 1525 | + formats emitted by :meth:`time.isoformat`. Specifically, this function supports | ||
| 1526 | + strings in the format(s) ``HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]``. | ||
| 1527 | + | ||
| 1528 | + .. caution:: | ||
| 1529 | + | ||
| 1530 | + This does not support parsing arbitrary ISO 8601 strings - it is only intended | ||
| 1531 | + as the inverse operation of :meth:`time.isoformat`. | ||
| 1532 | + | ||
| 1533 | + .. versionadded:: 3.7 | ||
| 1534 | + | ||
| 1535 | + | ||
| 1489 | 1536 | Instance methods: | |
| 1490 | 1537 | ||
| 1491 | 1538 | .. method:: time.replace(hour=self.hour, minute=self.minute, second=self.second, \ | |
@@ -1587,7 +1634,6 @@ Instance methods: | |||
| 1587 | 1634 | ``self.tzinfo.tzname(None)``, or raises an exception if the latter doesn't | |
| 1588 | 1635 | return ``None`` or a string object. | |
| 1589 | 1636 | ||
| 1590 | - | ||
| 1591 | 1637 | Example: | |
| 1592 | 1638 | ||
| 1593 | 1639 | >>> from datetime import time, tzinfo, timedelta | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -173,6 +173,24 @@ def _format_time(hh, mm, ss, us, timespec='auto'): | |||
| 173 | 173 | else: | |
| 174 | 174 | return fmt.format(hh, mm, ss, us) | |
| 175 | 175 | ||
| 176 | + def _format_offset(off): | ||
| 177 | + s = '' | ||
| 178 | + if off is not None: | ||
| 179 | + if off.days < 0: | ||
| 180 | + sign = "-" | ||
| 181 | + off = -off | ||
| 182 | + else: | ||
| 183 | + sign = "+" | ||
| 184 | + hh, mm = divmod(off, timedelta(hours=1)) | ||
| 185 | + mm, ss = divmod(mm, timedelta(minutes=1)) | ||
| 186 | + s += "%s%02d:%02d" % (sign, hh, mm) | ||
| 187 | + if ss or ss.microseconds: | ||
| 188 | + s += ":%02d" % ss.seconds | ||
| 189 | + | ||
| 190 | + if ss.microseconds: | ||
| 191 | + s += '.%06d' % ss.microseconds | ||
| 192 | + return s | ||
| 193 | + | ||
| 176 | 194 | # Correctly substitute for %z and %Z escapes in strftime formats. | |
| 177 | 195 | def _wrap_strftime(object, format, timetuple): | |
| 178 | 196 | # Don't call utcoffset() or tzname() unless actually needed. | |
@@ -237,6 +255,102 @@ def _wrap_strftime(object, format, timetuple): | |||
| 237 | 255 | newformat = "".join(newformat) | |
| 238 | 256 | return _time.strftime(newformat, timetuple) | |
| 239 | 257 | ||
| 258 | + # Helpers for parsing the result of isoformat() | ||
| 259 | + def _parse_isoformat_date(dtstr): | ||
| 260 | + # It is assumed that this function will only be called with a | ||
| 261 | + # string of length exactly 10, and (though this is not used) ASCII-only | ||
| 262 | + year = int(dtstr[0:4]) | ||
| 263 | + if dtstr[4] != '-': | ||
| 264 | + raise ValueError('Invalid date separator: %s' % dtstr[4]) | ||
| 265 | + | ||
| 266 | + month = int(dtstr[5:7]) | ||
| 267 | + | ||
| 268 | + if dtstr[7] != '-': | ||
| 269 | + raise ValueError('Invalid date separator') | ||
| 270 | + | ||
| 271 | + day = int(dtstr[8:10]) | ||
| 272 | + | ||
| 273 | + return [year, month, day] | ||
| 274 | + | ||
| 275 | + def _parse_hh_mm_ss_ff(tstr): | ||
| 276 | + # Parses things of the form HH[:MM[:SS[.fff[fff]]]] | ||
| 277 | + len_str = len(tstr) | ||
| 278 | + | ||
| 279 | + time_comps = [0, 0, 0, 0] | ||
| 280 | + pos = 0 | ||
| 281 | + for comp in range(0, 3): | ||
| 282 | + if (len_str - pos) < 2: | ||
| 283 | + raise ValueError('Incomplete time component') | ||
| 284 | + | ||
| 285 | + time_comps[comp] = int(tstr[pos:pos+2]) | ||
| 286 | + | ||
| 287 | + pos += 2 | ||
| 288 | + next_char = tstr[pos:pos+1] | ||
| 289 | + | ||
| 290 | + if not next_char or comp >= 2: | ||
| 291 | + break | ||
| 292 | + | ||
| 293 | + if next_char != ':': | ||
| 294 | + raise ValueError('Invalid time separator: %c' % next_char) | ||
| 295 | + | ||
| 296 | + pos += 1 | ||
| 297 | + | ||
| 298 | + if pos < len_str: | ||
| 299 | + if tstr[pos] != '.': | ||
| 300 | + raise ValueError('Invalid microsecond component') | ||
| 301 | + else: | ||
| 302 | + pos += 1 | ||
| 303 | + | ||
| 304 | + len_remainder = len_str - pos | ||
| 305 | + if len_remainder not in (3, 6): | ||
| 306 | + raise ValueError('Invalid microsecond component') | ||
| 307 | + | ||
| 308 | + time_comps[3] = int(tstr[pos:]) | ||
| 309 | + if len_remainder == 3: | ||
| 310 | + time_comps[3] *= 1000 | ||
| 311 | + | ||
| 312 | + return time_comps | ||
| 313 | + | ||
| 314 | + def _parse_isoformat_time(tstr): | ||
| 315 | + # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] | ||
| 316 | + len_str = len(tstr) | ||
| 317 | + if len_str < 2: | ||
| 318 | + raise ValueError('Isoformat time too short') | ||
| 319 | + | ||
| 320 | + # This is equivalent to re.search('[+-]', tstr), but faster | ||
| 321 | + tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1) | ||
| 322 | + timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr | ||
| 323 | + | ||
| 324 | + time_comps = _parse_hh_mm_ss_ff(timestr) | ||
| 325 | + | ||
| 326 | + tzi = None | ||
| 327 | + if tz_pos > 0: | ||
| 328 | + tzstr = tstr[tz_pos:] | ||
| 329 | + | ||
| 330 | + # Valid time zone strings are: | ||
| 331 | + # HH:MM len: 5 | ||
| 332 | + # HH:MM:SS len: 8 | ||
| 333 | + # HH:MM:SS.ffffff len: 15 | ||
| 334 | + | ||
| 335 | + if len(tzstr) not in (5, 8, 15): | ||
| 336 | + raise ValueError('Malformed time zone string') | ||
| 337 | + | ||
| 338 | + tz_comps = _parse_hh_mm_ss_ff(tzstr) | ||
| 339 | + if all(x == 0 for x in tz_comps): | ||
| 340 | + tzi = timezone.utc | ||
| 341 | + else: | ||
| 342 | + tzsign = -1 if tstr[tz_pos - 1] == '-' else 1 | ||
| 343 | + | ||
| 344 | + td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], | ||
| 345 | + seconds=tz_comps[2], microseconds=tz_comps[3]) | ||
| 346 | + | ||
| 347 | + tzi = timezone(tzsign * td) | ||
| 348 | + | ||
| 349 | + time_comps.append(tzi) | ||
| 350 | + | ||
| 351 | + return time_comps | ||
| 352 | + | ||
| 353 | + | ||
| 240 | 354 | # Just raise TypeError if the arg isn't None or a string. | |
| 241 | 355 | def _check_tzname(name): | |
| 242 | 356 | if name is not None and not isinstance(name, str): | |
@@ -732,6 +846,19 @@ def fromordinal(cls, n): | |||
| 732 | 846 | y, m, d = _ord2ymd(n) | |
| 733 | 847 | return cls(y, m, d) | |
| 734 | 848 | ||
| 849 | + @classmethod | ||
| 850 | + def fromisoformat(cls, date_string): | ||
| 851 | + """Construct a date from the output of date.isoformat().""" | ||
| 852 | + if not isinstance(date_string, str): | ||
| 853 | + raise TypeError('fromisoformat: argument must be str') | ||
| 854 | + | ||
| 855 | + try: | ||
| 856 | + assert len(date_string) == 10 | ||
| 857 | + return cls(*_parse_isoformat_date(date_string)) | ||
| 858 | + except Exception: | ||
| 859 | + raise ValueError('Invalid isoformat string: %s' % date_string) | ||
| 860 | + | ||
| 861 | + | ||
| 735 | 862 | # Conversions to string | |
| 736 | 863 | ||
| 737 | 864 | def __repr__(self): | |
@@ -1190,22 +1317,10 @@ def __hash__(self): | |||
| 1190 | 1317 | ||
| 1191 | 1318 | # Conversion to string | |
| 1192 | 1319 | ||
| 1193 | - def _tzstr(self, sep=":"): | ||
| 1194 | - """Return formatted timezone offset (+xx:xx) or None.""" | ||
| 1320 | + def _tzstr(self): | ||
| 1321 | + """Return formatted timezone offset (+xx:xx) or an empty string.""" | ||
| 1195 | 1322 | off = self.utcoffset() | |
| 1196 | - if off is not None: | ||
| 1197 | - if off.days < 0: | ||
| 1198 | - sign = "-" | ||
| 1199 | - off = -off | ||
| 1200 | - else: | ||
| 1201 | - sign = "+" | ||
| 1202 | - hh, mm = divmod(off, timedelta(hours=1)) | ||
| 1203 | - mm, ss = divmod(mm, timedelta(minutes=1)) | ||
| 1204 | - assert 0 <= hh < 24 | ||
| 1205 | - off = "%s%02d%s%02d" % (sign, hh, sep, mm) | ||
| 1206 | - if ss: | ||
| 1207 | - off += ':%02d' % ss.seconds | ||
| 1208 | - return off | ||
| 1323 | + return _format_offset(off) | ||
| 1209 | 1324 | ||
| 1210 | 1325 | def __repr__(self): | |
| 1211 | 1326 | """Convert to formal string, for repr().""" | |
@@ -1244,6 +1359,18 @@ def isoformat(self, timespec='auto'): | |||
| 1244 | 1359 | ||
| 1245 | 1360 | __str__ = isoformat | |
| 1246 | 1361 | ||
| 1362 | + @classmethod | ||
| 1363 | + def fromisoformat(cls, time_string): | ||
| 1364 | + """Construct a time from the output of isoformat().""" | ||
| 1365 | + if not isinstance(time_string, str): | ||
| 1366 | + raise TypeError('fromisoformat: argument must be str') | ||
| 1367 | + | ||
| 1368 | + try: | ||
| 1369 | + return cls(*_parse_isoformat_time(time_string)) | ||
| 1370 | + except Exception: | ||
| 1371 | + raise ValueError('Invalid isoformat string: %s' % time_string) | ||
| 1372 | + | ||
| 1373 | + | ||
| 1247 | 1374 | def strftime(self, fmt): | |
| 1248 | 1375 | """Format using strftime(). The date part of the timestamp passed | |
| 1249 | 1376 | to underlying strftime should not be used. | |
@@ -1497,6 +1624,31 @@ def combine(cls, date, time, tzinfo=True): | |||
| 1497 | 1624 | time.hour, time.minute, time.second, time.microsecond, | |
| 1498 | 1625 | tzinfo, fold=time.fold) | |
| 1499 | 1626 | ||
| 1627 | + @classmethod | ||
| 1628 | + def fromisoformat(cls, date_string): | ||
| 1629 | + """Construct a datetime from the output of datetime.isoformat().""" | ||
| 1630 | + if not isinstance(date_string, str): | ||
| 1631 | + raise TypeError('fromisoformat: argument must be str') | ||
| 1632 | + | ||
| 1633 | + # Split this at the separator | ||
| 1634 | + dstr = date_string[0:10] | ||
| 1635 | + tstr = date_string[11:] | ||
| 1636 | + | ||
| 1637 | + try: | ||
| 1638 | + date_components = _parse_isoformat_date(dstr) | ||
| 1639 | + except ValueError: | ||
| 1640 | + raise ValueError('Invalid isoformat string: %s' % date_string) | ||
| 1641 | + | ||
| 1642 | + if tstr: | ||
| 1643 | + try: | ||
| 1644 | + time_components = _parse_isoformat_time(tstr) | ||
| 1645 | + except ValueError: | ||
| 1646 | + raise ValueError('Invalid isoformat string: %s' % date_string) | ||
| 1647 | + else: | ||
| 1648 | + time_components = [0, 0, 0, 0, None] | ||
| 1649 | + | ||
| 1650 | + return cls(*(date_components + time_components)) | ||
| 1651 | + | ||
| 1500 | 1652 | def timetuple(self): | |
| 1501 | 1653 | "Return local time tuple compatible with time.localtime()." | |
| 1502 | 1654 | dst = self.dst() | |
@@ -1673,18 +1825,10 @@ def isoformat(self, sep='T', timespec='auto'): | |||
| 1673 | 1825 | self._microsecond, timespec)) | |
| 1674 | 1826 | ||
| 1675 | 1827 | off = self.utcoffset() | |
| 1676 | - if off is not None: | ||
| 1677 | - if off.days < 0: | ||
| 1678 | - sign = "-" | ||
| 1679 | - off = -off | ||
| 1680 | - else: | ||
| 1681 | - sign = "+" | ||
| 1682 | - hh, mm = divmod(off, timedelta(hours=1)) | ||
| 1683 | - mm, ss = divmod(mm, timedelta(minutes=1)) | ||
| 1684 | - s += "%s%02d:%02d" % (sign, hh, mm) | ||
| 1685 | - if ss: | ||
| 1686 | - assert not ss.microseconds | ||
| 1687 | - s += ":%02d" % ss.seconds | ||
| 1828 | + tz = _format_offset(off) | ||
| 1829 | + if tz: | ||
| 1830 | + s += tz | ||
| 1831 | + | ||
| 1688 | 1832 | return s | |
| 1689 | 1833 | ||
| 1690 | 1834 | def __repr__(self): | |
@@ -2275,9 +2419,10 @@ def _name_from_offset(delta): | |||
| 2275 | 2419 | _check_date_fields, _check_int_field, _check_time_fields, | |
| 2276 | 2420 | _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror, | |
| 2277 | 2421 | _date_class, _days_before_month, _days_before_year, _days_in_month, | |
| 2278 | - _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd, | ||
| 2279 | - _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord, | ||
| 2280 | - _divide_and_round) | ||
| 2422 | + _format_time, _format_offset, _is_leap, _isoweek1monday, _math, | ||
| 2423 | + _ord2ymd, _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord, | ||
| 2424 | + _divide_and_round, _parse_isoformat_date, _parse_isoformat_time, | ||
| 2425 | + _parse_hh_mm_ss_ff) | ||
| 2281 | 2426 | # XXX Since import * above excludes names that start with _, | |
| 2282 | 2427 | # docstring does not get overwritten. In the future, it may be | |
| 2283 | 2428 | # appropriate to maintain a single module level docstring and | |
| Back | FazBrowse Home | New Git URL |
0 commit comments