mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-02 17:33:37 +00:00
Update DateUtil 2.7.2 (ff03c0f) → 2.7.2 (49690ee)
This commit is contained in:
parent
74c524bc92
commit
86086b900f
10 changed files with 488 additions and 157 deletions
|
@ -364,13 +364,23 @@ class parserinfo(object):
|
||||||
return self.TZOFFSET.get(name)
|
return self.TZOFFSET.get(name)
|
||||||
|
|
||||||
def convertyear(self, year, century_specified=False):
|
def convertyear(self, year, century_specified=False):
|
||||||
|
"""
|
||||||
|
Converts two-digit years to year within [-50, 49]
|
||||||
|
range of self._year (current local time)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Function contract is that the year is always positive
|
||||||
|
assert year >= 0
|
||||||
|
|
||||||
if year < 100 and not century_specified:
|
if year < 100 and not century_specified:
|
||||||
|
# assume current century to start
|
||||||
year += self._century
|
year += self._century
|
||||||
if abs(year - self._year) >= 50:
|
|
||||||
if year < self._year:
|
if year >= self._year + 50: # if too far in future
|
||||||
year += 100
|
year -= 100
|
||||||
else:
|
elif year < self._year - 50: # if too far in past
|
||||||
year -= 100
|
year += 100
|
||||||
|
|
||||||
return year
|
return year
|
||||||
|
|
||||||
def validate(self, res):
|
def validate(self, res):
|
||||||
|
@ -448,10 +458,37 @@ class _ymd(list):
|
||||||
raise ValueError('Year is already set')
|
raise ValueError('Year is already set')
|
||||||
self.ystridx = len(self) - 1
|
self.ystridx = len(self) - 1
|
||||||
|
|
||||||
|
def _resolve_from_stridxs(self, strids):
|
||||||
|
"""
|
||||||
|
Try to resolve the identities of year/month/day elements using
|
||||||
|
ystridx, mstridx, and dstridx, if enough of these are specified.
|
||||||
|
"""
|
||||||
|
if len(self) == 3 and len(strids) == 2:
|
||||||
|
# we can back out the remaining stridx value
|
||||||
|
missing = [x for x in range(3) if x not in strids.values()]
|
||||||
|
key = [x for x in ['y', 'm', 'd'] if x not in strids]
|
||||||
|
assert len(missing) == len(key) == 1
|
||||||
|
key = key[0]
|
||||||
|
val = missing[0]
|
||||||
|
strids[key] = val
|
||||||
|
|
||||||
|
assert len(self) == len(strids) # otherwise this should not be called
|
||||||
|
out = {key: self[strids[key]] for key in strids}
|
||||||
|
return (out.get('y'), out.get('m'), out.get('d'))
|
||||||
|
|
||||||
def resolve_ymd(self, yearfirst, dayfirst):
|
def resolve_ymd(self, yearfirst, dayfirst):
|
||||||
len_ymd = len(self)
|
len_ymd = len(self)
|
||||||
year, month, day = (None, None, None)
|
year, month, day = (None, None, None)
|
||||||
|
|
||||||
|
strids = (('y', self.ystridx),
|
||||||
|
('m', self.mstridx),
|
||||||
|
('d', self.dstridx))
|
||||||
|
|
||||||
|
strids = {key: val for key, val in strids if val is not None}
|
||||||
|
if (len(self) == len(strids) > 0 or
|
||||||
|
(len(self) == 3 and len(strids) == 2)):
|
||||||
|
return self._resolve_from_stridxs(strids)
|
||||||
|
|
||||||
mstridx = self.mstridx
|
mstridx = self.mstridx
|
||||||
|
|
||||||
if len_ymd > 3:
|
if len_ymd > 3:
|
||||||
|
@ -460,13 +497,17 @@ class _ymd(list):
|
||||||
# One member, or two members with a month string
|
# One member, or two members with a month string
|
||||||
if mstridx is not None:
|
if mstridx is not None:
|
||||||
month = self[mstridx]
|
month = self[mstridx]
|
||||||
del self[mstridx]
|
# since mstridx is 0 or 1, self[mstridx-1] always
|
||||||
|
# looks up the other element
|
||||||
|
other = self[mstridx - 1]
|
||||||
|
else:
|
||||||
|
other = self[0]
|
||||||
|
|
||||||
if len_ymd > 1 or mstridx is None:
|
if len_ymd > 1 or mstridx is None:
|
||||||
if self[0] > 31:
|
if other > 31:
|
||||||
year = self[0]
|
year = other
|
||||||
else:
|
else:
|
||||||
day = self[0]
|
day = other
|
||||||
|
|
||||||
elif len_ymd == 2:
|
elif len_ymd == 2:
|
||||||
# Two members with numbers
|
# Two members with numbers
|
||||||
|
@ -1115,16 +1156,14 @@ class parser(object):
|
||||||
tzdata = tzinfos(tzname, tzoffset)
|
tzdata = tzinfos(tzname, tzoffset)
|
||||||
else:
|
else:
|
||||||
tzdata = tzinfos.get(tzname)
|
tzdata = tzinfos.get(tzname)
|
||||||
|
# handle case where tzinfo is paased an options that returns None
|
||||||
if isinstance(tzdata, datetime.tzinfo):
|
# eg tzinfos = {'BRST' : None}
|
||||||
|
if isinstance(tzdata, datetime.tzinfo) or tzdata is None:
|
||||||
tzinfo = tzdata
|
tzinfo = tzdata
|
||||||
elif isinstance(tzdata, text_type):
|
elif isinstance(tzdata, text_type):
|
||||||
tzinfo = tz.tzstr(tzdata)
|
tzinfo = tz.tzstr(tzdata)
|
||||||
elif isinstance(tzdata, integer_types):
|
elif isinstance(tzdata, integer_types):
|
||||||
tzinfo = tz.tzoffset(tzname, tzdata)
|
tzinfo = tz.tzoffset(tzname, tzdata)
|
||||||
else:
|
|
||||||
raise ValueError("Offset must be tzinfo subclass, "
|
|
||||||
"tz string, or int offset.")
|
|
||||||
return tzinfo
|
return tzinfo
|
||||||
|
|
||||||
def _build_tzaware(self, naive, res, tzinfos):
|
def _build_tzaware(self, naive, res, tzinfos):
|
||||||
|
@ -1160,7 +1199,7 @@ class parser(object):
|
||||||
warnings.warn("tzname {tzname} identified but not understood. "
|
warnings.warn("tzname {tzname} identified but not understood. "
|
||||||
"Pass `tzinfos` argument in order to correctly "
|
"Pass `tzinfos` argument in order to correctly "
|
||||||
"return a timezone-aware datetime. In a future "
|
"return a timezone-aware datetime. In a future "
|
||||||
"version, this raise an "
|
"version, this will raise an "
|
||||||
"exception.".format(tzname=res.tzname),
|
"exception.".format(tzname=res.tzname),
|
||||||
category=UnknownTimezoneWarning)
|
category=UnknownTimezoneWarning)
|
||||||
aware = naive
|
aware = naive
|
||||||
|
@ -1202,10 +1241,15 @@ class parser(object):
|
||||||
|
|
||||||
def _to_decimal(self, val):
|
def _to_decimal(self, val):
|
||||||
try:
|
try:
|
||||||
return Decimal(val)
|
decimal_value = Decimal(val)
|
||||||
|
# See GH 662, edge case, infinite value should not be converted via `_to_decimal`
|
||||||
|
if not decimal_value.is_finite():
|
||||||
|
raise ValueError("Converted decimal value is infinite or NaN")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = "Could not convert %s to decimal" % val
|
msg = "Could not convert %s to decimal" % val
|
||||||
six.raise_from(ValueError(msg), e)
|
six.raise_from(ValueError(msg), e)
|
||||||
|
else:
|
||||||
|
return decimal_value
|
||||||
|
|
||||||
|
|
||||||
DEFAULTPARSER = parser()
|
DEFAULTPARSER = parser()
|
||||||
|
|
|
@ -4,6 +4,8 @@ This module offers a parser for ISO-8601 strings
|
||||||
|
|
||||||
It is intended to support all valid date, time and datetime formats per the
|
It is intended to support all valid date, time and datetime formats per the
|
||||||
ISO-8601 specification.
|
ISO-8601 specification.
|
||||||
|
|
||||||
|
..versionadded:: 2.7.0
|
||||||
"""
|
"""
|
||||||
from datetime import datetime, timedelta, time, date
|
from datetime import datetime, timedelta, time, date
|
||||||
import calendar
|
import calendar
|
||||||
|
@ -86,10 +88,12 @@ class isoparser(object):
|
||||||
- ``hh``
|
- ``hh``
|
||||||
- ``hh:mm`` or ``hhmm``
|
- ``hh:mm`` or ``hhmm``
|
||||||
- ``hh:mm:ss`` or ``hhmmss``
|
- ``hh:mm:ss`` or ``hhmmss``
|
||||||
- ``hh:mm:ss.sss`` or ``hh:mm:ss.ssssss`` (3-6 sub-second digits)
|
- ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits)
|
||||||
|
|
||||||
Midnight is a special case for `hh`, as the standard supports both
|
Midnight is a special case for `hh`, as the standard supports both
|
||||||
00:00 and 24:00 as a representation.
|
00:00 and 24:00 as a representation. The decimal separator can be
|
||||||
|
either a dot or a comma.
|
||||||
|
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
|
|
||||||
|
@ -124,6 +128,8 @@ class isoparser(object):
|
||||||
currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not
|
currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not
|
||||||
guaranteed to continue failing in future versions if they encode
|
guaranteed to continue failing in future versions if they encode
|
||||||
a valid date.
|
a valid date.
|
||||||
|
|
||||||
|
.. versionadded:: 2.7.0
|
||||||
"""
|
"""
|
||||||
components, pos = self._parse_isodate(dt_str)
|
components, pos = self._parse_isodate(dt_str)
|
||||||
|
|
||||||
|
@ -133,6 +139,10 @@ class isoparser(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError('String contains unknown ISO components')
|
raise ValueError('String contains unknown ISO components')
|
||||||
|
|
||||||
|
if len(components) > 3 and components[3] == 24:
|
||||||
|
components[3] = 0
|
||||||
|
return datetime(*components) + timedelta(days=1)
|
||||||
|
|
||||||
return datetime(*components)
|
return datetime(*components)
|
||||||
|
|
||||||
@_takes_ascii
|
@_takes_ascii
|
||||||
|
@ -163,7 +173,10 @@ class isoparser(object):
|
||||||
:return:
|
:return:
|
||||||
Returns a :class:`datetime.time` object
|
Returns a :class:`datetime.time` object
|
||||||
"""
|
"""
|
||||||
return time(*self._parse_isotime(timestr))
|
components = self._parse_isotime(timestr)
|
||||||
|
if components[0] == 24:
|
||||||
|
components[0] = 0
|
||||||
|
return time(*components)
|
||||||
|
|
||||||
@_takes_ascii
|
@_takes_ascii
|
||||||
def parse_tzstr(self, tzstr, zero_as_utc=True):
|
def parse_tzstr(self, tzstr, zero_as_utc=True):
|
||||||
|
@ -186,10 +199,9 @@ class isoparser(object):
|
||||||
return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
|
return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
_MICROSECOND_END_REGEX = re.compile(b'[-+Z]+')
|
|
||||||
_DATE_SEP = b'-'
|
_DATE_SEP = b'-'
|
||||||
_TIME_SEP = b':'
|
_TIME_SEP = b':'
|
||||||
_MICRO_SEP = b'.'
|
_FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)')
|
||||||
|
|
||||||
def _parse_isodate(self, dt_str):
|
def _parse_isodate(self, dt_str):
|
||||||
try:
|
try:
|
||||||
|
@ -344,16 +356,14 @@ class isoparser(object):
|
||||||
pos += 1
|
pos += 1
|
||||||
|
|
||||||
if comp == 3:
|
if comp == 3:
|
||||||
# Microsecond
|
# Fraction of a second
|
||||||
if timestr[pos:pos + 1] != self._MICRO_SEP:
|
frac = self._FRACTION_REGEX.match(timestr[pos:])
|
||||||
|
if not frac:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos += 1
|
us_str = frac.group(1)[:6] # Truncate to microseconds
|
||||||
us_str = self._MICROSECOND_END_REGEX.split(timestr[pos:pos + 6],
|
|
||||||
1)[0]
|
|
||||||
|
|
||||||
components[comp] = int(us_str) * 10**(6 - len(us_str))
|
components[comp] = int(us_str) * 10**(6 - len(us_str))
|
||||||
pos += len(us_str)
|
pos += len(frac.group())
|
||||||
|
|
||||||
if pos < len_str:
|
if pos < len_str:
|
||||||
raise ValueError('Unused components in ISO string')
|
raise ValueError('Unused components in ISO string')
|
||||||
|
@ -362,7 +372,6 @@ class isoparser(object):
|
||||||
# Standard supports 00:00 and 24:00 as representations of midnight
|
# Standard supports 00:00 and 24:00 as representations of midnight
|
||||||
if any(component != 0 for component in components[1:4]):
|
if any(component != 0 for component in components[1:4]):
|
||||||
raise ValueError('Hour may only be 24 at 24:00:00.000')
|
raise ValueError('Hour may only be 24 at 24:00:00.000')
|
||||||
components[0] = 0
|
|
||||||
|
|
||||||
return components
|
return components
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,12 @@ __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
||||||
|
|
||||||
class relativedelta(object):
|
class relativedelta(object):
|
||||||
"""
|
"""
|
||||||
The relativedelta type is based on the specification of the excellent
|
The relativedelta type is designed to be applied to an existing datetime and
|
||||||
work done by M.-A. Lemburg in his
|
can replace specific components of that datetime, or represents an interval
|
||||||
|
of time.
|
||||||
|
|
||||||
|
It is based on the specification of the excellent work done by M.-A. Lemburg
|
||||||
|
in his
|
||||||
`mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.
|
`mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.
|
||||||
However, notice that this type does *NOT* implement the same algorithm as
|
However, notice that this type does *NOT* implement the same algorithm as
|
||||||
his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.
|
his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.
|
||||||
|
@ -44,12 +48,16 @@ class relativedelta(object):
|
||||||
the corresponding aritmetic operation on the original datetime value
|
the corresponding aritmetic operation on the original datetime value
|
||||||
with the information in the relativedelta.
|
with the information in the relativedelta.
|
||||||
|
|
||||||
weekday:
|
weekday:
|
||||||
One of the weekday instances (MO, TU, etc). These instances may
|
One of the weekday instances (MO, TU, etc) available in the
|
||||||
receive a parameter N, specifying the Nth weekday, which could
|
relativedelta module. These instances may receive a parameter N,
|
||||||
be positive or negative (like MO(+1) or MO(-2). Not specifying
|
specifying the Nth weekday, which could be positive or negative
|
||||||
it is the same as specifying +1. You can also use an integer,
|
(like MO(+1) or MO(-2)). Not specifying it is the same as specifying
|
||||||
where 0=MO.
|
+1. You can also use an integer, where 0=MO. This argument is always
|
||||||
|
relative e.g. if the calculated date is already Monday, using MO(1)
|
||||||
|
or MO(-1) won't change the day. To effectively make it absolute, use
|
||||||
|
it in combination with the day argument (e.g. day=1, MO(1) for first
|
||||||
|
Monday of the month).
|
||||||
|
|
||||||
leapdays:
|
leapdays:
|
||||||
Will add given days to the date found, if year is a leap
|
Will add given days to the date found, if year is a leap
|
||||||
|
@ -59,33 +67,39 @@ class relativedelta(object):
|
||||||
Set the yearday or the non-leap year day (jump leap days).
|
Set the yearday or the non-leap year day (jump leap days).
|
||||||
These are converted to day/month/leapdays information.
|
These are converted to day/month/leapdays information.
|
||||||
|
|
||||||
Here is the behavior of operations with relativedelta:
|
There are relative and absolute forms of the keyword
|
||||||
|
arguments. The plural is relative, and the singular is
|
||||||
|
absolute. For each argument in the order below, the absolute form
|
||||||
|
is applied first (by setting each attribute to that value) and
|
||||||
|
then the relative form (by adding the value to the attribute).
|
||||||
|
|
||||||
1. Calculate the absolute year, using the 'year' argument, or the
|
The order of attributes considered when this relativedelta is
|
||||||
original datetime year, if the argument is not present.
|
added to a datetime is:
|
||||||
|
|
||||||
2. Add the relative 'years' argument to the absolute year.
|
1. Year
|
||||||
|
2. Month
|
||||||
|
3. Day
|
||||||
|
4. Hours
|
||||||
|
5. Minutes
|
||||||
|
6. Seconds
|
||||||
|
7. Microseconds
|
||||||
|
|
||||||
3. Do steps 1 and 2 for month/months.
|
Finally, weekday is applied, using the rule described above.
|
||||||
|
|
||||||
4. Calculate the absolute day, using the 'day' argument, or the
|
For example
|
||||||
original datetime day, if the argument is not present. Then,
|
|
||||||
subtract from the day until it fits in the year and month
|
|
||||||
found after their operations.
|
|
||||||
|
|
||||||
5. Add the relative 'days' argument to the absolute day. Notice
|
>>> from datetime import datetime
|
||||||
that the 'weeks' argument is multiplied by 7 and added to
|
>>> from dateutil.relativedelta import relativedelta, MO
|
||||||
'days'.
|
>>> dt = datetime(2018, 4, 9, 13, 37, 0)
|
||||||
|
>>> delta = relativedelta(hours=25, day=1, weekday=MO(1))
|
||||||
|
>>> dt + delta
|
||||||
|
datetime.datetime(2018, 4, 2, 14, 37)
|
||||||
|
|
||||||
6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds,
|
First, the day is set to 1 (the first of the month), then 25 hours
|
||||||
microsecond/microseconds.
|
are added, to get to the 2nd day and 14th hour, finally the
|
||||||
|
weekday is applied, but since the 2nd is already a Monday there is
|
||||||
|
no effect.
|
||||||
|
|
||||||
7. If the 'weekday' argument is present, calculate the weekday,
|
|
||||||
with the given (wday, nth) tuple. wday is the index of the
|
|
||||||
weekday (0-6, 0=Mon), and nth is the number of weeks to add
|
|
||||||
forward or backward, depending on its signal. Notice that if
|
|
||||||
the calculated date is already Monday, for example, using
|
|
||||||
(0, 1) or (0, -1) won't change the day.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, dt1=None, dt2=None,
|
def __init__(self, dt1=None, dt2=None,
|
||||||
|
@ -271,7 +285,7 @@ class relativedelta(object):
|
||||||
values for the relative attributes.
|
values for the relative attributes.
|
||||||
|
|
||||||
>>> relativedelta(days=1.5, hours=2).normalized()
|
>>> relativedelta(days=1.5, hours=2).normalized()
|
||||||
relativedelta(days=1, hours=14)
|
relativedelta(days=+1, hours=+14)
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
Returns a :class:`dateutil.relativedelta.relativedelta` object.
|
Returns a :class:`dateutil.relativedelta.relativedelta` object.
|
||||||
|
|
|
@ -337,10 +337,6 @@ class rrule(rrulebase):
|
||||||
|
|
||||||
Additionally, it supports the following keyword arguments:
|
Additionally, it supports the following keyword arguments:
|
||||||
|
|
||||||
:param cache:
|
|
||||||
If given, it must be a boolean value specifying to enable or disable
|
|
||||||
caching of results. If you will use the same rrule instance multiple
|
|
||||||
times, enabling caching will improve the performance considerably.
|
|
||||||
:param dtstart:
|
:param dtstart:
|
||||||
The recurrence start. Besides being the base for the recurrence,
|
The recurrence start. Besides being the base for the recurrence,
|
||||||
missing parameters in the final recurrence instances will also be
|
missing parameters in the final recurrence instances will also be
|
||||||
|
@ -357,20 +353,26 @@ class rrule(rrulebase):
|
||||||
from calendar.firstweekday(), and may be modified by
|
from calendar.firstweekday(), and may be modified by
|
||||||
calendar.setfirstweekday().
|
calendar.setfirstweekday().
|
||||||
:param count:
|
:param count:
|
||||||
How many occurrences will be generated.
|
If given, this determines how many occurrences will be generated.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
As of version 2.5.0, the use of the ``until`` keyword together
|
As of version 2.5.0, the use of the keyword ``until`` in conjunction
|
||||||
with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10.
|
with ``count`` is deprecated, to make sure ``dateutil`` is fully
|
||||||
|
compliant with `RFC-5545 Sec. 3.3.10 <https://tools.ietf.org/
|
||||||
|
html/rfc5545#section-3.3.10>`_. Therefore, ``until`` and ``count``
|
||||||
|
**must not** occur in the same call to ``rrule``.
|
||||||
:param until:
|
:param until:
|
||||||
If given, this must be a datetime instance, that will specify the
|
If given, this must be a datetime instance specifying the upper-bound
|
||||||
limit of the recurrence. The last recurrence in the rule is the greatest
|
limit of the recurrence. The last recurrence in the rule is the greatest
|
||||||
datetime that is less than or equal to the value specified in the
|
datetime that is less than or equal to the value specified in the
|
||||||
``until`` parameter.
|
``until`` parameter.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
As of version 2.5.0, the use of the ``until`` keyword together
|
As of version 2.5.0, the use of the keyword ``until`` in conjunction
|
||||||
with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10.
|
with ``count`` is deprecated, to make sure ``dateutil`` is fully
|
||||||
|
compliant with `RFC-5545 Sec. 3.3.10 <https://tools.ietf.org/
|
||||||
|
html/rfc5545#section-3.3.10>`_. Therefore, ``until`` and ``count``
|
||||||
|
**must not** occur in the same call to ``rrule``.
|
||||||
:param bysetpos:
|
:param bysetpos:
|
||||||
If given, it must be either an integer, or a sequence of integers,
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
positive or negative. Each given integer will specify an occurrence
|
positive or negative. Each given integer will specify an occurrence
|
||||||
|
@ -387,6 +389,11 @@ class rrule(rrulebase):
|
||||||
:param byyearday:
|
:param byyearday:
|
||||||
If given, it must be either an integer, or a sequence of integers,
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
meaning the year days to apply the recurrence to.
|
meaning the year days to apply the recurrence to.
|
||||||
|
:param byeaster:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
positive or negative. Each integer will define an offset from the
|
||||||
|
Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
|
||||||
|
Sunday itself. This is an extension to the RFC specification.
|
||||||
:param byweekno:
|
:param byweekno:
|
||||||
If given, it must be either an integer, or a sequence of integers,
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
meaning the week numbers to apply the recurrence to. Week numbers
|
meaning the week numbers to apply the recurrence to. Week numbers
|
||||||
|
@ -412,11 +419,10 @@ class rrule(rrulebase):
|
||||||
:param bysecond:
|
:param bysecond:
|
||||||
If given, it must be either an integer, or a sequence of integers,
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
meaning the seconds to apply the recurrence to.
|
meaning the seconds to apply the recurrence to.
|
||||||
:param byeaster:
|
:param cache:
|
||||||
If given, it must be either an integer, or a sequence of integers,
|
If given, it must be a boolean value specifying to enable or disable
|
||||||
positive or negative. Each integer will define an offset from the
|
caching of results. If you will use the same rrule instance multiple
|
||||||
Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
|
times, enabling caching will improve the performance considerably.
|
||||||
Sunday itself. This is an extension to the RFC specification.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, freq, dtstart=None,
|
def __init__(self, freq, dtstart=None,
|
||||||
interval=1, wkst=None, count=None, until=None, bysetpos=None,
|
interval=1, wkst=None, count=None, until=None, bysetpos=None,
|
||||||
|
@ -427,7 +433,10 @@ class rrule(rrulebase):
|
||||||
super(rrule, self).__init__(cache)
|
super(rrule, self).__init__(cache)
|
||||||
global easter
|
global easter
|
||||||
if not dtstart:
|
if not dtstart:
|
||||||
dtstart = datetime.datetime.now().replace(microsecond=0)
|
if until and until.tzinfo:
|
||||||
|
dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0)
|
||||||
|
else:
|
||||||
|
dtstart = datetime.datetime.now().replace(microsecond=0)
|
||||||
elif not isinstance(dtstart, datetime.datetime):
|
elif not isinstance(dtstart, datetime.datetime):
|
||||||
dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
|
dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
|
||||||
else:
|
else:
|
||||||
|
@ -1404,6 +1413,49 @@ class rruleset(rrulebase):
|
||||||
|
|
||||||
|
|
||||||
class _rrulestr(object):
|
class _rrulestr(object):
|
||||||
|
""" Parses a string representation of a recurrence rule or set of
|
||||||
|
recurrence rules.
|
||||||
|
|
||||||
|
:param s:
|
||||||
|
Required, a string defining one or more recurrence rules.
|
||||||
|
|
||||||
|
:param dtstart:
|
||||||
|
If given, used as the default recurrence start if not specified in the
|
||||||
|
rule string.
|
||||||
|
|
||||||
|
:param cache:
|
||||||
|
If set ``True`` caching of results will be enabled, improving
|
||||||
|
performance of multiple queries considerably.
|
||||||
|
|
||||||
|
:param unfold:
|
||||||
|
If set ``True`` indicates that a rule string is split over more
|
||||||
|
than one line and should be joined before processing.
|
||||||
|
|
||||||
|
:param forceset:
|
||||||
|
If set ``True`` forces a :class:`dateutil.rrule.rruleset` to
|
||||||
|
be returned.
|
||||||
|
|
||||||
|
:param compatible:
|
||||||
|
If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``.
|
||||||
|
|
||||||
|
:param ignoretz:
|
||||||
|
If set ``True``, time zones in parsed strings are ignored and a naive
|
||||||
|
:class:`datetime.datetime` object is returned.
|
||||||
|
|
||||||
|
:param tzids:
|
||||||
|
If given, a callable or mapping used to retrieve a
|
||||||
|
:class:`datetime.tzinfo` from a string representation.
|
||||||
|
Defaults to :func:`dateutil.tz.gettz`.
|
||||||
|
|
||||||
|
:param tzinfos:
|
||||||
|
Additional time zone names / aliases which may be present in a string
|
||||||
|
representation. See :func:`dateutil.parser.parse` for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`dateutil.rrule.rruleset` or
|
||||||
|
:class:`dateutil.rrule.rrule`
|
||||||
|
"""
|
||||||
|
|
||||||
_freq_map = {"YEARLY": YEARLY,
|
_freq_map = {"YEARLY": YEARLY,
|
||||||
"MONTHLY": MONTHLY,
|
"MONTHLY": MONTHLY,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
from .tz import *
|
from .tz import *
|
||||||
|
from .tz import __doc__
|
||||||
|
|
||||||
#: Convenience constant providing a :class:`tzutc()` instance
|
#: Convenience constant providing a :class:`tzutc()` instance
|
||||||
#:
|
#:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from six import PY3
|
from six import PY2
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
@ -16,14 +16,18 @@ def tzname_in_python2(namefunc):
|
||||||
tzname() API changed in Python 3. It used to return bytes, but was changed
|
tzname() API changed in Python 3. It used to return bytes, but was changed
|
||||||
to unicode strings
|
to unicode strings
|
||||||
"""
|
"""
|
||||||
def adjust_encoding(*args, **kwargs):
|
if PY2:
|
||||||
name = namefunc(*args, **kwargs)
|
@wraps(namefunc)
|
||||||
if name is not None and not PY3:
|
def adjust_encoding(*args, **kwargs):
|
||||||
name = name.encode()
|
name = namefunc(*args, **kwargs)
|
||||||
|
if name is not None:
|
||||||
|
name = name.encode()
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
return adjust_encoding
|
return adjust_encoding
|
||||||
|
else:
|
||||||
|
return namefunc
|
||||||
|
|
||||||
|
|
||||||
# The following is adapted from Alexander Belopolsky's tz library
|
# The following is adapted from Alexander Belopolsky's tz library
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import weakref
|
||||||
|
|
||||||
class _TzSingleton(type):
|
class _TzSingleton(type):
|
||||||
def __init__(cls, *args, **kwargs):
|
def __init__(cls, *args, **kwargs):
|
||||||
|
@ -19,7 +19,7 @@ class _TzFactory(type):
|
||||||
|
|
||||||
class _TzOffsetFactory(_TzFactory):
|
class _TzOffsetFactory(_TzFactory):
|
||||||
def __init__(cls, *args, **kwargs):
|
def __init__(cls, *args, **kwargs):
|
||||||
cls.__instances = {}
|
cls.__instances = weakref.WeakValueDictionary()
|
||||||
|
|
||||||
def __call__(cls, name, offset):
|
def __call__(cls, name, offset):
|
||||||
if isinstance(offset, timedelta):
|
if isinstance(offset, timedelta):
|
||||||
|
@ -36,7 +36,7 @@ class _TzOffsetFactory(_TzFactory):
|
||||||
|
|
||||||
class _TzStrFactory(_TzFactory):
|
class _TzStrFactory(_TzFactory):
|
||||||
def __init__(cls, *args, **kwargs):
|
def __init__(cls, *args, **kwargs):
|
||||||
cls.__instances = {}
|
cls.__instances = weakref.WeakValueDictionary()
|
||||||
|
|
||||||
def __call__(cls, s, posix_offset=False):
|
def __call__(cls, s, posix_offset=False):
|
||||||
key = (s, posix_offset)
|
key = (s, posix_offset)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
This module offers timezone implementations subclassing the abstract
|
This module offers timezone implementations subclassing the abstract
|
||||||
:py:`datetime.tzinfo` type. There are classes to handle tzfile format files
|
:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format
|
||||||
(usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, etc), TZ
|
files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`,
|
||||||
environment string (in all known formats), given ranges (with help from
|
etc), TZ environment string (in all known formats), given ranges (with help
|
||||||
relative deltas), local machine timezone, fixed offset timezone, and UTC
|
from relative deltas), local machine timezone, fixed offset timezone, and UTC
|
||||||
timezone.
|
timezone.
|
||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -13,6 +13,7 @@ import time
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import bisect
|
import bisect
|
||||||
|
import weakref
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
@ -28,6 +29,9 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
tzwin = tzwinlocal = None
|
tzwin = tzwinlocal = None
|
||||||
|
|
||||||
|
# For warning about rounding tzinfo
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
ZERO = datetime.timedelta(0)
|
ZERO = datetime.timedelta(0)
|
||||||
EPOCH = datetime.datetime.utcfromtimestamp(0)
|
EPOCH = datetime.datetime.utcfromtimestamp(0)
|
||||||
EPOCHORDINAL = EPOCH.toordinal()
|
EPOCHORDINAL = EPOCH.toordinal()
|
||||||
|
@ -137,7 +141,8 @@ class tzoffset(datetime.tzinfo):
|
||||||
offset = offset.total_seconds()
|
offset = offset.total_seconds()
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
pass
|
pass
|
||||||
self._offset = datetime.timedelta(seconds=offset)
|
|
||||||
|
self._offset = datetime.timedelta(seconds=_get_supported_offset(offset))
|
||||||
|
|
||||||
def utcoffset(self, dt):
|
def utcoffset(self, dt):
|
||||||
return self._offset
|
return self._offset
|
||||||
|
@ -387,10 +392,60 @@ class tzfile(_tzinfo):
|
||||||
``fileobj``'s ``name`` attribute or to ``repr(fileobj)``.
|
``fileobj``'s ``name`` attribute or to ``repr(fileobj)``.
|
||||||
|
|
||||||
See `Sources for Time Zone and Daylight Saving Time Data
|
See `Sources for Time Zone and Daylight Saving Time Data
|
||||||
<https://data.iana.org/time-zones/tz-link.html>`_ for more information. Time
|
<https://data.iana.org/time-zones/tz-link.html>`_ for more information.
|
||||||
zone files can be compiled from the `IANA Time Zone database files
|
Time zone files can be compiled from the `IANA Time Zone database files
|
||||||
<https://www.iana.org/time-zones>`_ with the `zic time zone compiler
|
<https://www.iana.org/time-zones>`_ with the `zic time zone compiler
|
||||||
<https://www.freebsd.org/cgi/man.cgi?query=zic&sektion=8>`_
|
<https://www.freebsd.org/cgi/man.cgi?query=zic&sektion=8>`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Only construct a ``tzfile`` directly if you have a specific timezone
|
||||||
|
file on disk that you want to read into a Python ``tzinfo`` object.
|
||||||
|
If you want to get a ``tzfile`` representing a specific IANA zone,
|
||||||
|
(e.g. ``'America/New_York'``), you should call
|
||||||
|
:func:`dateutil.tz.gettz` with the zone identifier.
|
||||||
|
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
|
Using the US Eastern time zone as an example, we can see that a ``tzfile``
|
||||||
|
provides time zone information for the standard Daylight Saving offsets:
|
||||||
|
|
||||||
|
.. testsetup:: tzfile
|
||||||
|
|
||||||
|
from dateutil.tz import gettz
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
.. doctest:: tzfile
|
||||||
|
|
||||||
|
>>> NYC = gettz('America/New_York')
|
||||||
|
>>> NYC
|
||||||
|
tzfile('/usr/share/zoneinfo/America/New_York')
|
||||||
|
|
||||||
|
>>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST
|
||||||
|
2016-01-03 00:00:00-05:00
|
||||||
|
|
||||||
|
>>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT
|
||||||
|
2016-07-07 00:00:00-04:00
|
||||||
|
|
||||||
|
|
||||||
|
The ``tzfile`` structure contains a fully history of the time zone,
|
||||||
|
so historical dates will also have the right offsets. For example, before
|
||||||
|
the adoption of the UTC standards, New York used local solar mean time:
|
||||||
|
|
||||||
|
.. doctest:: tzfile
|
||||||
|
|
||||||
|
>>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT
|
||||||
|
1901-04-12 00:00:00-04:56
|
||||||
|
|
||||||
|
And during World War II, New York was on "Eastern War Time", which was a
|
||||||
|
state of permanent daylight saving time:
|
||||||
|
|
||||||
|
.. doctest:: tzfile
|
||||||
|
|
||||||
|
>>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT
|
||||||
|
1944-02-07 00:00:00-04:00
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, fileobj, filename=None):
|
def __init__(self, fileobj, filename=None):
|
||||||
|
@ -410,7 +465,7 @@ class tzfile(_tzinfo):
|
||||||
|
|
||||||
if fileobj is not None:
|
if fileobj is not None:
|
||||||
if not file_opened_here:
|
if not file_opened_here:
|
||||||
fileobj = _ContextWrapper(fileobj)
|
fileobj = _nullcontext(fileobj)
|
||||||
|
|
||||||
with fileobj as file_stream:
|
with fileobj as file_stream:
|
||||||
tzobj = self._read_tzfile(file_stream)
|
tzobj = self._read_tzfile(file_stream)
|
||||||
|
@ -487,7 +542,7 @@ class tzfile(_tzinfo):
|
||||||
|
|
||||||
if timecnt:
|
if timecnt:
|
||||||
out.trans_idx = struct.unpack(">%dB" % timecnt,
|
out.trans_idx = struct.unpack(">%dB" % timecnt,
|
||||||
fileobj.read(timecnt))
|
fileobj.read(timecnt))
|
||||||
else:
|
else:
|
||||||
out.trans_idx = []
|
out.trans_idx = []
|
||||||
|
|
||||||
|
@ -550,10 +605,7 @@ class tzfile(_tzinfo):
|
||||||
out.ttinfo_list = []
|
out.ttinfo_list = []
|
||||||
for i in range(typecnt):
|
for i in range(typecnt):
|
||||||
gmtoff, isdst, abbrind = ttinfo[i]
|
gmtoff, isdst, abbrind = ttinfo[i]
|
||||||
# Round to full-minutes if that's not the case. Python's
|
gmtoff = _get_supported_offset(gmtoff)
|
||||||
# datetime doesn't accept sub-minute timezones. Check
|
|
||||||
# http://python.org/sf/1447945 for some information.
|
|
||||||
gmtoff = 60 * ((gmtoff + 30) // 60)
|
|
||||||
tti = _ttinfo()
|
tti = _ttinfo()
|
||||||
tti.offset = gmtoff
|
tti.offset = gmtoff
|
||||||
tti.dstoffset = datetime.timedelta(0)
|
tti.dstoffset = datetime.timedelta(0)
|
||||||
|
@ -605,37 +657,44 @@ class tzfile(_tzinfo):
|
||||||
# isgmt are off, so it should be in wall time. OTOH, it's
|
# isgmt are off, so it should be in wall time. OTOH, it's
|
||||||
# always in gmt time. Let me know if you have comments
|
# always in gmt time. Let me know if you have comments
|
||||||
# about this.
|
# about this.
|
||||||
laststdoffset = None
|
lastdst = None
|
||||||
|
lastoffset = None
|
||||||
|
lastdstoffset = None
|
||||||
|
lastbaseoffset = None
|
||||||
out.trans_list = []
|
out.trans_list = []
|
||||||
|
|
||||||
for i, tti in enumerate(out.trans_idx):
|
for i, tti in enumerate(out.trans_idx):
|
||||||
if not tti.isdst:
|
offset = tti.offset
|
||||||
offset = tti.offset
|
dstoffset = 0
|
||||||
laststdoffset = offset
|
|
||||||
else:
|
|
||||||
if laststdoffset is not None:
|
|
||||||
# Store the DST offset as well and update it in the list
|
|
||||||
tti.dstoffset = tti.offset - laststdoffset
|
|
||||||
out.trans_idx[i] = tti
|
|
||||||
|
|
||||||
offset = laststdoffset or 0
|
if lastdst is not None:
|
||||||
|
if tti.isdst:
|
||||||
|
if not lastdst:
|
||||||
|
dstoffset = offset - lastoffset
|
||||||
|
|
||||||
out.trans_list.append(out.trans_list_utc[i] + offset)
|
if not dstoffset and lastdstoffset:
|
||||||
|
dstoffset = lastdstoffset
|
||||||
|
|
||||||
# In case we missed any DST offsets on the way in for some reason, make
|
tti.dstoffset = datetime.timedelta(seconds=dstoffset)
|
||||||
# a second pass over the list, looking for the /next/ DST offset.
|
lastdstoffset = dstoffset
|
||||||
laststdoffset = None
|
|
||||||
for i in reversed(range(len(out.trans_idx))):
|
|
||||||
tti = out.trans_idx[i]
|
|
||||||
if tti.isdst:
|
|
||||||
if not (tti.dstoffset or laststdoffset is None):
|
|
||||||
tti.dstoffset = tti.offset - laststdoffset
|
|
||||||
else:
|
|
||||||
laststdoffset = tti.offset
|
|
||||||
|
|
||||||
if not isinstance(tti.dstoffset, datetime.timedelta):
|
# If a time zone changes its base offset during a DST transition,
|
||||||
tti.dstoffset = datetime.timedelta(seconds=tti.dstoffset)
|
# then you need to adjust by the previous base offset to get the
|
||||||
|
# transition time in local time. Otherwise you use the current
|
||||||
|
# base offset. Ideally, I would have some mathematical proof of
|
||||||
|
# why this is true, but I haven't really thought about it enough.
|
||||||
|
baseoffset = offset - dstoffset
|
||||||
|
adjustment = baseoffset
|
||||||
|
if (lastbaseoffset is not None and baseoffset != lastbaseoffset
|
||||||
|
and tti.isdst != lastdst):
|
||||||
|
# The base DST has changed
|
||||||
|
adjustment = lastbaseoffset
|
||||||
|
|
||||||
out.trans_idx[i] = tti
|
lastdst = tti.isdst
|
||||||
|
lastoffset = offset
|
||||||
|
lastbaseoffset = baseoffset
|
||||||
|
|
||||||
|
out.trans_list.append(out.trans_list_utc[i] + adjustment)
|
||||||
|
|
||||||
out.trans_idx = tuple(out.trans_idx)
|
out.trans_idx = tuple(out.trans_idx)
|
||||||
out.trans_list = tuple(out.trans_list)
|
out.trans_list = tuple(out.trans_list)
|
||||||
|
@ -840,8 +899,9 @@ class tzrange(tzrangebase):
|
||||||
|
|
||||||
:param start:
|
:param start:
|
||||||
A :class:`relativedelta.relativedelta` object or equivalent specifying
|
A :class:`relativedelta.relativedelta` object or equivalent specifying
|
||||||
the time and time of year that daylight savings time starts. To specify,
|
the time and time of year that daylight savings time starts. To
|
||||||
for example, that DST starts at 2AM on the 2nd Sunday in March, pass:
|
specify, for example, that DST starts at 2AM on the 2nd Sunday in
|
||||||
|
March, pass:
|
||||||
|
|
||||||
``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))``
|
``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))``
|
||||||
|
|
||||||
|
@ -849,12 +909,12 @@ class tzrange(tzrangebase):
|
||||||
value is 2 AM on the first Sunday in April.
|
value is 2 AM on the first Sunday in April.
|
||||||
|
|
||||||
:param end:
|
:param end:
|
||||||
A :class:`relativedelta.relativedelta` object or equivalent representing
|
A :class:`relativedelta.relativedelta` object or equivalent
|
||||||
the time and time of year that daylight savings time ends, with the
|
representing the time and time of year that daylight savings time
|
||||||
same specification method as in ``start``. One note is that this should
|
ends, with the same specification method as in ``start``. One note is
|
||||||
point to the first time in the *standard* zone, so if a transition
|
that this should point to the first time in the *standard* zone, so if
|
||||||
occurs at 2AM in the DST zone and the clocks are set back 1 hour to 1AM,
|
a transition occurs at 2AM in the DST zone and the clocks are set back
|
||||||
set the `hours` parameter to +1.
|
1 hour to 1AM, set the ``hours`` parameter to +1.
|
||||||
|
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
@ -985,8 +1045,9 @@ class tzstr(tzrange):
|
||||||
|
|
||||||
:param s:
|
:param s:
|
||||||
A time zone string in ``TZ`` variable format. This can be a
|
A time zone string in ``TZ`` variable format. This can be a
|
||||||
:class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: :class:`unicode`)
|
:class:`bytes` (2.x: :class:`str`), :class:`str` (2.x:
|
||||||
or a stream emitting unicode characters (e.g. :class:`StringIO`).
|
:class:`unicode`) or a stream emitting unicode characters
|
||||||
|
(e.g. :class:`StringIO`).
|
||||||
|
|
||||||
:param posix_offset:
|
:param posix_offset:
|
||||||
Optional. If set to ``True``, interpret strings such as ``GMT+3`` or
|
Optional. If set to ``True``, interpret strings such as ``GMT+3`` or
|
||||||
|
@ -1203,7 +1264,7 @@ class tzical(object):
|
||||||
fileobj = open(fileobj, 'r')
|
fileobj = open(fileobj, 'r')
|
||||||
else:
|
else:
|
||||||
self._s = getattr(fileobj, 'name', repr(fileobj))
|
self._s = getattr(fileobj, 'name', repr(fileobj))
|
||||||
fileobj = _ContextWrapper(fileobj)
|
fileobj = _nullcontext(fileobj)
|
||||||
|
|
||||||
self._vtz = {}
|
self._vtz = {}
|
||||||
|
|
||||||
|
@ -1398,15 +1459,85 @@ else:
|
||||||
TZFILES = []
|
TZFILES = []
|
||||||
TZPATHS = []
|
TZPATHS = []
|
||||||
|
|
||||||
|
|
||||||
def __get_gettz(name, zoneinfo_priority=False):
|
def __get_gettz(name, zoneinfo_priority=False):
|
||||||
tzlocal_classes = (tzlocal,)
|
tzlocal_classes = (tzlocal,)
|
||||||
if tzwinlocal is not None:
|
if tzwinlocal is not None:
|
||||||
tzlocal_classes += (tzwinlocal,)
|
tzlocal_classes += (tzwinlocal,)
|
||||||
|
|
||||||
class GettzFunc(object):
|
class GettzFunc(object):
|
||||||
|
"""
|
||||||
|
Retrieve a time zone object from a string representation
|
||||||
|
|
||||||
|
This function is intended to retrieve the :py:class:`tzinfo` subclass
|
||||||
|
that best represents the time zone that would be used if a POSIX
|
||||||
|
`TZ variable`_ were set to the same value.
|
||||||
|
|
||||||
|
If no argument or an empty string is passed to ``gettz``, local time
|
||||||
|
is returned:
|
||||||
|
|
||||||
|
.. code-block:: python3
|
||||||
|
|
||||||
|
>>> gettz()
|
||||||
|
tzfile('/etc/localtime')
|
||||||
|
|
||||||
|
This function is also the preferred way to map IANA tz database keys
|
||||||
|
to :class:`tzfile` objects:
|
||||||
|
|
||||||
|
.. code-block:: python3
|
||||||
|
|
||||||
|
>>> gettz('Pacific/Kiritimati')
|
||||||
|
tzfile('/usr/share/zoneinfo/Pacific/Kiritimati')
|
||||||
|
|
||||||
|
On Windows, the standard is extended to include the Windows-specific
|
||||||
|
zone names provided by the operating system:
|
||||||
|
|
||||||
|
.. code-block:: python3
|
||||||
|
|
||||||
|
>>> gettz('Egypt Standard Time')
|
||||||
|
tzwin('Egypt Standard Time')
|
||||||
|
|
||||||
|
Passing a GNU ``TZ`` style string time zone specification returns a
|
||||||
|
:class:`tzstr` object:
|
||||||
|
|
||||||
|
.. code-block:: python3
|
||||||
|
|
||||||
|
>>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3')
|
||||||
|
tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3')
|
||||||
|
|
||||||
|
:param name:
|
||||||
|
A time zone name (IANA, or, on Windows, Windows keys), location of
|
||||||
|
a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone
|
||||||
|
specifier. An empty string, no argument or ``None`` is interpreted
|
||||||
|
as local time.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns an instance of one of ``dateutil``'s :py:class:`tzinfo`
|
||||||
|
subclasses.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.7.0
|
||||||
|
|
||||||
|
After version 2.7.0, any two calls to ``gettz`` using the same
|
||||||
|
input strings will return the same object:
|
||||||
|
|
||||||
|
.. code-block:: python3
|
||||||
|
|
||||||
|
>>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago')
|
||||||
|
True
|
||||||
|
|
||||||
|
In addition to improving performance, this ensures that
|
||||||
|
`"same zone" semantics`_ are used for datetimes in the same zone.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`TZ variable`:
|
||||||
|
https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
|
||||||
|
|
||||||
|
.. _`"same zone" semantics`:
|
||||||
|
https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html
|
||||||
|
"""
|
||||||
def __init__(self, name, zoneinfo_priority=False):
|
def __init__(self, name, zoneinfo_priority=False):
|
||||||
|
|
||||||
self.__instances = {}
|
self.__instances = weakref.WeakValueDictionary()
|
||||||
self._cache_lock = _thread.allocate_lock()
|
self._cache_lock = _thread.allocate_lock()
|
||||||
|
|
||||||
def __call__(self, name=None, zoneinfo_priority=False):
|
def __call__(self, name=None, zoneinfo_priority=False):
|
||||||
|
@ -1415,17 +1546,22 @@ def __get_gettz(name, zoneinfo_priority=False):
|
||||||
|
|
||||||
if rv is None:
|
if rv is None:
|
||||||
rv = self.nocache(name=name, zoneinfo_priority=zoneinfo_priority)
|
rv = self.nocache(name=name, zoneinfo_priority=zoneinfo_priority)
|
||||||
if not (name is None or isinstance(rv, tzlocal_classes)):
|
if not (name is None
|
||||||
|
or isinstance(rv, tzlocal_classes)
|
||||||
|
or rv is None):
|
||||||
# tzlocal is slightly more complicated than the other
|
# tzlocal is slightly more complicated than the other
|
||||||
# time zone providers because it depends on environment
|
# time zone providers because it depends on environment
|
||||||
# at construction time, so don't cache that.
|
# at construction time, so don't cache that.
|
||||||
|
#
|
||||||
|
# We also cannot store weak references to None, so we
|
||||||
|
# will also not store that.
|
||||||
self.__instances[name] = rv
|
self.__instances[name] = rv
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def cache_clear(self):
|
def cache_clear(self):
|
||||||
with self._cache_lock:
|
with self._cache_lock:
|
||||||
self.__instances = {}
|
self.__instances = weakref.WeakValueDictionary()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def nocache(name=None, zoneinfo_priority=False):
|
def nocache(name=None, zoneinfo_priority=False):
|
||||||
|
@ -1492,7 +1628,10 @@ def __get_gettz(name, zoneinfo_priority=False):
|
||||||
|
|
||||||
if not tz:
|
if not tz:
|
||||||
for c in name:
|
for c in name:
|
||||||
# name must have at least one offset to be a tzstr
|
# name is not a tzstr unless it has at least
|
||||||
|
# one offset. For short values of "name", an
|
||||||
|
# explicit for loop seems to be the fastest way
|
||||||
|
# To determine if a string contains a digit
|
||||||
if c in "0123456789":
|
if c in "0123456789":
|
||||||
try:
|
try:
|
||||||
tz = tzstr(name)
|
tz = tzstr(name)
|
||||||
|
@ -1508,9 +1647,11 @@ def __get_gettz(name, zoneinfo_priority=False):
|
||||||
|
|
||||||
return GettzFunc(name, zoneinfo_priority)
|
return GettzFunc(name, zoneinfo_priority)
|
||||||
|
|
||||||
|
|
||||||
gettz = __get_gettz(name=None, zoneinfo_priority=False)
|
gettz = __get_gettz(name=None, zoneinfo_priority=False)
|
||||||
del __get_gettz
|
del __get_gettz
|
||||||
|
|
||||||
|
|
||||||
def datetime_exists(dt, tz=None):
|
def datetime_exists(dt, tz=None):
|
||||||
"""
|
"""
|
||||||
Given a datetime and a time zone, determine whether or not a given datetime
|
Given a datetime and a time zone, determine whether or not a given datetime
|
||||||
|
@ -1525,9 +1666,10 @@ def datetime_exists(dt, tz=None):
|
||||||
``None`` or not provided, the datetime's own time zone will be used.
|
``None`` or not provided, the datetime's own time zone will be used.
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
Returns a boolean value whether or not the "wall time" exists in ``tz``.
|
Returns a boolean value whether or not the "wall time" exists in
|
||||||
|
``tz``.
|
||||||
|
|
||||||
..versionadded:: 2.7.0
|
.. versionadded:: 2.7.0
|
||||||
"""
|
"""
|
||||||
if tz is None:
|
if tz is None:
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
|
@ -1575,7 +1717,7 @@ def datetime_ambiguous(dt, tz=None):
|
||||||
if is_ambiguous_fn is not None:
|
if is_ambiguous_fn is not None:
|
||||||
try:
|
try:
|
||||||
return tz.is_ambiguous(dt)
|
return tz.is_ambiguous(dt)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If it doesn't come out and tell us it's ambiguous, we'll just check if
|
# If it doesn't come out and tell us it's ambiguous, we'll just check if
|
||||||
|
@ -1598,7 +1740,8 @@ def resolve_imaginary(dt):
|
||||||
wall time would be in a zone had the offset transition not occurred, so
|
wall time would be in a zone had the offset transition not occurred, so
|
||||||
it will always fall forward by the transition's change in offset.
|
it will always fall forward by the transition's change in offset.
|
||||||
|
|
||||||
..doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> from dateutil import tz
|
>>> from dateutil import tz
|
||||||
>>> from datetime import datetime
|
>>> from datetime import datetime
|
||||||
>>> NYC = tz.gettz('America/New_York')
|
>>> NYC = tz.gettz('America/New_York')
|
||||||
|
@ -1623,7 +1766,7 @@ def resolve_imaginary(dt):
|
||||||
imaginary, the datetime returned is guaranteed to be the same object
|
imaginary, the datetime returned is guaranteed to be the same object
|
||||||
passed to the function.
|
passed to the function.
|
||||||
|
|
||||||
..versionadded:: 2.7.0
|
.. versionadded:: 2.7.0
|
||||||
"""
|
"""
|
||||||
if dt.tzinfo is not None and not datetime_exists(dt):
|
if dt.tzinfo is not None and not datetime_exists(dt):
|
||||||
|
|
||||||
|
@ -1637,24 +1780,42 @@ def resolve_imaginary(dt):
|
||||||
|
|
||||||
def _datetime_to_timestamp(dt):
|
def _datetime_to_timestamp(dt):
|
||||||
"""
|
"""
|
||||||
Convert a :class:`datetime.datetime` object to an epoch timestamp in seconds
|
Convert a :class:`datetime.datetime` object to an epoch timestamp in
|
||||||
since January 1, 1970, ignoring the time zone.
|
seconds since January 1, 1970, ignoring the time zone.
|
||||||
"""
|
"""
|
||||||
return (dt.replace(tzinfo=None) - EPOCH).total_seconds()
|
return (dt.replace(tzinfo=None) - EPOCH).total_seconds()
|
||||||
|
|
||||||
|
|
||||||
class _ContextWrapper(object):
|
if sys.version_info >= (3, 6):
|
||||||
"""
|
def _get_supported_offset(second_offset):
|
||||||
Class for wrapping contexts so that they are passed through in a
|
return second_offset
|
||||||
with statement.
|
else:
|
||||||
"""
|
def _get_supported_offset(second_offset):
|
||||||
def __init__(self, context):
|
# For python pre-3.6, round to full-minutes if that's not the case.
|
||||||
self.context = context
|
# Python's datetime doesn't accept sub-minute timezones. Check
|
||||||
|
# http://python.org/sf/1447945 or https://bugs.python.org/issue5288
|
||||||
|
# for some information.
|
||||||
|
old_offset = second_offset
|
||||||
|
calculated_offset = 60 * ((second_offset + 30) // 60)
|
||||||
|
return calculated_offset
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self.context
|
|
||||||
|
|
||||||
def __exit__(*args, **kwargs):
|
try:
|
||||||
pass
|
# Python 3.7 feature
|
||||||
|
from contextmanager import nullcontext as _nullcontext
|
||||||
|
except ImportError:
|
||||||
|
class _nullcontext(object):
|
||||||
|
"""
|
||||||
|
Class for wrapping contexts so that they are passed through in a
|
||||||
|
with statement.
|
||||||
|
"""
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.context
|
||||||
|
|
||||||
|
def __exit__(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
# vim:ts=4:sw=4:et
|
# vim:ts=4:sw=4:et
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
This module provides an interface to the native time zone data on Windows,
|
||||||
|
including :py:class:`datetime.tzinfo` implementations.
|
||||||
|
|
||||||
|
Attempting to import this module on a non-Windows platform will raise an
|
||||||
|
:py:obj:`ImportError`.
|
||||||
|
"""
|
||||||
# This code was originally contributed by Jeffrey Harris.
|
# This code was originally contributed by Jeffrey Harris.
|
||||||
import datetime
|
import datetime
|
||||||
import struct
|
import struct
|
||||||
|
@ -39,7 +47,7 @@ TZKEYNAME = _settzkeyname()
|
||||||
|
|
||||||
class tzres(object):
|
class tzres(object):
|
||||||
"""
|
"""
|
||||||
Class for accessing `tzres.dll`, which contains timezone name related
|
Class for accessing ``tzres.dll``, which contains timezone name related
|
||||||
resources.
|
resources.
|
||||||
|
|
||||||
.. versionadded:: 2.5.0
|
.. versionadded:: 2.5.0
|
||||||
|
@ -72,9 +80,10 @@ class tzres(object):
|
||||||
:param offset:
|
:param offset:
|
||||||
A positive integer value referring to a string from the tzres dll.
|
A positive integer value referring to a string from the tzres dll.
|
||||||
|
|
||||||
..note:
|
.. note::
|
||||||
|
|
||||||
Offsets found in the registry are generally of the form
|
Offsets found in the registry are generally of the form
|
||||||
`@tzres.dll,-114`. The offset in this case if 114, not -114.
|
``@tzres.dll,-114``. The offset in this case is 114, not -114.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
resource = self.p_wchar()
|
resource = self.p_wchar()
|
||||||
|
@ -146,6 +155,9 @@ class tzwinbase(tzrangebase):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def display(self):
|
def display(self):
|
||||||
|
"""
|
||||||
|
Return the display name of the time zone.
|
||||||
|
"""
|
||||||
return self._display
|
return self._display
|
||||||
|
|
||||||
def transitions(self, year):
|
def transitions(self, year):
|
||||||
|
@ -188,6 +200,17 @@ class tzwinbase(tzrangebase):
|
||||||
|
|
||||||
|
|
||||||
class tzwin(tzwinbase):
|
class tzwin(tzwinbase):
|
||||||
|
"""
|
||||||
|
Time zone object created from the zone info in the Windows registry
|
||||||
|
|
||||||
|
These are similar to :py:class:`dateutil.tz.tzrange` objects in that
|
||||||
|
the time zone data is provided in the format of a single offset rule
|
||||||
|
for either 0 or 2 time zone transitions per year.
|
||||||
|
|
||||||
|
:param: name
|
||||||
|
The name of a Windows time zone key, e.g. "Eastern Standard Time".
|
||||||
|
The full list of keys can be retrieved with :func:`tzwin.list`.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
@ -234,6 +257,22 @@ class tzwin(tzwinbase):
|
||||||
|
|
||||||
|
|
||||||
class tzwinlocal(tzwinbase):
|
class tzwinlocal(tzwinbase):
|
||||||
|
"""
|
||||||
|
Class representing the local time zone information in the Windows registry
|
||||||
|
|
||||||
|
While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time`
|
||||||
|
module) to retrieve time zone information, ``tzwinlocal`` retrieves the
|
||||||
|
rules directly from the Windows registry and creates an object like
|
||||||
|
:class:`dateutil.tz.tzwin`.
|
||||||
|
|
||||||
|
Because Windows does not have an equivalent of :func:`time.tzset`, on
|
||||||
|
Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the
|
||||||
|
time zone settings *at the time that the process was started*, meaning
|
||||||
|
changes to the machine's time zone settings during the run of a program
|
||||||
|
on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`.
|
||||||
|
Because ``tzwinlocal`` reads the registry directly, it is unaffected by
|
||||||
|
this issue.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
This module offers general convenience and utility functions for dealing with
|
||||||
|
datetimes.
|
||||||
|
|
||||||
|
.. versionadded:: 2.7.0
|
||||||
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from datetime import datetime, time
|
from datetime import datetime, time
|
||||||
|
|
Loading…
Reference in a new issue