mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
13925 port fromisoformat from python 3.11
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import decimal
|
import decimal
|
||||||
import re
|
import re
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date, timezone
|
||||||
|
|
||||||
import django_filters
|
import django_filters
|
||||||
from django import forms
|
from django import forms
|
||||||
@ -599,7 +599,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
|
|
||||||
return filter_instance
|
return filter_instance
|
||||||
|
|
||||||
def _parse_hh_mm_ss_ff(tstr):
|
def _parse_hh_mm_ss_ff(self, tstr):
|
||||||
# Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]
|
# Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]
|
||||||
# TODO: Remove when drop python 3.10
|
# TODO: Remove when drop python 3.10
|
||||||
len_str = len(tstr)
|
len_str = len(tstr)
|
||||||
@ -641,12 +641,48 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
|
|
||||||
time_comps[3] = int(tstr[pos:(pos + to_parse)])
|
time_comps[3] = int(tstr[pos:(pos + to_parse)])
|
||||||
if to_parse < 6:
|
if to_parse < 6:
|
||||||
|
_FRACTION_CORRECTION = [100000, 10000, 1000, 100, 10]
|
||||||
time_comps[3] *= _FRACTION_CORRECTION[to_parse - 1]
|
time_comps[3] *= _FRACTION_CORRECTION[to_parse - 1]
|
||||||
if (len_remainder > to_parse and not all(map(_is_ascii_digit, tstr[(pos + to_parse):]))):
|
if (len_remainder > to_parse and not all(map(_is_ascii_digit, tstr[(pos + to_parse):]))):
|
||||||
raise ValueError(_("Non-digit values in unparsed fraction"))
|
raise ValueError(_("Non-digit values in unparsed fraction"))
|
||||||
|
|
||||||
return time_comps
|
return time_comps
|
||||||
|
|
||||||
|
def _parse_isoformat_date(self, dtstr):
|
||||||
|
# It is assumed that this is an ASCII-only string of lengths 7, 8 or 10,
|
||||||
|
# see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator
|
||||||
|
assert len(dtstr) in (7, 8, 10)
|
||||||
|
year = int(dtstr[0:4])
|
||||||
|
has_sep = dtstr[4] == '-'
|
||||||
|
|
||||||
|
pos = 4 + has_sep
|
||||||
|
if dtstr[pos:pos + 1] == "W":
|
||||||
|
# YYYY-?Www-?D?
|
||||||
|
pos += 1
|
||||||
|
weekno = int(dtstr[pos:pos + 2])
|
||||||
|
pos += 2
|
||||||
|
|
||||||
|
dayno = 1
|
||||||
|
if len(dtstr) > pos:
|
||||||
|
if (dtstr[pos:pos + 1] == '-') != has_sep:
|
||||||
|
raise ValueError("Inconsistent use of dash separator")
|
||||||
|
|
||||||
|
pos += has_sep
|
||||||
|
|
||||||
|
dayno = int(dtstr[pos:pos + 1])
|
||||||
|
|
||||||
|
return list(datetime._isoweek_to_gregorian(year, weekno, dayno))
|
||||||
|
else:
|
||||||
|
month = int(dtstr[pos:pos + 2])
|
||||||
|
pos += 2
|
||||||
|
if (dtstr[pos:pos + 1] == "-") != has_sep:
|
||||||
|
raise ValueError("Inconsistent use of dash separator")
|
||||||
|
|
||||||
|
pos += has_sep
|
||||||
|
day = int(dtstr[pos:pos + 2])
|
||||||
|
|
||||||
|
return [year, month, day]
|
||||||
|
|
||||||
def _parse_isoformat_time(self, tstr):
|
def _parse_isoformat_time(self, tstr):
|
||||||
# Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]
|
# Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]
|
||||||
# TODO: Remove when drop python 3.10
|
# TODO: Remove when drop python 3.10
|
||||||
@ -689,25 +725,101 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
hours=tz_comps[0], minutes=tz_comps[1],
|
hours=tz_comps[0], minutes=tz_comps[1],
|
||||||
seconds=tz_comps[2], microseconds=tz_comps[3])
|
seconds=tz_comps[2], microseconds=tz_comps[3])
|
||||||
|
|
||||||
tzi = datetime.timezone(tzsign * td)
|
tzi = timezone(tzsign * td)
|
||||||
|
|
||||||
time_comps.append(tzi)
|
time_comps.append(tzi)
|
||||||
|
|
||||||
return time_comps
|
return time_comps
|
||||||
|
|
||||||
|
# Helpers for parsing the result of isoformat()
|
||||||
|
def _is_ascii_digit(self, c):
|
||||||
|
return c in "0123456789"
|
||||||
|
|
||||||
|
def _find_isoformat_datetime_separator(self, dtstr):
|
||||||
|
# See the comment in _datetimemodule.c:_find_isoformat_datetime_separator
|
||||||
|
len_dtstr = len(dtstr)
|
||||||
|
if len_dtstr == 7:
|
||||||
|
return 7
|
||||||
|
|
||||||
|
assert len_dtstr > 7
|
||||||
|
date_separator = "-"
|
||||||
|
week_indicator = "W"
|
||||||
|
|
||||||
|
if dtstr[4] == date_separator:
|
||||||
|
if dtstr[5] == week_indicator:
|
||||||
|
if len_dtstr < 8:
|
||||||
|
raise ValueError("Invalid ISO string")
|
||||||
|
if len_dtstr > 8 and dtstr[8] == date_separator:
|
||||||
|
if len_dtstr == 9:
|
||||||
|
raise ValueError("Invalid ISO string")
|
||||||
|
if len_dtstr > 10 and self._is_ascii_digit(dtstr[10]):
|
||||||
|
# This is as far as we need to resolve the ambiguity for
|
||||||
|
# the moment - if we have YYYY-Www-##, the separator is
|
||||||
|
# either a hyphen at 8 or a number at 10.
|
||||||
|
#
|
||||||
|
# We'll assume it's a hyphen at 8 because it's way more
|
||||||
|
# likely that someone will use a hyphen as a separator than
|
||||||
|
# a number, but at this point it's really best effort
|
||||||
|
# because this is an extension of the spec anyway.
|
||||||
|
# TODO(pganssle): Document this
|
||||||
|
return 8
|
||||||
|
return 10
|
||||||
|
else:
|
||||||
|
# YYYY-Www (8)
|
||||||
|
return 8
|
||||||
|
else:
|
||||||
|
# YYYY-MM-DD (10)
|
||||||
|
return 10
|
||||||
|
else:
|
||||||
|
if dtstr[4] == week_indicator:
|
||||||
|
# YYYYWww (7) or YYYYWwwd (8)
|
||||||
|
idx = 7
|
||||||
|
while idx < len_dtstr:
|
||||||
|
if not self._is_ascii_digit(dtstr[idx]):
|
||||||
|
break
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
if idx < 9:
|
||||||
|
return idx
|
||||||
|
|
||||||
|
if idx % 2 == 0:
|
||||||
|
# If the index of the last number is even, it's YYYYWwwd
|
||||||
|
return 7
|
||||||
|
else:
|
||||||
|
return 8
|
||||||
|
else:
|
||||||
|
# YYYYMMDD (8)
|
||||||
|
return 8
|
||||||
|
|
||||||
def fromisoformat(self, date_string):
|
def fromisoformat(self, date_string):
|
||||||
"""Construct a date from a string in ISO 8601 format."""
|
"""Construct a datetime from a string in one of the ISO 8601 formats."""
|
||||||
# TODO: Remove when drop python 3.10
|
|
||||||
if not isinstance(date_string, str):
|
if not isinstance(date_string, str):
|
||||||
raise TypeError(_('fromisoformat: argument must be str'))
|
raise TypeError('fromisoformat: argument must be str')
|
||||||
|
|
||||||
if len(date_string) not in (7, 8, 10):
|
if len(date_string) < 7:
|
||||||
raise ValueError(_('Invalid isoformat string'))
|
raise ValueError(f'Invalid isoformat string: {date_string!r}')
|
||||||
|
|
||||||
|
# Split this at the separator
|
||||||
try:
|
try:
|
||||||
return self._parse_isoformat_date(date_string)
|
separator_location = self._find_isoformat_datetime_separator(date_string)
|
||||||
except Exception:
|
dstr = date_string[0:separator_location]
|
||||||
raise ValueError(_('Invalid isoformat string'))
|
tstr = date_string[(separator_location + 1):]
|
||||||
|
|
||||||
|
date_components = self._parse_isoformat_date(dstr)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid isoformat string: {date_string!r}') from None
|
||||||
|
|
||||||
|
if tstr:
|
||||||
|
try:
|
||||||
|
time_components = self._parse_isoformat_time(tstr)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid isoformat string: {date_string!r}') from None
|
||||||
|
else:
|
||||||
|
time_components = [0, 0, 0, 0, None]
|
||||||
|
|
||||||
|
return (date_components + time_components)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user