mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-07 10:33:38 +00:00
Merge pull request #510 from JackDandy/feature/UpdateDateutil
Feature/update dateutil
This commit is contained in:
commit
30b569aeb1
8 changed files with 263 additions and 175 deletions
|
@ -15,6 +15,7 @@
|
||||||
* Remove legacy anime split home option from anime settings tab (new option located in general/interface tab)
|
* Remove legacy anime split home option from anime settings tab (new option located in general/interface tab)
|
||||||
* Remove "Manage Torrents"
|
* Remove "Manage Torrents"
|
||||||
* Update Beautiful Soup 4.3.2 to 4.4.0 (r390)
|
* Update Beautiful Soup 4.3.2 to 4.4.0 (r390)
|
||||||
|
* Update dateutil library to 2.4.2 (083f666)
|
||||||
* Update Hachoir library 1.3.3 to 1.3.4 (r1383)
|
* Update Hachoir library 1.3.3 to 1.3.4 (r1383)
|
||||||
* Change configure quiet option in Hachoir to suppress warnings (add ref:hacks.txt)
|
* Change configure quiet option in Hachoir to suppress warnings (add ref:hacks.txt)
|
||||||
* Add parse media content to determine quality before making final assumptions during re-scan, update, pp
|
* Add parse media content to determine quality before making final assumptions during re-scan, update, pp
|
||||||
|
|
|
@ -4,28 +4,29 @@ This module offers a generic date/time string parser which is able to parse
|
||||||
most known formats to represent a date and/or time.
|
most known formats to represent a date and/or time.
|
||||||
|
|
||||||
This module attempts to be forgiving with regards to unlikely input formats,
|
This module attempts to be forgiving with regards to unlikely input formats,
|
||||||
returning a datetime object even for dates which are ambiguous. If an element of
|
returning a datetime object even for dates which are ambiguous. If an element
|
||||||
a date/time stamp is omitted, the following rules are applied:
|
of a date/time stamp is omitted, the following rules are applied:
|
||||||
- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour
|
- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour
|
||||||
on a 12-hour clock (`0 <= hour <= 12`) *must* be specified if AM or PM is
|
on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is
|
||||||
specified.
|
specified.
|
||||||
- If a time zone is omitted, it is assumed to be UTC.
|
- If a time zone is omitted, a timezone-naive datetime is returned.
|
||||||
|
|
||||||
If any other elements are missing, they are taken from the `datetime.datetime`
|
If any other elements are missing, they are taken from the
|
||||||
object passed to the parameter `default`. If this results in a day number
|
:class:`datetime.datetime` object passed to the parameter ``default``. If this
|
||||||
exceeding the valid number of days per month, one can fall back to the last
|
results in a day number exceeding the valid number of days per month, one can
|
||||||
day of the month by setting `fallback_on_invalid_day` parameter to `True`.
|
fall back to the last day of the month by setting ``fallback_on_invalid_day``
|
||||||
|
parameter to ``True``.
|
||||||
|
|
||||||
Also provided is the `smart_defaults` option, which attempts to fill in the
|
Also provided is the ``smart_defaults`` option, which attempts to fill in the
|
||||||
missing elements from context. If specified, the logic is:
|
missing elements from context. If specified, the logic is:
|
||||||
- If the omitted element is smaller than the largest specified element, select
|
- If the omitted element is smaller than the largest specified element, select
|
||||||
the *earliest* time matching the specified conditions; so `"June 2010"` is
|
the *earliest* time matching the specified conditions; so ``"June 2010"`` is
|
||||||
interpreted as `June 1, 2010 0:00:00`) and the (somewhat strange)
|
interpreted as ``June 1, 2010 0:00:00``) and the (somewhat strange)
|
||||||
`"Feb 1997 3:15 PM"` is interpreted as `February 1, 1997 15:15:00`.
|
``"Feb 1997 3:15 PM"`` is interpreted as ``February 1, 1997 15:15:00``.
|
||||||
- If the element is larger than the largest specified element, select the
|
- If the element is larger than the largest specified element, select the
|
||||||
*most recent* time matching the specified conditions (e.g parsing `"May"`
|
*most recent* time matching the specified conditions (e.g parsing ``"May"``
|
||||||
in June 2015 returns the date May 1st, 2015, whereas parsing it in April 2015
|
in June 2015 returns the date May 1st, 2015, whereas parsing it in April 2015
|
||||||
returns May 1st 2014). If using the `date_in_future` flag, this logic is
|
returns May 1st 2014). If using the ``date_in_future`` flag, this logic is
|
||||||
inverted, and instead the *next* time matching the specified conditions is
|
inverted, and instead the *next* time matching the specified conditions is
|
||||||
returned.
|
returned.
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ import datetime
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
import collections
|
import collections
|
||||||
|
import re
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from calendar import monthrange, isleap
|
from calendar import monthrange, isleap
|
||||||
|
|
||||||
|
@ -58,6 +60,9 @@ __all__ = ["parse", "parserinfo"]
|
||||||
|
|
||||||
|
|
||||||
class _timelex(object):
|
class _timelex(object):
|
||||||
|
# Fractional seconds are sometimes split by a comma
|
||||||
|
_split_decimal = re.compile("([\.,])")
|
||||||
|
|
||||||
def __init__(self, instream):
|
def __init__(self, instream):
|
||||||
if isinstance(instream, binary_type):
|
if isinstance(instream, binary_type):
|
||||||
instream = instream.decode()
|
instream = instream.decode()
|
||||||
|
@ -80,8 +85,8 @@ class _timelex(object):
|
||||||
"""
|
"""
|
||||||
This function breaks the time string into lexical units (tokens), which
|
This function breaks the time string into lexical units (tokens), which
|
||||||
can be parsed by the parser. Lexical units are demarcated by changes in
|
can be parsed by the parser. Lexical units are demarcated by changes in
|
||||||
the character set, so any continuous string of letters is considered one
|
the character set, so any continuous string of letters is considered
|
||||||
unit, any continuous string of numbers is considered one unit.
|
one unit, any continuous string of numbers is considered one unit.
|
||||||
|
|
||||||
The main complication arises from the fact that dots ('.') can be used
|
The main complication arises from the fact that dots ('.') can be used
|
||||||
both as separators (e.g. "Sep.20.2009") or decimal points (e.g.
|
both as separators (e.g. "Sep.20.2009") or decimal points (e.g.
|
||||||
|
@ -101,9 +106,9 @@ class _timelex(object):
|
||||||
whitespace = self.whitespace
|
whitespace = self.whitespace
|
||||||
|
|
||||||
while not self.eof:
|
while not self.eof:
|
||||||
# We only realize that we've reached the end of a token when we find
|
# We only realize that we've reached the end of a token when we
|
||||||
# a character that's not part of the current token - since that
|
# find a character that's not part of the current token - since
|
||||||
# character may be part of the next token, it's stored in the
|
# that character may be part of the next token, it's stored in the
|
||||||
# charstack.
|
# charstack.
|
||||||
if self.charstack:
|
if self.charstack:
|
||||||
nextchar = self.charstack.pop(0)
|
nextchar = self.charstack.pop(0)
|
||||||
|
@ -145,7 +150,7 @@ class _timelex(object):
|
||||||
# numbers until we find something that doesn't fit.
|
# numbers until we find something that doesn't fit.
|
||||||
if nextchar in numchars:
|
if nextchar in numchars:
|
||||||
token += nextchar
|
token += nextchar
|
||||||
elif nextchar == '.':
|
elif nextchar == '.' or (nextchar == ',' and len(token) >= 2):
|
||||||
token += nextchar
|
token += nextchar
|
||||||
state = '0.'
|
state = '0.'
|
||||||
else:
|
else:
|
||||||
|
@ -176,14 +181,16 @@ class _timelex(object):
|
||||||
break # emit token
|
break # emit token
|
||||||
|
|
||||||
if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or
|
if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or
|
||||||
token[-1] == '.')):
|
token[-1] in '.,')):
|
||||||
l = token.split('.')
|
l = self._split_decimal.split(token)
|
||||||
token = l[0]
|
token = l[0]
|
||||||
for tok in l[1:]:
|
for tok in l[1:]:
|
||||||
self.tokenstack.append('.')
|
|
||||||
if tok:
|
if tok:
|
||||||
self.tokenstack.append(tok)
|
self.tokenstack.append(tok)
|
||||||
|
|
||||||
|
if state == '0.' and token.count('.') == 0:
|
||||||
|
token = token.replace(',', '.')
|
||||||
|
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
@ -224,20 +231,20 @@ class _resultbase(object):
|
||||||
|
|
||||||
class parserinfo(object):
|
class parserinfo(object):
|
||||||
"""
|
"""
|
||||||
Class which handles what inputs are accepted. Subclass this to customize the
|
Class which handles what inputs are accepted. Subclass this to customize
|
||||||
language and acceptable values for each parameter.
|
the language and acceptable values for each parameter.
|
||||||
|
|
||||||
:param dayfirst:
|
:param dayfirst:
|
||||||
Whether to interpret the first value in an ambiguous 3-integer date
|
Whether to interpret the first value in an ambiguous 3-integer date
|
||||||
(e.g. 01/05/09) as the day (`True`) or month (`False`). If
|
(e.g. 01/05/09) as the day (``True``) or month (``False``). If
|
||||||
`yearfirst` is set to `True`, this distinguishes between YDM and
|
``yearfirst`` is set to ``True``, this distinguishes between YDM
|
||||||
YMD. Default is `False`.
|
and YMD. Default is ``False``.
|
||||||
|
|
||||||
:param yearfirst:
|
:param yearfirst:
|
||||||
Whether to interpret the first value in an ambiguous 3-integer date
|
Whether to interpret the first value in an ambiguous 3-integer date
|
||||||
(e.g. 01/05/09) as the year. If `True`, the first number is taken to
|
(e.g. 01/05/09) as the year. If ``True``, the first number is taken
|
||||||
be the year, otherwise the last number is taken to be the year.
|
to be the year, otherwise the last number is taken to be the year.
|
||||||
Default is `False`.
|
Default is ``False``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# m from a.m/p.m, t from ISO T separator
|
# m from a.m/p.m, t from ISO T separator
|
||||||
|
@ -287,7 +294,7 @@ class parserinfo(object):
|
||||||
self.smart_defaults = smart_defaults
|
self.smart_defaults = smart_defaults
|
||||||
|
|
||||||
self._year = time.localtime().tm_year
|
self._year = time.localtime().tm_year
|
||||||
self._century = self._year // 100*100
|
self._century = self._year // 100 * 100
|
||||||
|
|
||||||
def _convert(self, lst):
|
def _convert(self, lst):
|
||||||
dct = {}
|
dct = {}
|
||||||
|
@ -313,7 +320,7 @@ class parserinfo(object):
|
||||||
def month(self, name):
|
def month(self, name):
|
||||||
if len(name) >= 3:
|
if len(name) >= 3:
|
||||||
try:
|
try:
|
||||||
return self._months[name.lower()]+1
|
return self._months[name.lower()] + 1
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
@ -345,7 +352,7 @@ class parserinfo(object):
|
||||||
def convertyear(self, year):
|
def convertyear(self, year):
|
||||||
if year < 100:
|
if year < 100:
|
||||||
year += self._century
|
year += self._century
|
||||||
if abs(year-self._year) >= 50:
|
if abs(year - self._year) >= 50:
|
||||||
if year < self._year:
|
if year < self._year:
|
||||||
year += 100
|
year += 100
|
||||||
else:
|
else:
|
||||||
|
@ -373,65 +380,87 @@ class parser(object):
|
||||||
smart_defaults=None, date_in_future=False,
|
smart_defaults=None, date_in_future=False,
|
||||||
fallback_on_invalid_day=None, **kwargs):
|
fallback_on_invalid_day=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Parse the date/time string into a datetime object.
|
Parse the date/time string into a :class:`datetime.datetime` object.
|
||||||
|
|
||||||
:param timestr:
|
:param timestr:
|
||||||
Any date/time string using the supported formats.
|
Any date/time string using the supported formats.
|
||||||
|
|
||||||
:param default:
|
:param default:
|
||||||
The default datetime object, if this is a datetime object and not
|
The default datetime object, if this is a datetime object and not
|
||||||
`None`, elements specified in `timestr` replace elements in the
|
``None``, elements specified in ``timestr`` replace elements in the
|
||||||
default object, unless `smart_defaults` is set to `True`, in which
|
default object, unless ``smart_defaults`` is set to ``True``, in
|
||||||
case to the extent necessary, timestamps are calculated relative to
|
which case to the extent necessary, timestamps are calculated
|
||||||
this date.
|
relative to this date.
|
||||||
|
|
||||||
:param smart_defaults:
|
:param smart_defaults:
|
||||||
If using smart defaults, the `default` parameter is treated as the
|
If using smart defaults, the ``default`` parameter is treated as
|
||||||
effective parsing date/time, and the context of the datetime string
|
the effective parsing date/time, and the context of the datetime
|
||||||
is determined relative to `default`. If `None`, this parameter is
|
string is determined relative to ``default``. If ``None``, this
|
||||||
inherited from the :class:`parserinfo` object.
|
parameter is inherited from the :class:`parserinfo` object.
|
||||||
|
|
||||||
:param date_in_future:
|
:param date_in_future:
|
||||||
If `smart_defaults` is `True`, the parser assumes by default that
|
If ``smart_defaults`` is ``True``, the parser assumes by default
|
||||||
the timestamp refers to a date in the past, and will return the
|
that the timestamp refers to a date in the past, and will return
|
||||||
beginning of the most recent timespan which matches the time string
|
the beginning of the most recent timespan which matches the time
|
||||||
(e.g. if `default` is March 3rd, 2013, "Feb" parses to
|
string (e.g. if ``default`` is March 3rd, 2013, "Feb" parses to
|
||||||
"Feb 1, 2013" and "May 3" parses to May 3rd, 2012). Setting this
|
"Feb 1, 2013" and "May 3" parses to May 3rd, 2012). Setting this
|
||||||
parameter to `True` inverts this assumption, and returns the
|
parameter to ``True`` inverts this assumption, and returns the
|
||||||
beginning of the *next* matching timespan.
|
beginning of the *next* matching timespan.
|
||||||
|
|
||||||
:param fallback_on_invalid_day:
|
:param fallback_on_invalid_day:
|
||||||
If specified `True`, an otherwise invalid date such as "Feb 30" or
|
If specified ``True``, an otherwise invalid date such as "Feb 30"
|
||||||
"June 32" falls back to the last day of the month. If specified as
|
or "June 32" falls back to the last day of the month. If specified
|
||||||
"False", the parser is strict about parsing otherwise valid dates
|
as "False", the parser is strict about parsing otherwise valid
|
||||||
that would turn up as invalid because of the fallback rules (e.g.
|
dates that would turn up as invalid because of the fallback rules
|
||||||
"Feb 2010" run with a default of January 30, 2010 and `smartparser`
|
(e.g. "Feb 2010" run with a default of January 30, 2010 and
|
||||||
set to `False` would would throw an error, rather than falling
|
``smartparser`` set to ``False`` would would throw an error, rather
|
||||||
back to the end of February). If `None` or unspecified, the date
|
than falling back to the end of February). If ``None`` or
|
||||||
falls back to the most recent valid date only if the invalid date
|
unspecified, the date falls back to the most recent valid date only
|
||||||
is created as a result of an unspecified day in the time string.
|
if the invalid date is created as a result of an unspecified day in
|
||||||
|
the time string.
|
||||||
|
|
||||||
:param ignoretz:
|
:param ignoretz:
|
||||||
Whether or not to ignore the time zone.
|
If set ``True``, time zones in parsed strings are ignored and a
|
||||||
|
naive :class:`datetime.datetime` object is returned.
|
||||||
|
|
||||||
:param tzinfos:
|
:param tzinfos:
|
||||||
A time zone, to be applied to the date, if `ignoretz` is `True`.
|
Additional time zone names / aliases which may be present in the
|
||||||
This can be either a subclass of `tzinfo`, a time zone string or an
|
string. This argument maps time zone names (and optionally offsets
|
||||||
integer offset.
|
from those time zones) to time zones. This parameter can be a
|
||||||
|
dictionary with timezone aliases mapping time zone names to time
|
||||||
|
zones or a function taking two parameters (``tzname`` and
|
||||||
|
``tzoffset``) and returning a time zone.
|
||||||
|
|
||||||
|
The timezones to which the names are mapped can be an integer
|
||||||
|
offset from UTC in minutes or a :class:`tzinfo` object.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
:options: +NORMALIZE_WHITESPACE
|
||||||
|
|
||||||
|
>>> from dateutil.parser import parse
|
||||||
|
>>> from dateutil.tz import gettz
|
||||||
|
>>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")}
|
||||||
|
>>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
|
||||||
|
datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800))
|
||||||
|
>>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
|
||||||
|
datetime.datetime(2012, 1, 19, 17, 21,
|
||||||
|
tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))
|
||||||
|
|
||||||
|
This parameter is ignored if ``ignoretz`` is set.
|
||||||
|
|
||||||
:param **kwargs:
|
:param **kwargs:
|
||||||
Keyword arguments as passed to `_parse()`.
|
Keyword arguments as passed to ``_parse()``.
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
Returns a `datetime.datetime` object or, if the `fuzzy_with_tokens`
|
Returns a :class:`datetime.datetime` object or, if the
|
||||||
option is `True`, returns a tuple, the first element being a
|
``fuzzy_with_tokens`` option is ``True``, returns a tuple, the
|
||||||
`datetime.datetime` object, the second a tuple containing the
|
first element being a :class:`datetime.datetime` object, the second
|
||||||
fuzzy tokens.
|
a tuple containing the fuzzy tokens.
|
||||||
|
|
||||||
:raises ValueError:
|
:raises ValueError:
|
||||||
Raised for invalid or unknown string format, if the provided
|
Raised for invalid or unknown string format, if the provided
|
||||||
`tzinfo` is not in a valid format, or if an invalid date would
|
:class:`tzinfo` is not in a valid format, or if an invalid date
|
||||||
be created.
|
would be created.
|
||||||
|
|
||||||
:raises OverFlowError:
|
:raises OverFlowError:
|
||||||
Raised if the parsed date exceeds the largest valid C integer on
|
Raised if the parsed date exceeds the largest valid C integer on
|
||||||
|
@ -444,14 +473,11 @@ class parser(object):
|
||||||
if default is None:
|
if default is None:
|
||||||
effective_dt = datetime.datetime.now()
|
effective_dt = datetime.datetime.now()
|
||||||
default = datetime.datetime.now().replace(hour=0, minute=0,
|
default = datetime.datetime.now().replace(hour=0, minute=0,
|
||||||
second=0, microsecond=0)
|
second=0, microsecond=0)
|
||||||
else:
|
else:
|
||||||
effective_dt = default
|
effective_dt = default
|
||||||
|
|
||||||
if kwargs.get('fuzzy_with_tokens', False):
|
res, skipped_tokens = self._parse(timestr, **kwargs)
|
||||||
res, skipped_tokens = self._parse(timestr, **kwargs)
|
|
||||||
else:
|
|
||||||
res = self._parse(timestr, **kwargs)
|
|
||||||
|
|
||||||
if res is None:
|
if res is None:
|
||||||
raise ValueError("Unknown string format")
|
raise ValueError("Unknown string format")
|
||||||
|
@ -464,7 +490,7 @@ class parser(object):
|
||||||
repl[attr] = value
|
repl[attr] = value
|
||||||
|
|
||||||
# Choose the correct fallback position if requested by the
|
# Choose the correct fallback position if requested by the
|
||||||
# `smart_defaults` parameter.
|
# ``smart_defaults`` parameter.
|
||||||
if smart_defaults:
|
if smart_defaults:
|
||||||
# Determine if it refers to this year, last year or next year
|
# Determine if it refers to this year, last year or next year
|
||||||
if res.year is None:
|
if res.year is None:
|
||||||
|
@ -472,7 +498,7 @@ class parser(object):
|
||||||
# Explicitly deal with leap year problems
|
# Explicitly deal with leap year problems
|
||||||
if res.month == 2 and (res.day is not None and
|
if res.month == 2 and (res.day is not None and
|
||||||
res.day == 29):
|
res.day == 29):
|
||||||
|
|
||||||
ly_offset = 4 if date_in_future else -4
|
ly_offset = 4 if date_in_future else -4
|
||||||
next_year = 4 * (default.year // 4)
|
next_year = 4 * (default.year // 4)
|
||||||
|
|
||||||
|
@ -583,36 +609,42 @@ class parser(object):
|
||||||
fuzzy_with_tokens=False):
|
fuzzy_with_tokens=False):
|
||||||
"""
|
"""
|
||||||
Private method which performs the heavy lifting of parsing, called from
|
Private method which performs the heavy lifting of parsing, called from
|
||||||
`parse()`, which passes on its `kwargs` to this function.
|
``parse()``, which passes on its ``kwargs`` to this function.
|
||||||
|
|
||||||
:param timestr:
|
:param timestr:
|
||||||
The string to parse.
|
The string to parse.
|
||||||
|
|
||||||
:param dayfirst:
|
:param dayfirst:
|
||||||
Whether to interpret the first value in an ambiguous 3-integer date
|
Whether to interpret the first value in an ambiguous 3-integer date
|
||||||
(e.g. 01/05/09) as the day (`True`) or month (`False`). If
|
(e.g. 01/05/09) as the day (``True``) or month (``False``). If
|
||||||
`yearfirst` is set to `True`, this distinguishes between YDM and
|
``yearfirst`` is set to ``True``, this distinguishes between YDM
|
||||||
YMD. If set to `None`, this value is retrieved from the current
|
and YMD. If set to ``None``, this value is retrieved from the
|
||||||
`parserinfo` object (which itself defaults to `False`).
|
current :class:`parserinfo` object (which itself defaults to
|
||||||
|
``False``).
|
||||||
|
|
||||||
:param yearfirst:
|
:param yearfirst:
|
||||||
Whether to interpret the first value in an ambiguous 3-integer date
|
Whether to interpret the first value in an ambiguous 3-integer date
|
||||||
(e.g. 01/05/09) as the year. If `True`, the first number is taken to
|
(e.g. 01/05/09) as the year. If ``True``, the first number is taken
|
||||||
be the year, otherwise the last number is taken to be the year. If
|
to be the year, otherwise the last number is taken to be the year.
|
||||||
this is set to `None`, the value is retrieved from the current
|
If this is set to ``None``, the value is retrieved from the current
|
||||||
`parserinfo` object (which itself defaults to `False`).
|
:class:`parserinfo` object (which itself defaults to ``False``).
|
||||||
|
|
||||||
:param fuzzy:
|
:param fuzzy:
|
||||||
Whether to allow fuzzy parsing, allowing for string like "Today is
|
Whether to allow fuzzy parsing, allowing for string like "Today is
|
||||||
January 1, 2047 at 8:21:00AM".
|
January 1, 2047 at 8:21:00AM".
|
||||||
|
|
||||||
:param fuzzy_with_tokens:
|
:param fuzzy_with_tokens:
|
||||||
If `True`, `fuzzy` is automatically set to True, and the parser will
|
If ``True``, ``fuzzy`` is automatically set to True, and the parser
|
||||||
return a tuple where the first element is the parsed
|
will return a tuple where the first element is the parsed
|
||||||
`datetime.datetime` datetimestamp and the second element is a tuple
|
:class:`datetime.datetime` datetimestamp and the second element is
|
||||||
containing the portions of the string which were ignored, e.g.
|
a tuple containing the portions of the string which were ignored:
|
||||||
"Today is January 1, 2047 at 8:21:00AM" should return
|
|
||||||
`(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))`
|
.. doctest::
|
||||||
|
|
||||||
|
>>> from dateutil.parser import parse
|
||||||
|
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
|
||||||
|
(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if fuzzy_with_tokens:
|
if fuzzy_with_tokens:
|
||||||
fuzzy = True
|
fuzzy = True
|
||||||
|
@ -796,7 +828,7 @@ class parser(object):
|
||||||
assert mstridx == -1
|
assert mstridx == -1
|
||||||
mstridx = len(ymd)-1
|
mstridx = len(ymd)-1
|
||||||
else:
|
else:
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
@ -840,7 +872,7 @@ class parser(object):
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
elif not fuzzy:
|
elif not fuzzy:
|
||||||
return None
|
return None, None
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
|
@ -969,7 +1001,7 @@ class parser(object):
|
||||||
# -[0]3
|
# -[0]3
|
||||||
res.tzoffset = int(l[i][:2])*3600
|
res.tzoffset = int(l[i][:2])*3600
|
||||||
else:
|
else:
|
||||||
return None
|
return None, None
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
res.tzoffset *= signal
|
res.tzoffset *= signal
|
||||||
|
@ -987,7 +1019,7 @@ class parser(object):
|
||||||
|
|
||||||
# Check jumps
|
# Check jumps
|
||||||
if not (info.jump(l[i]) or fuzzy):
|
if not (info.jump(l[i]) or fuzzy):
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
if last_skipped_token_i == i - 1:
|
if last_skipped_token_i == i - 1:
|
||||||
# recombine the tokens
|
# recombine the tokens
|
||||||
|
@ -1002,7 +1034,7 @@ class parser(object):
|
||||||
len_ymd = len(ymd)
|
len_ymd = len(ymd)
|
||||||
if len_ymd > 3:
|
if len_ymd > 3:
|
||||||
# More than three members!?
|
# More than three members!?
|
||||||
return None
|
return None, None
|
||||||
elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2):
|
elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2):
|
||||||
# One member, or two members with a month string
|
# One member, or two members with a month string
|
||||||
if mstridx != -1:
|
if mstridx != -1:
|
||||||
|
@ -1066,72 +1098,113 @@ class parser(object):
|
||||||
res.month, res.day, res.year = ymd
|
res.month, res.day, res.year = ymd
|
||||||
|
|
||||||
except (IndexError, ValueError, AssertionError):
|
except (IndexError, ValueError, AssertionError):
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
if not info.validate(res):
|
if not info.validate(res):
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
if fuzzy_with_tokens:
|
if fuzzy_with_tokens:
|
||||||
return res, tuple(skipped_tokens)
|
return res, tuple(skipped_tokens)
|
||||||
else:
|
else:
|
||||||
return res
|
return res, None
|
||||||
|
|
||||||
DEFAULTPARSER = parser()
|
DEFAULTPARSER = parser()
|
||||||
|
|
||||||
|
|
||||||
def parse(timestr, parserinfo=None, **kwargs):
|
def parse(timestr, parserinfo=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Parse a string in one of the supported formats, using the `parserinfo`
|
|
||||||
parameters.
|
Parse a string in one of the supported formats, using the
|
||||||
|
``parserinfo`` parameters.
|
||||||
|
|
||||||
:param timestr:
|
:param timestr:
|
||||||
A string containing a date/time stamp.
|
A string containing a date/time stamp.
|
||||||
|
|
||||||
:param parserinfo:
|
:param parserinfo:
|
||||||
A :class:`parserinfo` object containing parameters for the parser.
|
A :class:`parserinfo` object containing parameters for the parser.
|
||||||
If `None`, the default arguments to the `parserinfo` constructor are
|
If ``None``, the default arguments to the :class:`parserinfo`
|
||||||
used.
|
constructor are used.
|
||||||
|
|
||||||
The `**kwargs` parameter takes the following keyword arguments:
|
The ``**kwargs`` parameter takes the following keyword arguments:
|
||||||
|
|
||||||
:param default:
|
:param default:
|
||||||
The default datetime object, if this is a datetime object and not
|
The default datetime object, if this is a datetime object and not
|
||||||
`None`, elements specified in `timestr` replace elements in the
|
``None``, elements specified in ``timestr`` replace elements in the
|
||||||
default object.
|
default object.
|
||||||
|
|
||||||
:param ignoretz:
|
:param ignoretz:
|
||||||
Whether or not to ignore the time zone (boolean).
|
If set ``True``, time zones in parsed strings are ignored and a naive
|
||||||
|
:class:`datetime` object is returned.
|
||||||
|
|
||||||
:param tzinfos:
|
:param tzinfos:
|
||||||
A time zone, to be applied to the date, if `ignoretz` is `True`.
|
Additional time zone names / aliases which may be present in the
|
||||||
This can be either a subclass of `tzinfo`, a time zone string or an
|
string. This argument maps time zone names (and optionally offsets
|
||||||
integer offset.
|
from those time zones) to time zones. This parameter can be a
|
||||||
|
dictionary with timezone aliases mapping time zone names to time
|
||||||
|
zones or a function taking two parameters (``tzname`` and
|
||||||
|
``tzoffset``) and returning a time zone.
|
||||||
|
|
||||||
|
The timezones to which the names are mapped can be an integer
|
||||||
|
offset from UTC in minutes or a :class:`tzinfo` object.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
:options: +NORMALIZE_WHITESPACE
|
||||||
|
|
||||||
|
>>> from dateutil.parser import parse
|
||||||
|
>>> from dateutil.tz import gettz
|
||||||
|
>>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")}
|
||||||
|
>>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
|
||||||
|
datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800))
|
||||||
|
>>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
|
||||||
|
datetime.datetime(2012, 1, 19, 17, 21,
|
||||||
|
tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))
|
||||||
|
|
||||||
|
This parameter is ignored if ``ignoretz`` is set.
|
||||||
|
|
||||||
:param dayfirst:
|
:param dayfirst:
|
||||||
Whether to interpret the first value in an ambiguous 3-integer date
|
Whether to interpret the first value in an ambiguous 3-integer date
|
||||||
(e.g. 01/05/09) as the day (`True`) or month (`False`). If
|
(e.g. 01/05/09) as the day (``True``) or month (``False``). If
|
||||||
`yearfirst` is set to `True`, this distinguishes between YDM and
|
``yearfirst`` is set to ``True``, this distinguishes between YDM and
|
||||||
YMD. If set to `None`, this value is retrieved from the current
|
YMD. If set to ``None``, this value is retrieved from the current
|
||||||
:class:`parserinfo` object (which itself defaults to `False`).
|
:class:`parserinfo` object (which itself defaults to ``False``).
|
||||||
|
|
||||||
:param yearfirst:
|
:param yearfirst:
|
||||||
Whether to interpret the first value in an ambiguous 3-integer date
|
Whether to interpret the first value in an ambiguous 3-integer date
|
||||||
(e.g. 01/05/09) as the year. If `True`, the first number is taken to
|
(e.g. 01/05/09) as the year. If ``True``, the first number is taken to
|
||||||
be the year, otherwise the last number is taken to be the year. If
|
be the year, otherwise the last number is taken to be the year. If
|
||||||
this is set to `None`, the value is retrieved from the current
|
this is set to ``None``, the value is retrieved from the current
|
||||||
:class:`parserinfo` object (which itself defaults to `False`).
|
:class:`parserinfo` object (which itself defaults to ``False``).
|
||||||
|
|
||||||
:param fuzzy:
|
:param fuzzy:
|
||||||
Whether to allow fuzzy parsing, allowing for string like "Today is
|
Whether to allow fuzzy parsing, allowing for string like "Today is
|
||||||
January 1, 2047 at 8:21:00AM".
|
January 1, 2047 at 8:21:00AM".
|
||||||
|
|
||||||
:param fuzzy_with_tokens:
|
:param fuzzy_with_tokens:
|
||||||
If `True`, `fuzzy` is automatically set to True, and the parser will
|
If ``True``, ``fuzzy`` is automatically set to True, and the parser
|
||||||
return a tuple where the first element is the parsed
|
will return a tuple where the first element is the parsed
|
||||||
`datetime.datetime` datetimestamp and the second element is a tuple
|
:class:`datetime.datetime` datetimestamp and the second element is
|
||||||
containing the portions of the string which were ignored, e.g.
|
a tuple containing the portions of the string which were ignored:
|
||||||
"Today is January 1, 2047 at 8:21:00AM" should return
|
|
||||||
`(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))`
|
.. doctest::
|
||||||
|
|
||||||
|
>>> from dateutil.parser import parse
|
||||||
|
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
|
||||||
|
(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`datetime.datetime` object or, if the
|
||||||
|
``fuzzy_with_tokens`` option is ``True``, returns a tuple, the
|
||||||
|
first element being a :class:`datetime.datetime` object, the second
|
||||||
|
a tuple containing the fuzzy tokens.
|
||||||
|
|
||||||
|
:raises ValueError:
|
||||||
|
Raised for invalid or unknown string format, if the provided
|
||||||
|
:class:`tzinfo` is not in a valid format, or if an invalid date
|
||||||
|
would be created.
|
||||||
|
|
||||||
|
:raises OverFlowError:
|
||||||
|
Raised if the parsed date exceeds the largest valid C integer on
|
||||||
|
your system.
|
||||||
"""
|
"""
|
||||||
if parserinfo:
|
if parserinfo:
|
||||||
return parser(parserinfo).parse(timestr, **kwargs)
|
return parser(parserinfo).parse(timestr, **kwargs)
|
||||||
|
|
|
@ -423,6 +423,7 @@ Here is the behavior of operations with relativedelta:
|
||||||
self.hours == other.hours and
|
self.hours == other.hours and
|
||||||
self.minutes == other.minutes and
|
self.minutes == other.minutes and
|
||||||
self.seconds == other.seconds and
|
self.seconds == other.seconds and
|
||||||
|
self.microseconds == other.microseconds and
|
||||||
self.leapdays == other.leapdays and
|
self.leapdays == other.leapdays and
|
||||||
self.year == other.year and
|
self.year == other.year and
|
||||||
self.month == other.month and
|
self.month == other.month and
|
||||||
|
|
|
@ -104,12 +104,12 @@ class tzoffset(datetime.tzinfo):
|
||||||
|
|
||||||
|
|
||||||
class tzlocal(datetime.tzinfo):
|
class tzlocal(datetime.tzinfo):
|
||||||
|
def __init__(self):
|
||||||
_std_offset = datetime.timedelta(seconds=-time.timezone)
|
self._std_offset = datetime.timedelta(seconds=-time.timezone)
|
||||||
if time.daylight:
|
if time.daylight:
|
||||||
_dst_offset = datetime.timedelta(seconds=-time.altzone)
|
self._dst_offset = datetime.timedelta(seconds=-time.altzone)
|
||||||
else:
|
else:
|
||||||
_dst_offset = _std_offset
|
self._dst_offset = self._std_offset
|
||||||
|
|
||||||
def utcoffset(self, dt):
|
def utcoffset(self, dt):
|
||||||
if self._isdst(dt):
|
if self._isdst(dt):
|
||||||
|
|
|
@ -4,6 +4,8 @@ import struct
|
||||||
|
|
||||||
from six.moves import winreg
|
from six.moves import winreg
|
||||||
|
|
||||||
|
from .tz import tzname_in_python2
|
||||||
|
|
||||||
__all__ = ["tzwin", "tzwinlocal"]
|
__all__ = ["tzwin", "tzwinlocal"]
|
||||||
|
|
||||||
ONEWEEK = datetime.timedelta(7)
|
ONEWEEK = datetime.timedelta(7)
|
||||||
|
@ -42,6 +44,7 @@ class tzwinbase(datetime.tzinfo):
|
||||||
else:
|
else:
|
||||||
return datetime.timedelta(0)
|
return datetime.timedelta(0)
|
||||||
|
|
||||||
|
@tzname_in_python2
|
||||||
def tzname(self, dt):
|
def tzname(self, dt):
|
||||||
if self._isdst(dt):
|
if self._isdst(dt):
|
||||||
return self._dstname
|
return self._dstname
|
||||||
|
@ -89,8 +92,8 @@ class tzwin(tzwinbase):
|
||||||
"%s\%s" % (TZKEYNAME, name)) as tzkey:
|
"%s\%s" % (TZKEYNAME, name)) as tzkey:
|
||||||
keydict = valuestodict(tzkey)
|
keydict = valuestodict(tzkey)
|
||||||
|
|
||||||
self._stdname = keydict["Std"].encode("iso-8859-1")
|
self._stdname = keydict["Std"]
|
||||||
self._dstname = keydict["Dlt"].encode("iso-8859-1")
|
self._dstname = keydict["Dlt"]
|
||||||
|
|
||||||
self._display = keydict["Display"]
|
self._display = keydict["Display"]
|
||||||
|
|
||||||
|
@ -129,8 +132,8 @@ class tzwinlocal(tzwinbase):
|
||||||
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||||
keydict = valuestodict(tzlocalkey)
|
keydict = valuestodict(tzlocalkey)
|
||||||
|
|
||||||
self._stdname = keydict["StandardName"].encode("iso-8859-1")
|
self._stdname = keydict["StandardName"]
|
||||||
self._dstname = keydict["DaylightName"].encode("iso-8859-1")
|
self._dstname = keydict["DaylightName"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with winreg.OpenKey(
|
with winreg.OpenKey(
|
||||||
|
|
|
@ -16,14 +16,14 @@ from dateutil.tz import tzfile
|
||||||
|
|
||||||
__all__ = ["gettz", "gettz_db_metadata", "rebuild"]
|
__all__ = ["gettz", "gettz_db_metadata", "rebuild"]
|
||||||
|
|
||||||
_ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||||
_METADATA_FN = 'METADATA'
|
METADATA_FN = 'METADATA'
|
||||||
|
|
||||||
# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but
|
# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but
|
||||||
# it's close enough for python2.6
|
# it's close enough for python2.6
|
||||||
_tar_open = TarFile.open
|
tar_open = TarFile.open
|
||||||
if not hasattr(TarFile, '__exit__'):
|
if not hasattr(TarFile, '__exit__'):
|
||||||
def _tar_open(*args, **kwargs):
|
def tar_open(*args, **kwargs):
|
||||||
return closing(TarFile.open(*args, **kwargs))
|
return closing(TarFile.open(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class tzfile(tzfile):
|
||||||
|
|
||||||
def getzoneinfofile_stream():
|
def getzoneinfofile_stream():
|
||||||
try:
|
try:
|
||||||
return BytesIO(get_data(__name__, _ZONEFILENAME))
|
return BytesIO(get_data(__name__, ZONEFILENAME))
|
||||||
except IOError as e: # TODO switch to FileNotFoundError?
|
except IOError as e: # TODO switch to FileNotFoundError?
|
||||||
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||||
return None
|
return None
|
||||||
|
@ -43,7 +43,7 @@ def getzoneinfofile_stream():
|
||||||
class ZoneInfoFile(object):
|
class ZoneInfoFile(object):
|
||||||
def __init__(self, zonefile_stream=None):
|
def __init__(self, zonefile_stream=None):
|
||||||
if zonefile_stream is not None:
|
if zonefile_stream is not None:
|
||||||
with _tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
with tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
||||||
# dict comprehension does not work on python2.6
|
# dict comprehension does not work on python2.6
|
||||||
# TODO: get back to the nicer syntax when we ditch python2.6
|
# TODO: get back to the nicer syntax when we ditch python2.6
|
||||||
# self.zones = {zf.name: tzfile(tf.extractfile(zf),
|
# self.zones = {zf.name: tzfile(tf.extractfile(zf),
|
||||||
|
@ -52,7 +52,7 @@ class ZoneInfoFile(object):
|
||||||
self.zones = dict((zf.name, tzfile(tf.extractfile(zf),
|
self.zones = dict((zf.name, tzfile(tf.extractfile(zf),
|
||||||
filename=zf.name))
|
filename=zf.name))
|
||||||
for zf in tf.getmembers()
|
for zf in tf.getmembers()
|
||||||
if zf.isfile() and zf.name != _METADATA_FN)
|
if zf.isfile() and zf.name != METADATA_FN)
|
||||||
# deal with links: They'll point to their parent object. Less
|
# deal with links: They'll point to their parent object. Less
|
||||||
# waste of memory
|
# waste of memory
|
||||||
# links = {zl.name: self.zones[zl.linkname]
|
# links = {zl.name: self.zones[zl.linkname]
|
||||||
|
@ -62,7 +62,7 @@ class ZoneInfoFile(object):
|
||||||
zl.islnk() or zl.issym())
|
zl.islnk() or zl.issym())
|
||||||
self.zones.update(links)
|
self.zones.update(links)
|
||||||
try:
|
try:
|
||||||
metadata_json = tf.extractfile(tf.getmember(_METADATA_FN))
|
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
||||||
metadata_str = metadata_json.read().decode('UTF-8')
|
metadata_str = metadata_json.read().decode('UTF-8')
|
||||||
self.metadata = json.loads(metadata_str)
|
self.metadata = json.loads(metadata_str)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -100,36 +100,3 @@ def gettz_db_metadata():
|
||||||
return _CLASS_ZONE_INSTANCE[0].metadata
|
return _CLASS_ZONE_INSTANCE[0].metadata
|
||||||
|
|
||||||
|
|
||||||
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
|
||||||
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
|
||||||
|
|
||||||
filename is the timezone tarball from ftp.iana.org/tz.
|
|
||||||
|
|
||||||
"""
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
|
||||||
zonedir = os.path.join(tmpdir, "zoneinfo")
|
|
||||||
moduledir = os.path.dirname(__file__)
|
|
||||||
try:
|
|
||||||
with _tar_open(filename) as tf:
|
|
||||||
for name in zonegroups:
|
|
||||||
tf.extract(name, tmpdir)
|
|
||||||
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
|
||||||
try:
|
|
||||||
check_call(["zic", "-d", zonedir] + filepaths)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == 2:
|
|
||||||
logging.error(
|
|
||||||
"Could not find zic. Perhaps you need to install "
|
|
||||||
"libc-bin or some other package that provides it, "
|
|
||||||
"or it's not in your PATH?")
|
|
||||||
raise
|
|
||||||
# write metadata file
|
|
||||||
with open(os.path.join(zonedir, _METADATA_FN), 'w') as f:
|
|
||||||
json.dump(metadata, f, indent=4, sort_keys=True)
|
|
||||||
target = os.path.join(moduledir, _ZONEFILENAME)
|
|
||||||
with _tar_open(target, "w:%s" % format) as tf:
|
|
||||||
for entry in os.listdir(zonedir):
|
|
||||||
entrypath = os.path.join(zonedir, entry)
|
|
||||||
tf.add(entrypath, entry)
|
|
||||||
finally:
|
|
||||||
shutil.rmtree(tmpdir)
|
|
||||||
|
|
43
lib/dateutil/zoneinfo/rebuild.py
Normal file
43
lib/dateutil/zoneinfo/rebuild.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
import json
|
||||||
|
from subprocess import check_call
|
||||||
|
|
||||||
|
from dateutil.zoneinfo import tar_open, METADATA_FN, ZONEFILENAME
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
||||||
|
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
||||||
|
|
||||||
|
filename is the timezone tarball from ftp.iana.org/tz.
|
||||||
|
|
||||||
|
"""
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
zonedir = os.path.join(tmpdir, "zoneinfo")
|
||||||
|
moduledir = os.path.dirname(__file__)
|
||||||
|
try:
|
||||||
|
with tar_open(filename) as tf:
|
||||||
|
for name in zonegroups:
|
||||||
|
tf.extract(name, tmpdir)
|
||||||
|
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
||||||
|
try:
|
||||||
|
check_call(["zic", "-d", zonedir] + filepaths)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == 2:
|
||||||
|
logging.error(
|
||||||
|
"Could not find zic. Perhaps you need to install "
|
||||||
|
"libc-bin or some other package that provides it, "
|
||||||
|
"or it's not in your PATH?")
|
||||||
|
raise
|
||||||
|
# write metadata file
|
||||||
|
with open(os.path.join(zonedir, METADATA_FN), 'w') as f:
|
||||||
|
json.dump(metadata, f, indent=4, sort_keys=True)
|
||||||
|
target = os.path.join(moduledir, ZONEFILENAME)
|
||||||
|
with tar_open(target, "w:%s" % format) as tf:
|
||||||
|
for entry in os.listdir(zonedir):
|
||||||
|
entrypath = os.path.join(zonedir, entry)
|
||||||
|
tf.add(entrypath, entry)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tmpdir)
|
|
@ -48,7 +48,7 @@ def _remove_zoneinfo_failed(filename):
|
||||||
|
|
||||||
# helper to remove old unneeded zoneinfo files
|
# helper to remove old unneeded zoneinfo files
|
||||||
def _remove_old_zoneinfo():
|
def _remove_old_zoneinfo():
|
||||||
zonefilename = zoneinfo._ZONEFILENAME
|
zonefilename = zoneinfo.ZONEFILENAME
|
||||||
if None is zonefilename:
|
if None is zonefilename:
|
||||||
return
|
return
|
||||||
cur_zoneinfo = ek.ek(basename, zonefilename)
|
cur_zoneinfo = ek.ek(basename, zonefilename)
|
||||||
|
@ -83,7 +83,7 @@ def _update_zoneinfo():
|
||||||
logger.WARNING)
|
logger.WARNING)
|
||||||
return
|
return
|
||||||
|
|
||||||
zonefilename = zoneinfo._ZONEFILENAME
|
zonefilename = zoneinfo.ZONEFILENAME
|
||||||
cur_zoneinfo = zonefilename
|
cur_zoneinfo = zonefilename
|
||||||
if None is not cur_zoneinfo:
|
if None is not cur_zoneinfo:
|
||||||
cur_zoneinfo = ek.ek(basename, zonefilename)
|
cur_zoneinfo = ek.ek(basename, zonefilename)
|
||||||
|
@ -222,7 +222,7 @@ def get_network_timezone(network, network_dict):
|
||||||
return sb_timezone
|
return sb_timezone
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if zoneinfo._ZONEFILENAME is not None:
|
if zoneinfo.ZONEFILENAME is not None:
|
||||||
try:
|
try:
|
||||||
n_t = tz.gettz(network_dict[network])
|
n_t = tz.gettz(network_dict[network])
|
||||||
except:
|
except:
|
||||||
|
|
Loading…
Reference in a new issue