second commit
This commit is contained in:
103
env/lib/python3.11/site-packages/isodate/__init__.py
vendored
Normal file
103
env/lib/python3.11/site-packages/isodate/__init__.py
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
"""
|
||||
Import all essential functions and constants to re-export them here for easy
|
||||
access.
|
||||
|
||||
This module contains also various pre-defined ISO 8601 format strings.
|
||||
"""
|
||||
|
||||
from isodate.duration import Duration
|
||||
from isodate.isodates import date_isoformat, parse_date
|
||||
from isodate.isodatetime import datetime_isoformat, parse_datetime
|
||||
from isodate.isoduration import duration_isoformat, parse_duration
|
||||
from isodate.isoerror import ISO8601Error
|
||||
from isodate.isostrf import (
|
||||
D_ALT_BAS,
|
||||
D_ALT_BAS_ORD,
|
||||
D_ALT_EXT,
|
||||
D_ALT_EXT_ORD,
|
||||
D_DEFAULT,
|
||||
D_WEEK,
|
||||
DATE_BAS_COMPLETE,
|
||||
DATE_BAS_MONTH,
|
||||
DATE_BAS_ORD_COMPLETE,
|
||||
DATE_BAS_WEEK,
|
||||
DATE_BAS_WEEK_COMPLETE,
|
||||
DATE_CENTURY,
|
||||
DATE_EXT_COMPLETE,
|
||||
DATE_EXT_MONTH,
|
||||
DATE_EXT_ORD_COMPLETE,
|
||||
DATE_EXT_WEEK,
|
||||
DATE_EXT_WEEK_COMPLETE,
|
||||
DATE_YEAR,
|
||||
DT_BAS_COMPLETE,
|
||||
DT_BAS_ORD_COMPLETE,
|
||||
DT_BAS_WEEK_COMPLETE,
|
||||
DT_EXT_COMPLETE,
|
||||
DT_EXT_ORD_COMPLETE,
|
||||
DT_EXT_WEEK_COMPLETE,
|
||||
TIME_BAS_COMPLETE,
|
||||
TIME_BAS_MINUTE,
|
||||
TIME_EXT_COMPLETE,
|
||||
TIME_EXT_MINUTE,
|
||||
TIME_HOUR,
|
||||
TZ_BAS,
|
||||
TZ_EXT,
|
||||
TZ_HOUR,
|
||||
strftime,
|
||||
)
|
||||
from isodate.isotime import parse_time, time_isoformat
|
||||
from isodate.isotzinfo import parse_tzinfo, tz_isoformat
|
||||
from isodate.tzinfo import LOCAL, UTC, FixedOffset
|
||||
from isodate.version import version as __version__
|
||||
|
||||
__all__ = [
|
||||
"parse_date",
|
||||
"date_isoformat",
|
||||
"parse_time",
|
||||
"time_isoformat",
|
||||
"parse_datetime",
|
||||
"datetime_isoformat",
|
||||
"parse_duration",
|
||||
"duration_isoformat",
|
||||
"ISO8601Error",
|
||||
"parse_tzinfo",
|
||||
"tz_isoformat",
|
||||
"UTC",
|
||||
"FixedOffset",
|
||||
"LOCAL",
|
||||
"Duration",
|
||||
"strftime",
|
||||
"DATE_BAS_COMPLETE",
|
||||
"DATE_BAS_ORD_COMPLETE",
|
||||
"DATE_BAS_WEEK",
|
||||
"DATE_BAS_WEEK_COMPLETE",
|
||||
"DATE_CENTURY",
|
||||
"DATE_EXT_COMPLETE",
|
||||
"DATE_EXT_ORD_COMPLETE",
|
||||
"DATE_EXT_WEEK",
|
||||
"DATE_EXT_WEEK_COMPLETE",
|
||||
"DATE_YEAR",
|
||||
"DATE_BAS_MONTH",
|
||||
"DATE_EXT_MONTH",
|
||||
"TIME_BAS_COMPLETE",
|
||||
"TIME_BAS_MINUTE",
|
||||
"TIME_EXT_COMPLETE",
|
||||
"TIME_EXT_MINUTE",
|
||||
"TIME_HOUR",
|
||||
"TZ_BAS",
|
||||
"TZ_EXT",
|
||||
"TZ_HOUR",
|
||||
"DT_BAS_COMPLETE",
|
||||
"DT_EXT_COMPLETE",
|
||||
"DT_BAS_ORD_COMPLETE",
|
||||
"DT_EXT_ORD_COMPLETE",
|
||||
"DT_BAS_WEEK_COMPLETE",
|
||||
"DT_EXT_WEEK_COMPLETE",
|
||||
"D_DEFAULT",
|
||||
"D_WEEK",
|
||||
"D_ALT_EXT",
|
||||
"D_ALT_BAS",
|
||||
"D_ALT_BAS_ORD",
|
||||
"D_ALT_EXT_ORD",
|
||||
"__version__",
|
||||
]
|
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/__init__.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/__init__.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/duration.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/duration.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isodates.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isodates.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isodatetime.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isodatetime.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isoduration.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isoduration.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isoerror.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isoerror.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isostrf.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isostrf.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isotime.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isotime.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isotzinfo.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/isotzinfo.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/tzinfo.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/tzinfo.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/version.cpython-311.pyc
vendored
Normal file
BIN
env/lib/python3.11/site-packages/isodate/__pycache__/version.cpython-311.pyc
vendored
Normal file
Binary file not shown.
316
env/lib/python3.11/site-packages/isodate/duration.py
vendored
Normal file
316
env/lib/python3.11/site-packages/isodate/duration.py
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
"""
|
||||
This module defines a Duration class.
|
||||
|
||||
The class Duration allows to define durations in years and months and can be
|
||||
used as limited replacement for timedelta objects.
|
||||
"""
|
||||
|
||||
from datetime import timedelta
|
||||
from decimal import ROUND_FLOOR, Decimal
|
||||
|
||||
|
||||
def fquotmod(val, low, high):
|
||||
"""
|
||||
A divmod function with boundaries.
|
||||
|
||||
"""
|
||||
# assumes that all the maths is done with Decimals.
|
||||
# divmod for Decimal uses truncate instead of floor as builtin
|
||||
# divmod, so we have to do it manually here.
|
||||
a, b = val - low, high - low
|
||||
div = (a / b).to_integral(ROUND_FLOOR)
|
||||
mod = a - div * b
|
||||
# if we were not using Decimal, it would look like this.
|
||||
# div, mod = divmod(val - low, high - low)
|
||||
mod += low
|
||||
return int(div), mod
|
||||
|
||||
|
||||
def max_days_in_month(year, month):
|
||||
"""
|
||||
Determines the number of days of a specific month in a specific year.
|
||||
"""
|
||||
if month in (1, 3, 5, 7, 8, 10, 12):
|
||||
return 31
|
||||
if month in (4, 6, 9, 11):
|
||||
return 30
|
||||
if ((year % 400) == 0) or ((year % 100) != 0) and ((year % 4) == 0):
|
||||
return 29
|
||||
return 28
|
||||
|
||||
|
||||
class Duration:
|
||||
"""
|
||||
A class which represents a duration.
|
||||
|
||||
The difference to datetime.timedelta is, that this class handles also
|
||||
differences given in years and months.
|
||||
A Duration treats differences given in year, months separately from all
|
||||
other components.
|
||||
|
||||
A Duration can be used almost like any timedelta object, however there
|
||||
are some restrictions:
|
||||
* It is not really possible to compare Durations, because it is unclear,
|
||||
whether a duration of 1 year is bigger than 365 days or not.
|
||||
* Equality is only tested between the two (year, month vs. timedelta)
|
||||
basic components.
|
||||
|
||||
A Duration can also be converted into a datetime object, but this requires
|
||||
a start date or an end date.
|
||||
|
||||
The algorithm to add a duration to a date is defined at
|
||||
http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
days=0,
|
||||
seconds=0,
|
||||
microseconds=0,
|
||||
milliseconds=0,
|
||||
minutes=0,
|
||||
hours=0,
|
||||
weeks=0,
|
||||
months=0,
|
||||
years=0,
|
||||
):
|
||||
"""
|
||||
Initialise this Duration instance with the given parameters.
|
||||
"""
|
||||
if not isinstance(months, Decimal):
|
||||
months = Decimal(str(months))
|
||||
if not isinstance(years, Decimal):
|
||||
years = Decimal(str(years))
|
||||
self.months = months
|
||||
self.years = years
|
||||
self.tdelta = timedelta(
|
||||
days, seconds, microseconds, milliseconds, minutes, hours, weeks
|
||||
)
|
||||
|
||||
def __getstate__(self):
|
||||
return self.__dict__
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__dict__.update(state)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Provide direct access to attributes of included timedelta instance.
|
||||
"""
|
||||
return getattr(self.tdelta, name)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Return a string representation of this duration similar to timedelta.
|
||||
"""
|
||||
params = []
|
||||
if self.years:
|
||||
params.append("%d years" % self.years)
|
||||
if self.months:
|
||||
fmt = "%d months"
|
||||
if self.months <= 1:
|
||||
fmt = "%d month"
|
||||
params.append(fmt % self.months)
|
||||
params.append(str(self.tdelta))
|
||||
return ", ".join(params)
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Return a string suitable for repr(x) calls.
|
||||
"""
|
||||
return "%s.%s(%d, %d, %d, years=%d, months=%d)" % (
|
||||
self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
self.tdelta.days,
|
||||
self.tdelta.seconds,
|
||||
self.tdelta.microseconds,
|
||||
self.years,
|
||||
self.months,
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
"""
|
||||
Return a hash of this instance so that it can be used in, for
|
||||
example, dicts and sets.
|
||||
"""
|
||||
return hash((self.tdelta, self.months, self.years))
|
||||
|
||||
def __neg__(self):
|
||||
"""
|
||||
A simple unary minus.
|
||||
|
||||
Returns a new Duration instance with all it's negated.
|
||||
"""
|
||||
negduration = Duration(years=-self.years, months=-self.months)
|
||||
negduration.tdelta = -self.tdelta
|
||||
return negduration
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
Durations can be added with Duration, timedelta, date and datetime
|
||||
objects.
|
||||
"""
|
||||
if isinstance(other, Duration):
|
||||
newduration = Duration(
|
||||
years=self.years + other.years, months=self.months + other.months
|
||||
)
|
||||
newduration.tdelta = self.tdelta + other.tdelta
|
||||
return newduration
|
||||
try:
|
||||
# try anything that looks like a date or datetime
|
||||
# 'other' has attributes year, month, day
|
||||
# and relies on 'timedelta + other' being implemented
|
||||
if not (float(self.years).is_integer() and float(self.months).is_integer()):
|
||||
raise ValueError(
|
||||
"fractional years or months not supported" " for date calculations"
|
||||
)
|
||||
newmonth = other.month + self.months
|
||||
carry, newmonth = fquotmod(newmonth, 1, 13)
|
||||
newyear = other.year + self.years + carry
|
||||
maxdays = max_days_in_month(newyear, newmonth)
|
||||
if other.day > maxdays:
|
||||
newday = maxdays
|
||||
else:
|
||||
newday = other.day
|
||||
newdt = other.replace(
|
||||
year=int(newyear), month=int(newmonth), day=int(newday)
|
||||
)
|
||||
# does a timedelta + date/datetime
|
||||
return self.tdelta + newdt
|
||||
except AttributeError:
|
||||
# other probably was not a date/datetime compatible object
|
||||
pass
|
||||
try:
|
||||
# try if other is a timedelta
|
||||
# relies on timedelta + timedelta supported
|
||||
newduration = Duration(years=self.years, months=self.months)
|
||||
newduration.tdelta = self.tdelta + other
|
||||
return newduration
|
||||
except AttributeError:
|
||||
# ignore ... other probably was not a timedelta compatible object
|
||||
pass
|
||||
# we have tried everything .... return a NotImplemented
|
||||
return NotImplemented
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __mul__(self, other):
|
||||
if isinstance(other, int):
|
||||
newduration = Duration(years=self.years * other, months=self.months * other)
|
||||
newduration.tdelta = self.tdelta * other
|
||||
return newduration
|
||||
return NotImplemented
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __sub__(self, other):
|
||||
"""
|
||||
It is possible to subtract Duration and timedelta objects from Duration
|
||||
objects.
|
||||
"""
|
||||
if isinstance(other, Duration):
|
||||
newduration = Duration(
|
||||
years=self.years - other.years, months=self.months - other.months
|
||||
)
|
||||
newduration.tdelta = self.tdelta - other.tdelta
|
||||
return newduration
|
||||
try:
|
||||
# do maths with our timedelta object ....
|
||||
newduration = Duration(years=self.years, months=self.months)
|
||||
newduration.tdelta = self.tdelta - other
|
||||
return newduration
|
||||
except TypeError:
|
||||
# looks like timedelta - other is not implemented
|
||||
pass
|
||||
return NotImplemented
|
||||
|
||||
def __rsub__(self, other):
|
||||
"""
|
||||
It is possible to subtract Duration objects from date, datetime and
|
||||
timedelta objects.
|
||||
|
||||
TODO: there is some weird behaviour in date - timedelta ...
|
||||
if timedelta has seconds or microseconds set, then
|
||||
date - timedelta != date + (-timedelta)
|
||||
for now we follow this behaviour to avoid surprises when mixing
|
||||
timedeltas with Durations, but in case this ever changes in
|
||||
the stdlib we can just do:
|
||||
return -self + other
|
||||
instead of all the current code
|
||||
"""
|
||||
if isinstance(other, timedelta):
|
||||
tmpdur = Duration()
|
||||
tmpdur.tdelta = other
|
||||
return tmpdur - self
|
||||
try:
|
||||
# check if other behaves like a date/datetime object
|
||||
# does it have year, month, day and replace?
|
||||
if not (float(self.years).is_integer() and float(self.months).is_integer()):
|
||||
raise ValueError(
|
||||
"fractional years or months not supported" " for date calculations"
|
||||
)
|
||||
newmonth = other.month - self.months
|
||||
carry, newmonth = fquotmod(newmonth, 1, 13)
|
||||
newyear = other.year - self.years + carry
|
||||
maxdays = max_days_in_month(newyear, newmonth)
|
||||
if other.day > maxdays:
|
||||
newday = maxdays
|
||||
else:
|
||||
newday = other.day
|
||||
newdt = other.replace(
|
||||
year=int(newyear), month=int(newmonth), day=int(newday)
|
||||
)
|
||||
return newdt - self.tdelta
|
||||
except AttributeError:
|
||||
# other probably was not compatible with data/datetime
|
||||
pass
|
||||
return NotImplemented
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
If the years, month part and the timedelta part are both equal, then
|
||||
the two Durations are considered equal.
|
||||
"""
|
||||
if isinstance(other, Duration):
|
||||
if (self.years * 12 + self.months) == (
|
||||
other.years * 12 + other.months
|
||||
) and self.tdelta == other.tdelta:
|
||||
return True
|
||||
return False
|
||||
# check if other con be compared against timedelta object
|
||||
# will raise an AssertionError when optimisation is off
|
||||
if self.years == 0 and self.months == 0:
|
||||
return self.tdelta == other
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
"""
|
||||
If the years, month part or the timedelta part is not equal, then
|
||||
the two Durations are considered not equal.
|
||||
"""
|
||||
if isinstance(other, Duration):
|
||||
if (self.years * 12 + self.months) != (
|
||||
other.years * 12 + other.months
|
||||
) or self.tdelta != other.tdelta:
|
||||
return True
|
||||
return False
|
||||
# check if other can be compared against timedelta object
|
||||
# will raise an AssertionError when optimisation is off
|
||||
if self.years == 0 and self.months == 0:
|
||||
return self.tdelta != other
|
||||
return True
|
||||
|
||||
def totimedelta(self, start=None, end=None):
|
||||
"""
|
||||
Convert this duration into a timedelta object.
|
||||
|
||||
This method requires a start datetime or end datetimem, but raises
|
||||
an exception if both are given.
|
||||
"""
|
||||
if start is None and end is None:
|
||||
raise ValueError("start or end required")
|
||||
if start is not None and end is not None:
|
||||
raise ValueError("only start or end allowed")
|
||||
if start is not None:
|
||||
return (start + self) - start
|
||||
return end - (end - self)
|
203
env/lib/python3.11/site-packages/isodate/isodates.py
vendored
Normal file
203
env/lib/python3.11/site-packages/isodate/isodates.py
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
"""
|
||||
This modules provides a method to parse an ISO 8601:2004 date string to a
|
||||
python datetime.date instance.
|
||||
|
||||
It supports all basic, extended and expanded formats as described in the ISO
|
||||
standard. The only limitations it has, are given by the Python datetime.date
|
||||
implementation, which does not support dates before 0001-01-01.
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import date, timedelta
|
||||
|
||||
from isodate.isoerror import ISO8601Error
|
||||
from isodate.isostrf import DATE_EXT_COMPLETE, strftime
|
||||
|
||||
DATE_REGEX_CACHE = {}
|
||||
# A dictionary to cache pre-compiled regular expressions.
|
||||
# A set of regular expressions is identified, by number of year digits allowed
|
||||
# and whether a plus/minus sign is required or not. (This option is changeable
|
||||
# only for 4 digit years).
|
||||
|
||||
|
||||
def build_date_regexps(yeardigits=4, expanded=False):
|
||||
"""
|
||||
Compile set of regular expressions to parse ISO dates. The expressions will
|
||||
be created only if they are not already in REGEX_CACHE.
|
||||
|
||||
It is necessary to fix the number of year digits, else it is not possible
|
||||
to automatically distinguish between various ISO date formats.
|
||||
|
||||
ISO 8601 allows more than 4 digit years, on prior agreement, but then a +/-
|
||||
sign is required (expanded format). To support +/- sign for 4 digit years,
|
||||
the expanded parameter needs to be set to True.
|
||||
"""
|
||||
if yeardigits != 4:
|
||||
expanded = True
|
||||
if (yeardigits, expanded) not in DATE_REGEX_CACHE:
|
||||
cache_entry = []
|
||||
# ISO 8601 expanded DATE formats allow an arbitrary number of year
|
||||
# digits with a leading +/- sign.
|
||||
if expanded:
|
||||
sign = 1
|
||||
else:
|
||||
sign = 0
|
||||
|
||||
def add_re(regex_text):
|
||||
cache_entry.append(re.compile(r"\A" + regex_text + r"\Z"))
|
||||
|
||||
# 1. complete dates:
|
||||
# YYYY-MM-DD or +- YYYYYY-MM-DD... extended date format
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})" % (sign, yeardigits)
|
||||
)
|
||||
# YYYYMMDD or +- YYYYYYMMDD... basic date format
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"(?P<month>[0-9]{2})(?P<day>[0-9]{2})" % (sign, yeardigits)
|
||||
)
|
||||
# 2. complete week dates:
|
||||
# YYYY-Www-D or +-YYYYYY-Www-D ... extended week date
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"-W(?P<week>[0-9]{2})-(?P<day>[0-9]{1})" % (sign, yeardigits)
|
||||
)
|
||||
# YYYYWwwD or +-YYYYYYWwwD ... basic week date
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
|
||||
r"(?P<week>[0-9]{2})(?P<day>[0-9]{1})" % (sign, yeardigits)
|
||||
)
|
||||
# 3. ordinal dates:
|
||||
# YYYY-DDD or +-YYYYYY-DDD ... extended format
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"-(?P<day>[0-9]{3})" % (sign, yeardigits)
|
||||
)
|
||||
# YYYYDDD or +-YYYYYYDDD ... basic format
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"(?P<day>[0-9]{3})" % (sign, yeardigits)
|
||||
)
|
||||
# 4. week dates:
|
||||
# YYYY-Www or +-YYYYYY-Www ... extended reduced accuracy week date
|
||||
# 4. week dates:
|
||||
# YYYY-Www or +-YYYYYY-Www ... extended reduced accuracy week date
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"-W(?P<week>[0-9]{2})" % (sign, yeardigits)
|
||||
)
|
||||
# YYYYWww or +-YYYYYYWww ... basic reduced accuracy week date
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
|
||||
r"(?P<week>[0-9]{2})" % (sign, yeardigits)
|
||||
)
|
||||
# 5. month dates:
|
||||
# YYY-MM or +-YYYYYY-MM ... reduced accuracy specific month
|
||||
# 5. month dates:
|
||||
# YYY-MM or +-YYYYYY-MM ... reduced accuracy specific month
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"-(?P<month>[0-9]{2})" % (sign, yeardigits)
|
||||
)
|
||||
# YYYMM or +-YYYYYYMM ... basic incomplete month date format
|
||||
add_re(
|
||||
r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
|
||||
r"(?P<month>[0-9]{2})" % (sign, yeardigits)
|
||||
)
|
||||
# 6. year dates:
|
||||
# YYYY or +-YYYYYY ... reduced accuracy specific year
|
||||
add_re(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})" % (sign, yeardigits))
|
||||
# 7. century dates:
|
||||
# YY or +-YYYY ... reduced accuracy specific century
|
||||
add_re(r"(?P<sign>[+-]){%d}" r"(?P<century>[0-9]{%d})" % (sign, yeardigits - 2))
|
||||
|
||||
DATE_REGEX_CACHE[(yeardigits, expanded)] = cache_entry
|
||||
return DATE_REGEX_CACHE[(yeardigits, expanded)]
|
||||
|
||||
|
||||
def parse_date(datestring, yeardigits=4, expanded=False, defaultmonth=1, defaultday=1):
|
||||
"""
|
||||
Parse an ISO 8601 date string into a datetime.date object.
|
||||
|
||||
As the datetime.date implementation is limited to dates starting from
|
||||
0001-01-01, negative dates (BC) and year 0 can not be parsed by this
|
||||
method.
|
||||
|
||||
For incomplete dates, this method chooses the first day for it. For
|
||||
instance if only a century is given, this method returns the 1st of
|
||||
January in year 1 of this century.
|
||||
|
||||
supported formats: (expanded formats are shown with 6 digits for year)
|
||||
YYYYMMDD +-YYYYYYMMDD basic complete date
|
||||
YYYY-MM-DD +-YYYYYY-MM-DD extended complete date
|
||||
YYYYWwwD +-YYYYYYWwwD basic complete week date
|
||||
YYYY-Www-D +-YYYYYY-Www-D extended complete week date
|
||||
YYYYDDD +-YYYYYYDDD basic ordinal date
|
||||
YYYY-DDD +-YYYYYY-DDD extended ordinal date
|
||||
YYYYWww +-YYYYYYWww basic incomplete week date
|
||||
YYYY-Www +-YYYYYY-Www extended incomplete week date
|
||||
YYYMM +-YYYYYYMM basic incomplete month date
|
||||
YYY-MM +-YYYYYY-MM incomplete month date
|
||||
YYYY +-YYYYYY incomplete year date
|
||||
YY +-YYYY incomplete century date
|
||||
|
||||
@param datestring: the ISO date string to parse
|
||||
@param yeardigits: how many digits are used to represent a year
|
||||
@param expanded: if True then +/- signs are allowed. This parameter
|
||||
is forced to True, if yeardigits != 4
|
||||
|
||||
@return: a datetime.date instance represented by datestring
|
||||
@raise ISO8601Error: if this function can not parse the datestring
|
||||
@raise ValueError: if datestring can not be represented by datetime.date
|
||||
"""
|
||||
if yeardigits != 4:
|
||||
expanded = True
|
||||
isodates = build_date_regexps(yeardigits, expanded)
|
||||
for pattern in isodates:
|
||||
match = pattern.match(datestring)
|
||||
if match:
|
||||
groups = match.groupdict()
|
||||
# sign, century, year, month, week, day,
|
||||
# FIXME: negative dates not possible with python standard types
|
||||
sign = (groups["sign"] == "-" and -1) or 1
|
||||
if "century" in groups:
|
||||
return date(
|
||||
sign * (int(groups["century"]) * 100 + 1), defaultmonth, defaultday
|
||||
)
|
||||
if "month" not in groups: # weekdate or ordinal date
|
||||
ret = date(sign * int(groups["year"]), 1, 1)
|
||||
if "week" in groups:
|
||||
isotuple = ret.isocalendar()
|
||||
if "day" in groups:
|
||||
days = int(groups["day"] or 1)
|
||||
else:
|
||||
days = 1
|
||||
# if first week in year, do weeks-1
|
||||
return ret + timedelta(
|
||||
weeks=int(groups["week"]) - (((isotuple[1] == 1) and 1) or 0),
|
||||
days=-isotuple[2] + days,
|
||||
)
|
||||
elif "day" in groups: # ordinal date
|
||||
return ret + timedelta(days=int(groups["day"]) - 1)
|
||||
else: # year date
|
||||
return ret.replace(month=defaultmonth, day=defaultday)
|
||||
# year-, month-, or complete date
|
||||
if "day" not in groups or groups["day"] is None:
|
||||
day = defaultday
|
||||
else:
|
||||
day = int(groups["day"])
|
||||
return date(
|
||||
sign * int(groups["year"]), int(groups["month"]) or defaultmonth, day
|
||||
)
|
||||
raise ISO8601Error("Unrecognised ISO 8601 date format: %r" % datestring)
|
||||
|
||||
|
||||
def date_isoformat(tdate, format=DATE_EXT_COMPLETE, yeardigits=4):
|
||||
"""
|
||||
Format date strings.
|
||||
|
||||
This method is just a wrapper around isodate.isostrf.strftime and uses
|
||||
Date-Extended-Complete as default format.
|
||||
"""
|
||||
return strftime(tdate, format, yeardigits)
|
45
env/lib/python3.11/site-packages/isodate/isodatetime.py
vendored
Normal file
45
env/lib/python3.11/site-packages/isodate/isodatetime.py
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
This module defines a method to parse an ISO 8601:2004 date time string.
|
||||
|
||||
For this job it uses the parse_date and parse_time methods defined in date
|
||||
and time module.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from isodate.isodates import parse_date
|
||||
from isodate.isoerror import ISO8601Error
|
||||
from isodate.isostrf import DATE_EXT_COMPLETE, TIME_EXT_COMPLETE, TZ_EXT, strftime
|
||||
from isodate.isotime import parse_time
|
||||
|
||||
|
||||
def parse_datetime(datetimestring):
|
||||
"""
|
||||
Parses ISO 8601 date-times into datetime.datetime objects.
|
||||
|
||||
This function uses parse_date and parse_time to do the job, so it allows
|
||||
more combinations of date and time representations, than the actual
|
||||
ISO 8601:2004 standard allows.
|
||||
"""
|
||||
try:
|
||||
datestring, timestring = datetimestring.split("T")
|
||||
except ValueError:
|
||||
raise ISO8601Error(
|
||||
"ISO 8601 time designator 'T' missing. Unable to"
|
||||
" parse datetime string %r" % datetimestring
|
||||
)
|
||||
tmpdate = parse_date(datestring)
|
||||
tmptime = parse_time(timestring)
|
||||
return datetime.combine(tmpdate, tmptime)
|
||||
|
||||
|
||||
def datetime_isoformat(
|
||||
tdt, format=DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
|
||||
):
|
||||
"""
|
||||
Format datetime strings.
|
||||
|
||||
This method is just a wrapper around isodate.isostrf.strftime and uses
|
||||
Extended-Complete as default format.
|
||||
"""
|
||||
return strftime(tdt, format)
|
147
env/lib/python3.11/site-packages/isodate/isoduration.py
vendored
Normal file
147
env/lib/python3.11/site-packages/isodate/isoduration.py
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
"""
|
||||
This module provides an ISO 8601:2004 duration parser.
|
||||
|
||||
It also provides a wrapper to strftime. This wrapper makes it easier to
|
||||
format timedelta or Duration instances as ISO conforming strings.
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from isodate.duration import Duration
|
||||
from isodate.isodatetime import parse_datetime
|
||||
from isodate.isoerror import ISO8601Error
|
||||
from isodate.isostrf import D_DEFAULT, strftime
|
||||
|
||||
ISO8601_PERIOD_REGEX = re.compile(
|
||||
r"^(?P<sign>[+-])?"
|
||||
r"P(?!\b)"
|
||||
r"(?P<years>[0-9]+([,.][0-9]+)?Y)?"
|
||||
r"(?P<months>[0-9]+([,.][0-9]+)?M)?"
|
||||
r"(?P<weeks>[0-9]+([,.][0-9]+)?W)?"
|
||||
r"(?P<days>[0-9]+([,.][0-9]+)?D)?"
|
||||
r"((?P<separator>T)(?P<hours>[0-9]+([,.][0-9]+)?H)?"
|
||||
r"(?P<minutes>[0-9]+([,.][0-9]+)?M)?"
|
||||
r"(?P<seconds>[0-9]+([,.][0-9]+)?S)?)?$"
|
||||
)
|
||||
# regular expression to parse ISO duration strings.
|
||||
|
||||
|
||||
def parse_duration(datestring, as_timedelta_if_possible=True):
|
||||
"""
|
||||
Parses an ISO 8601 durations into datetime.timedelta or Duration objects.
|
||||
|
||||
If the ISO date string does not contain years or months, a timedelta
|
||||
instance is returned, else a Duration instance is returned.
|
||||
|
||||
The following duration formats are supported:
|
||||
-PnnW duration in weeks
|
||||
-PnnYnnMnnDTnnHnnMnnS complete duration specification
|
||||
-PYYYYMMDDThhmmss basic alternative complete date format
|
||||
-PYYYY-MM-DDThh:mm:ss extended alternative complete date format
|
||||
-PYYYYDDDThhmmss basic alternative ordinal date format
|
||||
-PYYYY-DDDThh:mm:ss extended alternative ordinal date format
|
||||
|
||||
The '-' is optional.
|
||||
|
||||
Limitations: ISO standard defines some restrictions about where to use
|
||||
fractional numbers and which component and format combinations are
|
||||
allowed. This parser implementation ignores all those restrictions and
|
||||
returns something when it is able to find all necessary components.
|
||||
In detail:
|
||||
it does not check, whether only the last component has fractions.
|
||||
it allows weeks specified with all other combinations
|
||||
|
||||
The alternative format does not support durations with years, months or
|
||||
days set to 0.
|
||||
"""
|
||||
if not isinstance(datestring, str):
|
||||
raise TypeError("Expecting a string %r" % datestring)
|
||||
match = ISO8601_PERIOD_REGEX.match(datestring)
|
||||
if not match:
|
||||
# try alternative format:
|
||||
if datestring.startswith("P"):
|
||||
durdt = parse_datetime(datestring[1:])
|
||||
if as_timedelta_if_possible and durdt.year == 0 and durdt.month == 0:
|
||||
# FIXME: currently not possible in alternative format
|
||||
# create timedelta
|
||||
ret = timedelta(
|
||||
days=durdt.day,
|
||||
seconds=durdt.second,
|
||||
microseconds=durdt.microsecond,
|
||||
minutes=durdt.minute,
|
||||
hours=durdt.hour,
|
||||
)
|
||||
else:
|
||||
# create Duration
|
||||
ret = Duration(
|
||||
days=durdt.day,
|
||||
seconds=durdt.second,
|
||||
microseconds=durdt.microsecond,
|
||||
minutes=durdt.minute,
|
||||
hours=durdt.hour,
|
||||
months=durdt.month,
|
||||
years=durdt.year,
|
||||
)
|
||||
return ret
|
||||
raise ISO8601Error("Unable to parse duration string %r" % datestring)
|
||||
groups = match.groupdict()
|
||||
for key, val in groups.items():
|
||||
if key not in ("separator", "sign"):
|
||||
if val is None:
|
||||
groups[key] = "0n"
|
||||
# print groups[key]
|
||||
if key in ("years", "months"):
|
||||
groups[key] = Decimal(groups[key][:-1].replace(",", "."))
|
||||
else:
|
||||
# these values are passed into a timedelta object,
|
||||
# which works with floats.
|
||||
groups[key] = float(groups[key][:-1].replace(",", "."))
|
||||
if as_timedelta_if_possible and groups["years"] == 0 and groups["months"] == 0:
|
||||
ret = timedelta(
|
||||
days=groups["days"],
|
||||
hours=groups["hours"],
|
||||
minutes=groups["minutes"],
|
||||
seconds=groups["seconds"],
|
||||
weeks=groups["weeks"],
|
||||
)
|
||||
if groups["sign"] == "-":
|
||||
ret = timedelta(0) - ret
|
||||
else:
|
||||
ret = Duration(
|
||||
years=groups["years"],
|
||||
months=groups["months"],
|
||||
days=groups["days"],
|
||||
hours=groups["hours"],
|
||||
minutes=groups["minutes"],
|
||||
seconds=groups["seconds"],
|
||||
weeks=groups["weeks"],
|
||||
)
|
||||
if groups["sign"] == "-":
|
||||
ret = Duration(0) - ret
|
||||
return ret
|
||||
|
||||
|
||||
def duration_isoformat(tduration, format=D_DEFAULT):
|
||||
"""
|
||||
Format duration strings.
|
||||
|
||||
This method is just a wrapper around isodate.isostrf.strftime and uses
|
||||
P%P (D_DEFAULT) as default format.
|
||||
"""
|
||||
# TODO: implement better decision for negative Durations.
|
||||
# should be done in Duration class in consistent way with timedelta.
|
||||
if (
|
||||
isinstance(tduration, Duration)
|
||||
and (
|
||||
tduration.years < 0
|
||||
or tduration.months < 0
|
||||
or tduration.tdelta < timedelta(0)
|
||||
)
|
||||
) or (isinstance(tduration, timedelta) and (tduration < timedelta(0))):
|
||||
ret = "-"
|
||||
else:
|
||||
ret = ""
|
||||
ret += strftime(tduration, format)
|
||||
return ret
|
7
env/lib/python3.11/site-packages/isodate/isoerror.py
vendored
Normal file
7
env/lib/python3.11/site-packages/isodate/isoerror.py
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
"""
|
||||
This module defines all exception classes in the whole package.
|
||||
"""
|
||||
|
||||
|
||||
class ISO8601Error(ValueError):
|
||||
"""Raised when the given ISO string can not be parsed."""
|
189
env/lib/python3.11/site-packages/isodate/isostrf.py
vendored
Normal file
189
env/lib/python3.11/site-packages/isodate/isostrf.py
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
"""
|
||||
This module provides an alternative strftime method.
|
||||
|
||||
The strftime method in this module allows only a subset of Python's strftime
|
||||
format codes, plus a few additional. It supports the full range of date values
|
||||
possible with standard Python date/time objects. Furthermore there are several
|
||||
pr-defined format strings in this module to make ease producing of ISO 8601
|
||||
conforming strings.
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import date, timedelta
|
||||
|
||||
from isodate.duration import Duration
|
||||
from isodate.isotzinfo import tz_isoformat
|
||||
|
||||
# Date specific format strings
|
||||
DATE_BAS_COMPLETE = "%Y%m%d"
|
||||
DATE_EXT_COMPLETE = "%Y-%m-%d"
|
||||
DATE_BAS_WEEK_COMPLETE = "%YW%W%w"
|
||||
DATE_EXT_WEEK_COMPLETE = "%Y-W%W-%w"
|
||||
DATE_BAS_ORD_COMPLETE = "%Y%j"
|
||||
DATE_EXT_ORD_COMPLETE = "%Y-%j"
|
||||
DATE_BAS_WEEK = "%YW%W"
|
||||
DATE_EXT_WEEK = "%Y-W%W"
|
||||
DATE_BAS_MONTH = "%Y%m"
|
||||
DATE_EXT_MONTH = "%Y-%m"
|
||||
DATE_YEAR = "%Y"
|
||||
DATE_CENTURY = "%C"
|
||||
|
||||
# Time specific format strings
|
||||
TIME_BAS_COMPLETE = "%H%M%S"
|
||||
TIME_EXT_COMPLETE = "%H:%M:%S"
|
||||
TIME_BAS_MINUTE = "%H%M"
|
||||
TIME_EXT_MINUTE = "%H:%M"
|
||||
TIME_HOUR = "%H"
|
||||
|
||||
# Time zone formats
|
||||
TZ_BAS = "%z"
|
||||
TZ_EXT = "%Z"
|
||||
TZ_HOUR = "%h"
|
||||
|
||||
# DateTime formats
|
||||
DT_EXT_COMPLETE = DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
|
||||
DT_BAS_COMPLETE = DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
|
||||
DT_EXT_ORD_COMPLETE = DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
|
||||
DT_BAS_ORD_COMPLETE = DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
|
||||
DT_EXT_WEEK_COMPLETE = DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
|
||||
DT_BAS_WEEK_COMPLETE = DATE_BAS_WEEK_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
|
||||
|
||||
# Duration formts
|
||||
D_DEFAULT = "P%P"
|
||||
D_WEEK = "P%p"
|
||||
D_ALT_EXT = "P" + DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE
|
||||
D_ALT_BAS = "P" + DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE
|
||||
D_ALT_EXT_ORD = "P" + DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE
|
||||
D_ALT_BAS_ORD = "P" + DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE
|
||||
|
||||
STRF_DT_MAP = {
|
||||
"%d": lambda tdt, yds: "%02d" % tdt.day,
|
||||
"%f": lambda tdt, yds: "%06d" % tdt.microsecond,
|
||||
"%H": lambda tdt, yds: "%02d" % tdt.hour,
|
||||
"%j": lambda tdt, yds: "%03d"
|
||||
% (tdt.toordinal() - date(tdt.year, 1, 1).toordinal() + 1),
|
||||
"%m": lambda tdt, yds: "%02d" % tdt.month,
|
||||
"%M": lambda tdt, yds: "%02d" % tdt.minute,
|
||||
"%S": lambda tdt, yds: "%02d" % tdt.second,
|
||||
"%w": lambda tdt, yds: "%1d" % tdt.isoweekday(),
|
||||
"%W": lambda tdt, yds: "%02d" % tdt.isocalendar()[1],
|
||||
"%Y": lambda tdt, yds: (((yds != 4) and "+") or "") + (("%%0%dd" % yds) % tdt.year),
|
||||
"%C": lambda tdt, yds: (((yds != 4) and "+") or "")
|
||||
+ (("%%0%dd" % (yds - 2)) % (tdt.year / 100)),
|
||||
"%h": lambda tdt, yds: tz_isoformat(tdt, "%h"),
|
||||
"%Z": lambda tdt, yds: tz_isoformat(tdt, "%Z"),
|
||||
"%z": lambda tdt, yds: tz_isoformat(tdt, "%z"),
|
||||
"%%": lambda tdt, yds: "%",
|
||||
}
|
||||
|
||||
STRF_D_MAP = {
|
||||
"%d": lambda tdt, yds: "%02d" % tdt.days,
|
||||
"%f": lambda tdt, yds: "%06d" % tdt.microseconds,
|
||||
"%H": lambda tdt, yds: "%02d" % (tdt.seconds / 60 / 60),
|
||||
"%m": lambda tdt, yds: "%02d" % tdt.months,
|
||||
"%M": lambda tdt, yds: "%02d" % ((tdt.seconds / 60) % 60),
|
||||
"%S": lambda tdt, yds: "%02d" % (tdt.seconds % 60),
|
||||
"%W": lambda tdt, yds: "%02d" % (abs(tdt.days / 7)),
|
||||
"%Y": lambda tdt, yds: (((yds != 4) and "+") or "")
|
||||
+ (("%%0%dd" % yds) % tdt.years),
|
||||
"%C": lambda tdt, yds: (((yds != 4) and "+") or "")
|
||||
+ (("%%0%dd" % (yds - 2)) % (tdt.years / 100)),
|
||||
"%%": lambda tdt, yds: "%",
|
||||
}
|
||||
|
||||
|
||||
def _strfduration(tdt, format, yeardigits=4):
|
||||
"""
|
||||
this is the work method for timedelta and Duration instances.
|
||||
|
||||
see strftime for more details.
|
||||
"""
|
||||
|
||||
def repl(match):
|
||||
"""
|
||||
lookup format command and return corresponding replacement.
|
||||
"""
|
||||
if match.group(0) in STRF_D_MAP:
|
||||
return STRF_D_MAP[match.group(0)](tdt, yeardigits)
|
||||
elif match.group(0) == "%P":
|
||||
ret = []
|
||||
if isinstance(tdt, Duration):
|
||||
if tdt.years:
|
||||
ret.append("%sY" % abs(tdt.years))
|
||||
if tdt.months:
|
||||
ret.append("%sM" % abs(tdt.months))
|
||||
usecs = abs(
|
||||
(tdt.days * 24 * 60 * 60 + tdt.seconds) * 1000000 + tdt.microseconds
|
||||
)
|
||||
seconds, usecs = divmod(usecs, 1000000)
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
days, hours = divmod(hours, 24)
|
||||
if days:
|
||||
ret.append("%sD" % days)
|
||||
if hours or minutes or seconds or usecs:
|
||||
ret.append("T")
|
||||
if hours:
|
||||
ret.append("%sH" % hours)
|
||||
if minutes:
|
||||
ret.append("%sM" % minutes)
|
||||
if seconds or usecs:
|
||||
if usecs:
|
||||
ret.append(("%d.%06d" % (seconds, usecs)).rstrip("0"))
|
||||
else:
|
||||
ret.append("%d" % seconds)
|
||||
ret.append("S")
|
||||
# at least one component has to be there.
|
||||
return ret and "".join(ret) or "0D"
|
||||
elif match.group(0) == "%p":
|
||||
return str(abs(tdt.days // 7)) + "W"
|
||||
return match.group(0)
|
||||
|
||||
return re.sub("%d|%f|%H|%m|%M|%S|%W|%Y|%C|%%|%P|%p", repl, format)
|
||||
|
||||
|
||||
def _strfdt(tdt, format, yeardigits=4):
|
||||
"""
|
||||
this is the work method for time and date instances.
|
||||
|
||||
see strftime for more details.
|
||||
"""
|
||||
|
||||
def repl(match):
|
||||
"""
|
||||
lookup format command and return corresponding replacement.
|
||||
"""
|
||||
if match.group(0) in STRF_DT_MAP:
|
||||
return STRF_DT_MAP[match.group(0)](tdt, yeardigits)
|
||||
return match.group(0)
|
||||
|
||||
return re.sub("%d|%f|%H|%j|%m|%M|%S|%w|%W|%Y|%C|%z|%Z|%h|%%", repl, format)
|
||||
|
||||
|
||||
def strftime(tdt, format, yeardigits=4):
|
||||
"""Directive Meaning Notes
|
||||
%d Day of the month as a decimal number [01,31].
|
||||
%f Microsecond as a decimal number [0,999999], zero-padded
|
||||
on the left (1)
|
||||
%H Hour (24-hour clock) as a decimal number [00,23].
|
||||
%j Day of the year as a decimal number [001,366].
|
||||
%m Month as a decimal number [01,12].
|
||||
%M Minute as a decimal number [00,59].
|
||||
%S Second as a decimal number [00,61]. (3)
|
||||
%w Weekday as a decimal number [0(Monday),6].
|
||||
%W Week number of the year (Monday as the first day of the week)
|
||||
as a decimal number [00,53]. All days in a new year preceding the
|
||||
first Monday are considered to be in week 0. (4)
|
||||
%Y Year with century as a decimal number. [0000,9999]
|
||||
%C Century as a decimal number. [00,99]
|
||||
%z UTC offset in the form +HHMM or -HHMM (empty string if the
|
||||
object is naive). (5)
|
||||
%Z Time zone name (empty string if the object is naive).
|
||||
%P ISO8601 duration format.
|
||||
%p ISO8601 duration format in weeks.
|
||||
%% A literal '%' character.
|
||||
|
||||
"""
|
||||
if isinstance(tdt, (timedelta, Duration)):
|
||||
return _strfduration(tdt, format, yeardigits)
|
||||
return _strfdt(tdt, format, yeardigits)
|
155
env/lib/python3.11/site-packages/isodate/isotime.py
vendored
Normal file
155
env/lib/python3.11/site-packages/isodate/isotime.py
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
"""
|
||||
This modules provides a method to parse an ISO 8601:2004 time string to a
|
||||
Python datetime.time instance.
|
||||
|
||||
It supports all basic and extended formats including time zone specifications
|
||||
as described in the ISO standard.
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import time
|
||||
from decimal import ROUND_FLOOR, Decimal
|
||||
|
||||
from isodate.isoerror import ISO8601Error
|
||||
from isodate.isostrf import TIME_EXT_COMPLETE, TZ_EXT, strftime
|
||||
from isodate.isotzinfo import TZ_REGEX, build_tzinfo
|
||||
|
||||
TIME_REGEX_CACHE = []
|
||||
# used to cache regular expressions to parse ISO time strings.
|
||||
|
||||
|
||||
def build_time_regexps():
|
||||
"""
|
||||
Build regular expressions to parse ISO time string.
|
||||
|
||||
The regular expressions are compiled and stored in TIME_REGEX_CACHE
|
||||
for later reuse.
|
||||
"""
|
||||
if not TIME_REGEX_CACHE:
|
||||
# ISO 8601 time representations allow decimal fractions on least
|
||||
# significant time component. Command and Full Stop are both valid
|
||||
# fraction separators.
|
||||
# The letter 'T' is allowed as time designator in front of a time
|
||||
# expression.
|
||||
# Immediately after a time expression, a time zone definition is
|
||||
# allowed.
|
||||
# a TZ may be missing (local time), be a 'Z' for UTC or a string of
|
||||
# +-hh:mm where the ':mm' part can be skipped.
|
||||
# TZ information patterns:
|
||||
# ''
|
||||
# Z
|
||||
# +-hh:mm
|
||||
# +-hhmm
|
||||
# +-hh =>
|
||||
# isotzinfo.TZ_REGEX
|
||||
def add_re(regex_text):
|
||||
TIME_REGEX_CACHE.append(re.compile(r"\A" + regex_text + TZ_REGEX + r"\Z"))
|
||||
|
||||
# 1. complete time:
|
||||
# hh:mm:ss.ss ... extended format
|
||||
add_re(
|
||||
r"T?(?P<hour>[0-9]{2}):"
|
||||
r"(?P<minute>[0-9]{2}):"
|
||||
r"(?P<second>[0-9]{2}"
|
||||
r"([,.][0-9]+)?)"
|
||||
)
|
||||
# hhmmss.ss ... basic format
|
||||
add_re(
|
||||
r"T?(?P<hour>[0-9]{2})"
|
||||
r"(?P<minute>[0-9]{2})"
|
||||
r"(?P<second>[0-9]{2}"
|
||||
r"([,.][0-9]+)?)"
|
||||
)
|
||||
# 2. reduced accuracy:
|
||||
# hh:mm.mm ... extended format
|
||||
add_re(r"T?(?P<hour>[0-9]{2}):" r"(?P<minute>[0-9]{2}" r"([,.][0-9]+)?)")
|
||||
# hhmm.mm ... basic format
|
||||
add_re(r"T?(?P<hour>[0-9]{2})" r"(?P<minute>[0-9]{2}" r"([,.][0-9]+)?)")
|
||||
# hh.hh ... basic format
|
||||
add_re(r"T?(?P<hour>[0-9]{2}" r"([,.][0-9]+)?)")
|
||||
return TIME_REGEX_CACHE
|
||||
|
||||
|
||||
def parse_time(timestring):
|
||||
"""
|
||||
Parses ISO 8601 times into datetime.time objects.
|
||||
|
||||
Following ISO 8601 formats are supported:
|
||||
(as decimal separator a ',' or a '.' is allowed)
|
||||
hhmmss.ssTZD basic complete time
|
||||
hh:mm:ss.ssTZD extended complete time
|
||||
hhmm.mmTZD basic reduced accuracy time
|
||||
hh:mm.mmTZD extended reduced accuracy time
|
||||
hh.hhTZD basic reduced accuracy time
|
||||
TZD is the time zone designator which can be in the following format:
|
||||
no designator indicates local time zone
|
||||
Z UTC
|
||||
+-hhmm basic hours and minutes
|
||||
+-hh:mm extended hours and minutes
|
||||
+-hh hours
|
||||
"""
|
||||
isotimes = build_time_regexps()
|
||||
for pattern in isotimes:
|
||||
match = pattern.match(timestring)
|
||||
if match:
|
||||
groups = match.groupdict()
|
||||
for key, value in groups.items():
|
||||
if value is not None:
|
||||
groups[key] = value.replace(",", ".")
|
||||
tzinfo = build_tzinfo(
|
||||
groups["tzname"],
|
||||
groups["tzsign"],
|
||||
int(groups["tzhour"] or 0),
|
||||
int(groups["tzmin"] or 0),
|
||||
)
|
||||
if "second" in groups:
|
||||
second = Decimal(groups["second"]).quantize(
|
||||
Decimal(".000001"), rounding=ROUND_FLOOR
|
||||
)
|
||||
microsecond = (second - int(second)) * int(1e6)
|
||||
# int(...) ... no rounding
|
||||
# to_integral() ... rounding
|
||||
return time(
|
||||
int(groups["hour"]),
|
||||
int(groups["minute"]),
|
||||
int(second),
|
||||
int(microsecond.to_integral()),
|
||||
tzinfo,
|
||||
)
|
||||
if "minute" in groups:
|
||||
minute = Decimal(groups["minute"])
|
||||
second = Decimal((minute - int(minute)) * 60).quantize(
|
||||
Decimal(".000001"), rounding=ROUND_FLOOR
|
||||
)
|
||||
microsecond = (second - int(second)) * int(1e6)
|
||||
return time(
|
||||
int(groups["hour"]),
|
||||
int(minute),
|
||||
int(second),
|
||||
int(microsecond.to_integral()),
|
||||
tzinfo,
|
||||
)
|
||||
else:
|
||||
microsecond, second, minute = 0, 0, 0
|
||||
hour = Decimal(groups["hour"])
|
||||
minute = (hour - int(hour)) * 60
|
||||
second = (minute - int(minute)) * 60
|
||||
microsecond = (second - int(second)) * int(1e6)
|
||||
return time(
|
||||
int(hour),
|
||||
int(minute),
|
||||
int(second),
|
||||
int(microsecond.to_integral()),
|
||||
tzinfo,
|
||||
)
|
||||
raise ISO8601Error("Unrecognised ISO 8601 time format: %r" % timestring)
|
||||
|
||||
|
||||
def time_isoformat(ttime, format=TIME_EXT_COMPLETE + TZ_EXT):
|
||||
"""
|
||||
Format time strings.
|
||||
|
||||
This method is just a wrapper around isodate.isostrf.strftime and uses
|
||||
Time-Extended-Complete with extended time zone as default format.
|
||||
"""
|
||||
return strftime(ttime, format)
|
91
env/lib/python3.11/site-packages/isodate/isotzinfo.py
vendored
Normal file
91
env/lib/python3.11/site-packages/isodate/isotzinfo.py
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
"""
|
||||
This module provides an ISO 8601:2004 time zone info parser.
|
||||
|
||||
It offers a function to parse the time zone offset as specified by ISO 8601.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from isodate.isoerror import ISO8601Error
|
||||
from isodate.tzinfo import UTC, ZERO, FixedOffset
|
||||
|
||||
TZ_REGEX = (
|
||||
r"(?P<tzname>(Z|(?P<tzsign>[+-])" r"(?P<tzhour>[0-9]{2})(:?(?P<tzmin>[0-9]{2}))?)?)"
|
||||
)
|
||||
|
||||
TZ_RE = re.compile(TZ_REGEX)
|
||||
|
||||
|
||||
def build_tzinfo(tzname, tzsign="+", tzhour=0, tzmin=0):
|
||||
"""
|
||||
create a tzinfo instance according to given parameters.
|
||||
|
||||
tzname:
|
||||
'Z' ... return UTC
|
||||
'' | None ... return None
|
||||
other ... return FixedOffset
|
||||
"""
|
||||
if tzname is None or tzname == "":
|
||||
return None
|
||||
if tzname == "Z":
|
||||
return UTC
|
||||
tzsign = ((tzsign == "-") and -1) or 1
|
||||
return FixedOffset(tzsign * tzhour, tzsign * tzmin, tzname)
|
||||
|
||||
|
||||
def parse_tzinfo(tzstring):
|
||||
"""
|
||||
Parses ISO 8601 time zone designators to tzinfo objects.
|
||||
|
||||
A time zone designator can be in the following format:
|
||||
no designator indicates local time zone
|
||||
Z UTC
|
||||
+-hhmm basic hours and minutes
|
||||
+-hh:mm extended hours and minutes
|
||||
+-hh hours
|
||||
"""
|
||||
match = TZ_RE.match(tzstring)
|
||||
if match:
|
||||
groups = match.groupdict()
|
||||
return build_tzinfo(
|
||||
groups["tzname"],
|
||||
groups["tzsign"],
|
||||
int(groups["tzhour"] or 0),
|
||||
int(groups["tzmin"] or 0),
|
||||
)
|
||||
raise ISO8601Error("%s not a valid time zone info" % tzstring)
|
||||
|
||||
|
||||
def tz_isoformat(dt, format="%Z"):
|
||||
"""
|
||||
return time zone offset ISO 8601 formatted.
|
||||
The various ISO formats can be chosen with the format parameter.
|
||||
|
||||
if tzinfo is None returns ''
|
||||
if tzinfo is UTC returns 'Z'
|
||||
else the offset is rendered to the given format.
|
||||
format:
|
||||
%h ... +-HH
|
||||
%z ... +-HHMM
|
||||
%Z ... +-HH:MM
|
||||
"""
|
||||
tzinfo = dt.tzinfo
|
||||
if (tzinfo is None) or (tzinfo.utcoffset(dt) is None):
|
||||
return ""
|
||||
if tzinfo.utcoffset(dt) == ZERO and tzinfo.dst(dt) == ZERO:
|
||||
return "Z"
|
||||
tdelta = tzinfo.utcoffset(dt)
|
||||
seconds = tdelta.days * 24 * 60 * 60 + tdelta.seconds
|
||||
sign = ((seconds < 0) and "-") or "+"
|
||||
seconds = abs(seconds)
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
if hours > 99:
|
||||
raise OverflowError("can not handle differences > 99 hours")
|
||||
if format == "%Z":
|
||||
return "%s%02d:%02d" % (sign, hours, minutes)
|
||||
elif format == "%z":
|
||||
return "%s%02d%02d" % (sign, hours, minutes)
|
||||
elif format == "%h":
|
||||
return "%s%02d" % (sign, hours)
|
||||
raise ValueError('unknown format string "%s"' % format)
|
166
env/lib/python3.11/site-packages/isodate/tzinfo.py
vendored
Normal file
166
env/lib/python3.11/site-packages/isodate/tzinfo.py
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
"""
|
||||
This module provides some datetime.tzinfo implementations.
|
||||
|
||||
All those classes are taken from the Python documentation.
|
||||
"""
|
||||
|
||||
import time
|
||||
from datetime import timedelta, tzinfo
|
||||
|
||||
ZERO = timedelta(0)
|
||||
# constant for zero time offset.
|
||||
|
||||
|
||||
class Utc(tzinfo):
|
||||
"""UTC
|
||||
|
||||
Universal time coordinated time zone.
|
||||
"""
|
||||
|
||||
def utcoffset(self, dt):
|
||||
"""
|
||||
Return offset from UTC in minutes east of UTC, which is ZERO for UTC.
|
||||
"""
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
"""
|
||||
Return the time zone name corresponding to the datetime object dt,
|
||||
as a string.
|
||||
"""
|
||||
return "UTC"
|
||||
|
||||
def dst(self, dt):
|
||||
"""
|
||||
Return the daylight saving time (DST) adjustment, in minutes east
|
||||
of UTC.
|
||||
"""
|
||||
return ZERO
|
||||
|
||||
def __reduce__(self):
|
||||
"""
|
||||
When unpickling a Utc object, return the default instance below, UTC.
|
||||
"""
|
||||
return _Utc, ()
|
||||
|
||||
|
||||
UTC = Utc()
|
||||
# the default instance for UTC.
|
||||
|
||||
|
||||
def _Utc():
|
||||
"""
|
||||
Helper function for unpickling a Utc object.
|
||||
"""
|
||||
return UTC
|
||||
|
||||
|
||||
class FixedOffset(tzinfo):
|
||||
"""
|
||||
A class building tzinfo objects for fixed-offset time zones.
|
||||
|
||||
Note that FixedOffset(0, 0, "UTC") or FixedOffset() is a different way to
|
||||
build a UTC tzinfo object.
|
||||
"""
|
||||
|
||||
def __init__(self, offset_hours=0, offset_minutes=0, name="UTC"):
|
||||
"""
|
||||
Initialise an instance with time offset and name.
|
||||
The time offset should be positive for time zones east of UTC
|
||||
and negate for time zones west of UTC.
|
||||
"""
|
||||
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
|
||||
self.__name = name
|
||||
|
||||
def utcoffset(self, dt):
|
||||
"""
|
||||
Return offset from UTC in minutes of UTC.
|
||||
"""
|
||||
return self.__offset
|
||||
|
||||
def tzname(self, dt):
|
||||
"""
|
||||
Return the time zone name corresponding to the datetime object dt, as a
|
||||
string.
|
||||
"""
|
||||
return self.__name
|
||||
|
||||
def dst(self, dt):
|
||||
"""
|
||||
Return the daylight saving time (DST) adjustment, in minutes east of
|
||||
UTC.
|
||||
"""
|
||||
return ZERO
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Return nicely formatted repr string.
|
||||
"""
|
||||
return "<FixedOffset %r>" % self.__name
|
||||
|
||||
|
||||
STDOFFSET = timedelta(seconds=-time.timezone)
|
||||
# locale time zone offset
|
||||
|
||||
# calculate local daylight saving offset if any.
|
||||
if time.daylight:
|
||||
DSTOFFSET = timedelta(seconds=-time.altzone)
|
||||
else:
|
||||
DSTOFFSET = STDOFFSET
|
||||
|
||||
DSTDIFF = DSTOFFSET - STDOFFSET
|
||||
# difference between local time zone and local DST time zone
|
||||
|
||||
|
||||
class LocalTimezone(tzinfo):
|
||||
"""
|
||||
A class capturing the platform's idea of local time.
|
||||
"""
|
||||
|
||||
def utcoffset(self, dt):
|
||||
"""
|
||||
Return offset from UTC in minutes of UTC.
|
||||
"""
|
||||
if self._isdst(dt):
|
||||
return DSTOFFSET
|
||||
else:
|
||||
return STDOFFSET
|
||||
|
||||
def dst(self, dt):
|
||||
"""
|
||||
Return daylight saving offset.
|
||||
"""
|
||||
if self._isdst(dt):
|
||||
return DSTDIFF
|
||||
else:
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
"""
|
||||
Return the time zone name corresponding to the datetime object dt, as a
|
||||
string.
|
||||
"""
|
||||
return time.tzname[self._isdst(dt)]
|
||||
|
||||
def _isdst(self, dt):
|
||||
"""
|
||||
Returns true if DST is active for given datetime object dt.
|
||||
"""
|
||||
tt = (
|
||||
dt.year,
|
||||
dt.month,
|
||||
dt.day,
|
||||
dt.hour,
|
||||
dt.minute,
|
||||
dt.second,
|
||||
dt.weekday(),
|
||||
0,
|
||||
-1,
|
||||
)
|
||||
stamp = time.mktime(tt)
|
||||
tt = time.localtime(stamp)
|
||||
return tt.tm_isdst > 0
|
||||
|
||||
|
||||
# the default instance for local time zone.
|
||||
LOCAL = LocalTimezone()
|
16
env/lib/python3.11/site-packages/isodate/version.py
vendored
Normal file
16
env/lib/python3.11/site-packages/isodate/version.py
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# file generated by setuptools_scm
|
||||
# don't change, don't track in version control
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from typing import Tuple, Union
|
||||
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
||||
else:
|
||||
VERSION_TUPLE = object
|
||||
|
||||
version: str
|
||||
__version__: str
|
||||
__version_tuple__: VERSION_TUPLE
|
||||
version_tuple: VERSION_TUPLE
|
||||
|
||||
__version__ = version = '0.7.2'
|
||||
__version_tuple__ = version_tuple = (0, 7, 2)
|
Reference in New Issue
Block a user