Make bundle portable by bundling third party packages we need
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SCRIPT_PATH = os.path.realpath(__file__)
|
||||||
|
SCRIPT_DIR = os.path.dirname(SCRIPT_PATH)
|
||||||
|
sys.path.append(os.path.join(SCRIPT_DIR, "third_party"))
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
import foundation
|
import foundation
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
|
import multiprocessing
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from third_party import cpuinfo
|
# Usually comes from third_party
|
||||||
|
import cpuinfo
|
||||||
|
import cpu_cores
|
||||||
|
|
||||||
|
|
||||||
def _getBlenderDeviceInfo(ctx):
|
def _getBlenderDeviceInfo(ctx):
|
||||||
@@ -55,28 +58,7 @@ def getBlenderVersion(ctx):
|
|||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def getNumPhysicalCPUs_Linux():
|
|
||||||
ids = set()
|
|
||||||
with open("/proc/cpuinfo") as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
if line.startswith("physical id"):
|
|
||||||
id = int(line.split(":", 1)[1])
|
|
||||||
ids.add(id)
|
|
||||||
return len(ids)
|
|
||||||
|
|
||||||
|
|
||||||
def getNumPhysicalCPUs():
|
|
||||||
if sys.platform == 'linux':
|
|
||||||
return getNumPhysicalCPUs_Linux()
|
|
||||||
elif sys.platform == 'win32':
|
|
||||||
# TODO(sergey): Currently all WIndows machines here are single socket.
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
raise Exception("Needs implementation")
|
|
||||||
|
|
||||||
|
|
||||||
def gatherSystemInfo(ctx):
|
def gatherSystemInfo(ctx):
|
||||||
import psutil
|
|
||||||
system_info = {}
|
system_info = {}
|
||||||
system_info['bitness'] = platform.architecture()[0]
|
system_info['bitness'] = platform.architecture()[0]
|
||||||
system_info['machine'] = platform.machine()
|
system_info['machine'] = platform.machine()
|
||||||
@@ -88,10 +70,11 @@ def gatherSystemInfo(ctx):
|
|||||||
# system_info['libc_version'] = "-".join(platform.libc_ver())
|
# system_info['libc_version'] = "-".join(platform.libc_ver())
|
||||||
# TODO(sergey): Make this to work on Windows and macOS
|
# TODO(sergey): Make this to work on Windows and macOS
|
||||||
cpu_info = cpuinfo.get_cpu_info()
|
cpu_info = cpuinfo.get_cpu_info()
|
||||||
|
cores_info = cpu_cores.CPUCoresCounter.factory()
|
||||||
system_info['cpu_brand'] = cpu_info['brand']
|
system_info['cpu_brand'] = cpu_info['brand']
|
||||||
system_info["num_cpu_cores"] = psutil.cpu_count(logical=False)
|
system_info["num_cpu_cores"] = cores_info.get_physical_cores_count()
|
||||||
system_info["num_cpu_threads"] = psutil.cpu_count(logical=True)
|
system_info["num_cpu_threads"] = multiprocessing.cpu_count()
|
||||||
system_info["num_cpu_sockets"] = getNumPhysicalCPUs()
|
system_info["num_cpu_sockets"] = cores_info.get_physical_processors_count()
|
||||||
system_info['devices'] = _getBlenderDeviceInfo(ctx)
|
system_info['devices'] = _getBlenderDeviceInfo(ctx)
|
||||||
# TODO(sergey): query number of CPUs and threads.
|
# TODO(sergey): query number of CPUs and threads.
|
||||||
return system_info
|
return system_info
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import foundation
|
import foundation
|
||||||
from foundation import progress
|
from foundation import progress
|
||||||
|
import dateutil
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import tarfile
|
import tarfile
|
||||||
@@ -133,7 +134,6 @@ def blenderCommitUnixTimestamp(commit_date, commit_time):
|
|||||||
"""
|
"""
|
||||||
Convert commit build time and time to unix timestamp
|
Convert commit build time and time to unix timestamp
|
||||||
"""
|
"""
|
||||||
import dateutil
|
|
||||||
date_time = commit_date + " " + commit_time
|
date_time = commit_date + " " + commit_time
|
||||||
return dateutil.parser.parse(date_time)
|
return dateutil.parser.parse(date_time)
|
||||||
|
|
||||||
|
@@ -1,5 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SCRIPT_PATH = os.path.realpath(__file__)
|
||||||
|
SCRIPT_DIR = os.path.dirname(SCRIPT_PATH)
|
||||||
|
sys.path.append(os.path.join(SCRIPT_DIR, "third_party"))
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import foundation
|
import foundation
|
||||||
from foundation import (benchrunner,
|
from foundation import (benchrunner,
|
||||||
@@ -79,21 +86,21 @@ def _printFullResult(ctx, results):
|
|||||||
for scene in ctx.scenes:
|
for scene in ctx.scenes:
|
||||||
if scene not in stats:
|
if scene not in stats:
|
||||||
continue
|
continue
|
||||||
stats = stats[scene]
|
scene_stats = stats[scene]
|
||||||
print(" {}:" . format(scene))
|
print(" {}:" . format(scene))
|
||||||
print(" - Engine render time: {}" . format(
|
print(" - Engine render time: {}" . format(
|
||||||
util.humanReadableTimeDifference(
|
util.humanReadableTimeDifference(
|
||||||
stats.total_render_time)))
|
scene_stats.total_render_time)))
|
||||||
print(" - Render time without sync: {}" . format(
|
print(" - Render time without sync: {}" . format(
|
||||||
util.humanReadableTimeDifference(
|
util.humanReadableTimeDifference(
|
||||||
stats.render_time_no_sync)))
|
scene_stats.render_time_no_sync)))
|
||||||
print(" - Total render time: {}" . format(
|
print(" - Total render time: {}" . format(
|
||||||
util.humanReadableTimeDifference(
|
util.humanReadableTimeDifference(
|
||||||
stats.pipeline_render_time)))
|
scene_stats.pipeline_render_time)))
|
||||||
print(" - Peak memory used on device: {}" . format(
|
print(" - Peak memory used on device: {}" . format(
|
||||||
util.humanReadableSize(stats.device_peak_memory)))
|
util.humanReadableSize(scene_stats.device_peak_memory)))
|
||||||
print(" - Memory used on device during rendering: {}" . format(
|
print(" - Memory used on device during rendering: {}" . format(
|
||||||
util.humanReadableSize(stats.device_memory_usage)))
|
util.humanReadableSize(scene_stats.device_memory_usage)))
|
||||||
|
|
||||||
|
|
||||||
def _printFullJSONResult(ctx, results):
|
def _printFullJSONResult(ctx, results):
|
||||||
|
@@ -288,8 +288,9 @@ class HistoryResultVisitor(ResultVisitor):
|
|||||||
continue
|
continue
|
||||||
scene_stats_history = stats_history[scene_name]
|
scene_stats_history = stats_history[scene_name]
|
||||||
data = []
|
data = []
|
||||||
sorted_scene_stats_history = sorted(scene_stats_history,
|
sorted_scene_stats_history = sorted(
|
||||||
key=lambda k: k['timestamp'])
|
scene_stats_history,
|
||||||
|
key=lambda k: k['timestamp'])
|
||||||
uniq_scene_stats_history = self.removeDuplicated(
|
uniq_scene_stats_history = self.removeDuplicated(
|
||||||
sorted_scene_stats_history)
|
sorted_scene_stats_history)
|
||||||
for scene_stats in uniq_scene_stats_history:
|
for scene_stats in uniq_scene_stats_history:
|
||||||
@@ -313,6 +314,7 @@ class HistoryResultVisitor(ResultVisitor):
|
|||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
f.write(code)
|
f.write(code)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = configureArgumentParser()
|
parser = configureArgumentParser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
9
benchmark/third_party/cpu_cores/__init__.py
vendored
Normal file
9
benchmark/third_party/cpu_cores/__init__.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# This file is part of cpu_cores released under the MIT license.
|
||||||
|
# See the LICENSE file for more information.
|
||||||
|
|
||||||
|
version_info = (0, 1, 3)
|
||||||
|
__version__ = ".".join([str(x) for x in version_info])
|
||||||
|
|
||||||
|
from cpu_cores.common import CPUCoresCounter
|
||||||
|
|
||||||
|
__all__ = ['CPUCoresCounter']
|
43
benchmark/third_party/cpu_cores/common.py
vendored
Normal file
43
benchmark/third_party/cpu_cores/common.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# This file is part of cpu_cores released under the MIT license.
|
||||||
|
# See the LICENSE file for more information.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class CPUCoresCounter(object):
|
||||||
|
|
||||||
|
platform = None
|
||||||
|
_physical_cores_count = None
|
||||||
|
_physical_processors_count = None
|
||||||
|
|
||||||
|
def _count(self, *args, **kwargs):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def factory(cls, force_platform=None):
|
||||||
|
if force_platform is not None:
|
||||||
|
cls.platform = force_platform
|
||||||
|
else:
|
||||||
|
cls.platform = sys.platform
|
||||||
|
if cls.platform.startswith('darwin'):
|
||||||
|
from cpu_cores.darwin import DarwinCPUCoresCounter
|
||||||
|
return DarwinCPUCoresCounter()
|
||||||
|
elif cls.platform.startswith('linux'):
|
||||||
|
from cpu_cores.linux import LinuxCPUCoresCounter
|
||||||
|
return LinuxCPUCoresCounter()
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("unsupported platform type [%s]" %
|
||||||
|
cls.platform)
|
||||||
|
|
||||||
|
def _check_counting_or_do_it(self):
|
||||||
|
if self._physical_processors_count is None or \
|
||||||
|
self._physical_cores_count is None:
|
||||||
|
self._count()
|
||||||
|
|
||||||
|
def get_physical_cores_count(self):
|
||||||
|
self._check_counting_or_do_it()
|
||||||
|
return self._physical_cores_count
|
||||||
|
|
||||||
|
def get_physical_processors_count(self):
|
||||||
|
self._check_counting_or_do_it()
|
||||||
|
return self._physical_processors_count
|
35
benchmark/third_party/cpu_cores/darwin.py
vendored
Normal file
35
benchmark/third_party/cpu_cores/darwin.py
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# This file is part of cpu_cores released under the MIT license.
|
||||||
|
# See the LICENSE file for more information.
|
||||||
|
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from cpu_cores.common import CPUCoresCounter
|
||||||
|
|
||||||
|
CPUINFO_COMMAND = "/usr/sbin/system_profiler" \
|
||||||
|
" -detailLevel full SPHardwareDataType"
|
||||||
|
|
||||||
|
|
||||||
|
class DarwinCPUCoresCounter(CPUCoresCounter):
|
||||||
|
|
||||||
|
def _count(self, command=None):
|
||||||
|
if command is None:
|
||||||
|
command = CPUINFO_COMMAND
|
||||||
|
s = subprocess.Popen(shlex.split(command),
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
if s:
|
||||||
|
out, err = s.communicate()
|
||||||
|
if len(err.strip()) > 0 or len(out.strip()) == 0:
|
||||||
|
raise Exception('impossible to get the cpu cores count' +
|
||||||
|
'(darwin) (error message = %s)' % err.strip())
|
||||||
|
lines = out.split(b'\n')
|
||||||
|
for line in lines:
|
||||||
|
tmp = line.strip()
|
||||||
|
if tmp.startswith(b'Total Number of Cores:'):
|
||||||
|
self._physical_cores_count = int(tmp.split(b':')[1])
|
||||||
|
if tmp.startswith(b'Number of Processors:'):
|
||||||
|
self._physical_processors_count = int(tmp.split(b':')[1])
|
||||||
|
if self._physical_processors_count is None or \
|
||||||
|
self._physical_cores_count is None:
|
||||||
|
raise Exception('impossible to get the cpu cores count (darwin)')
|
54
benchmark/third_party/cpu_cores/linux.py
vendored
Normal file
54
benchmark/third_party/cpu_cores/linux.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# This file is part of cpu_cores released under the MIT license.
|
||||||
|
# See the LICENSE file for more information.
|
||||||
|
|
||||||
|
from cpu_cores.common import CPUCoresCounter
|
||||||
|
|
||||||
|
CPUINFO_FILEPATH = "/proc/cpuinfo"
|
||||||
|
|
||||||
|
|
||||||
|
def _core_hash(cpu_infos):
|
||||||
|
if 'core id' not in cpu_infos and 'physical id' not in cpu_infos:
|
||||||
|
return "%i" % cpu_infos['processor']
|
||||||
|
if 'core id' in cpu_infos and 'physical id' not in cpu_infos:
|
||||||
|
raise Exception("incorrect cpuinfo file :"
|
||||||
|
" we have a core_id without physical_id")
|
||||||
|
if 'core id' in cpu_infos:
|
||||||
|
return "%i_%i" % (cpu_infos['physical id'], cpu_infos['core id'])
|
||||||
|
else:
|
||||||
|
return "%i" % cpu_infos['physical id']
|
||||||
|
|
||||||
|
|
||||||
|
def _processor_hash(cpu_infos):
|
||||||
|
if 'core id' not in cpu_infos and 'physical id' not in cpu_infos:
|
||||||
|
return "%i" % cpu_infos['processor']
|
||||||
|
if 'core id' in cpu_infos and 'physical id' not in cpu_infos:
|
||||||
|
raise Exception("incorrect cpuinfo file :"
|
||||||
|
" we have a core_id without physical_id")
|
||||||
|
return "%i" % cpu_infos['physical id']
|
||||||
|
|
||||||
|
|
||||||
|
class LinuxCPUCoresCounter(CPUCoresCounter):
|
||||||
|
|
||||||
|
def _count(self, cpuinfo_filepath=None):
|
||||||
|
if cpuinfo_filepath is None:
|
||||||
|
cpuinfo_filepath = CPUINFO_FILEPATH
|
||||||
|
with open(cpuinfo_filepath, 'r') as f:
|
||||||
|
# we read lines in reversed order to be sure to end with a
|
||||||
|
# "processor:" line
|
||||||
|
lines = reversed(f.readlines())
|
||||||
|
cores = set()
|
||||||
|
processors = set()
|
||||||
|
cpu_infos = {}
|
||||||
|
for line in lines:
|
||||||
|
tmp = line.strip()
|
||||||
|
for key in ('processor', 'physical id', 'core id'):
|
||||||
|
if tmp.startswith(key):
|
||||||
|
cpu_infos[key] = int(tmp.split(':')[1].strip())
|
||||||
|
if key == 'processor':
|
||||||
|
cores.add(_core_hash(cpu_infos))
|
||||||
|
processors.add(_processor_hash(cpu_infos))
|
||||||
|
cpu_infos = {}
|
||||||
|
if len(cores) == 0 or len(processors) == 0:
|
||||||
|
raise Exception("can't get the cpu cores count (linux)")
|
||||||
|
self._physical_cores_count = len(cores)
|
||||||
|
self._physical_processors_count = len(processors)
|
2
benchmark/third_party/dateutil/__init__.py
vendored
Normal file
2
benchmark/third_party/dateutil/__init__.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from ._version import VERSION as __version__
|
34
benchmark/third_party/dateutil/_common.py
vendored
Normal file
34
benchmark/third_party/dateutil/_common.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
Common code used in multiple modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class weekday(object):
|
||||||
|
__slots__ = ["weekday", "n"]
|
||||||
|
|
||||||
|
def __init__(self, weekday, n=None):
|
||||||
|
self.weekday = weekday
|
||||||
|
self.n = n
|
||||||
|
|
||||||
|
def __call__(self, n):
|
||||||
|
if n == self.n:
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return self.__class__(self.weekday, n)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
try:
|
||||||
|
if self.weekday != other.weekday or self.n != other.n:
|
||||||
|
return False
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
__hash__ = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
|
||||||
|
if not self.n:
|
||||||
|
return s
|
||||||
|
else:
|
||||||
|
return "%s(%+d)" % (s, self.n)
|
10
benchmark/third_party/dateutil/_version.py
vendored
Normal file
10
benchmark/third_party/dateutil/_version.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
Contains information about the dateutil version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
VERSION_MAJOR = 2
|
||||||
|
VERSION_MINOR = 6
|
||||||
|
VERSION_PATCH = 1
|
||||||
|
|
||||||
|
VERSION_TUPLE = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
|
||||||
|
VERSION = '.'.join(map(str, VERSION_TUPLE))
|
89
benchmark/third_party/dateutil/easter.py
vendored
Normal file
89
benchmark/third_party/dateutil/easter.py
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
This module offers a generic easter computing method for any given year, using
|
||||||
|
Western, Orthodox or Julian algorithms.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
||||||
|
|
||||||
|
EASTER_JULIAN = 1
|
||||||
|
EASTER_ORTHODOX = 2
|
||||||
|
EASTER_WESTERN = 3
|
||||||
|
|
||||||
|
|
||||||
|
def easter(year, method=EASTER_WESTERN):
|
||||||
|
"""
|
||||||
|
This method was ported from the work done by GM Arts,
|
||||||
|
on top of the algorithm by Claus Tondering, which was
|
||||||
|
based in part on the algorithm of Ouding (1940), as
|
||||||
|
quoted in "Explanatory Supplement to the Astronomical
|
||||||
|
Almanac", P. Kenneth Seidelmann, editor.
|
||||||
|
|
||||||
|
This algorithm implements three different easter
|
||||||
|
calculation methods:
|
||||||
|
|
||||||
|
1 - Original calculation in Julian calendar, valid in
|
||||||
|
dates after 326 AD
|
||||||
|
2 - Original method, with date converted to Gregorian
|
||||||
|
calendar, valid in years 1583 to 4099
|
||||||
|
3 - Revised method, in Gregorian calendar, valid in
|
||||||
|
years 1583 to 4099 as well
|
||||||
|
|
||||||
|
These methods are represented by the constants:
|
||||||
|
|
||||||
|
* ``EASTER_JULIAN = 1``
|
||||||
|
* ``EASTER_ORTHODOX = 2``
|
||||||
|
* ``EASTER_WESTERN = 3``
|
||||||
|
|
||||||
|
The default method is method 3.
|
||||||
|
|
||||||
|
More about the algorithm may be found at:
|
||||||
|
|
||||||
|
http://users.chariot.net.au/~gmarts/eastalg.htm
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
http://www.tondering.dk/claus/calendar.html
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not (1 <= method <= 3):
|
||||||
|
raise ValueError("invalid method")
|
||||||
|
|
||||||
|
# g - Golden year - 1
|
||||||
|
# c - Century
|
||||||
|
# h - (23 - Epact) mod 30
|
||||||
|
# i - Number of days from March 21 to Paschal Full Moon
|
||||||
|
# j - Weekday for PFM (0=Sunday, etc)
|
||||||
|
# p - Number of days from March 21 to Sunday on or before PFM
|
||||||
|
# (-6 to 28 methods 1 & 3, to 56 for method 2)
|
||||||
|
# e - Extra days to add for method 2 (converting Julian
|
||||||
|
# date to Gregorian date)
|
||||||
|
|
||||||
|
y = year
|
||||||
|
g = y % 19
|
||||||
|
e = 0
|
||||||
|
if method < 3:
|
||||||
|
# Old method
|
||||||
|
i = (19*g + 15) % 30
|
||||||
|
j = (y + y//4 + i) % 7
|
||||||
|
if method == 2:
|
||||||
|
# Extra dates to convert Julian to Gregorian date
|
||||||
|
e = 10
|
||||||
|
if y > 1600:
|
||||||
|
e = e + y//100 - 16 - (y//100 - 16)//4
|
||||||
|
else:
|
||||||
|
# New method
|
||||||
|
c = y//100
|
||||||
|
h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30
|
||||||
|
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
|
||||||
|
j = (y + y//4 + i + 2 - c + c//4) % 7
|
||||||
|
|
||||||
|
# p can be from -6 to 56 corresponding to dates 22 March to 23 May
|
||||||
|
# (later dates apply to method 2, although 23 May never actually occurs)
|
||||||
|
p = i - j + e
|
||||||
|
d = 1 + (p + 27 + (p + 6)//40) % 31
|
||||||
|
m = 3 + (p + 26)//30
|
||||||
|
return datetime.date(int(y), int(m), int(d))
|
1374
benchmark/third_party/dateutil/parser.py
vendored
Normal file
1374
benchmark/third_party/dateutil/parser.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
549
benchmark/third_party/dateutil/relativedelta.py
vendored
Normal file
549
benchmark/third_party/dateutil/relativedelta.py
vendored
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
import calendar
|
||||||
|
|
||||||
|
import operator
|
||||||
|
from math import copysign
|
||||||
|
|
||||||
|
from six import integer_types
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
|
from ._common import weekday
|
||||||
|
|
||||||
|
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))
|
||||||
|
|
||||||
|
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
||||||
|
|
||||||
|
|
||||||
|
class relativedelta(object):
|
||||||
|
"""
|
||||||
|
The relativedelta type is based on the specification of the excellent
|
||||||
|
work done by M.-A. Lemburg in his
|
||||||
|
`mx.DateTime <http://www.egenix.com/files/python/mxDateTime.html>`_ extension.
|
||||||
|
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.
|
||||||
|
|
||||||
|
There are two different ways to build a relativedelta instance. The
|
||||||
|
first one is passing it two date/datetime classes::
|
||||||
|
|
||||||
|
relativedelta(datetime1, datetime2)
|
||||||
|
|
||||||
|
The second one is passing it any number of the following keyword arguments::
|
||||||
|
|
||||||
|
relativedelta(arg1=x,arg2=y,arg3=z...)
|
||||||
|
|
||||||
|
year, month, day, hour, minute, second, microsecond:
|
||||||
|
Absolute information (argument is singular); adding or subtracting a
|
||||||
|
relativedelta with absolute information does not perform an aritmetic
|
||||||
|
operation, but rather REPLACES the corresponding value in the
|
||||||
|
original datetime with the value(s) in relativedelta.
|
||||||
|
|
||||||
|
years, months, weeks, days, hours, minutes, seconds, microseconds:
|
||||||
|
Relative information, may be negative (argument is plural); adding
|
||||||
|
or subtracting a relativedelta with relative information performs
|
||||||
|
the corresponding aritmetic operation on the original datetime value
|
||||||
|
with the information in the relativedelta.
|
||||||
|
|
||||||
|
weekday:
|
||||||
|
One of the weekday instances (MO, TU, etc). These instances may
|
||||||
|
receive a parameter N, specifying the Nth weekday, which could
|
||||||
|
be positive or negative (like MO(+1) or MO(-2). Not specifying
|
||||||
|
it is the same as specifying +1. You can also use an integer,
|
||||||
|
where 0=MO.
|
||||||
|
|
||||||
|
leapdays:
|
||||||
|
Will add given days to the date found, if year is a leap
|
||||||
|
year, and the date found is post 28 of february.
|
||||||
|
|
||||||
|
yearday, nlyearday:
|
||||||
|
Set the yearday or the non-leap year day (jump leap days).
|
||||||
|
These are converted to day/month/leapdays information.
|
||||||
|
|
||||||
|
Here is the behavior of operations with relativedelta:
|
||||||
|
|
||||||
|
1. Calculate the absolute year, using the 'year' argument, or the
|
||||||
|
original datetime year, if the argument is not present.
|
||||||
|
|
||||||
|
2. Add the relative 'years' argument to the absolute year.
|
||||||
|
|
||||||
|
3. Do steps 1 and 2 for month/months.
|
||||||
|
|
||||||
|
4. Calculate the absolute day, using the 'day' argument, or the
|
||||||
|
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
|
||||||
|
that the 'weeks' argument is multiplied by 7 and added to
|
||||||
|
'days'.
|
||||||
|
|
||||||
|
6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds,
|
||||||
|
microsecond/microseconds.
|
||||||
|
|
||||||
|
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,
|
||||||
|
years=0, months=0, days=0, leapdays=0, weeks=0,
|
||||||
|
hours=0, minutes=0, seconds=0, microseconds=0,
|
||||||
|
year=None, month=None, day=None, weekday=None,
|
||||||
|
yearday=None, nlyearday=None,
|
||||||
|
hour=None, minute=None, second=None, microsecond=None):
|
||||||
|
|
||||||
|
# Check for non-integer values in integer-only quantities
|
||||||
|
if any(x is not None and x != int(x) for x in (years, months)):
|
||||||
|
raise ValueError("Non-integer years and months are "
|
||||||
|
"ambiguous and not currently supported.")
|
||||||
|
|
||||||
|
if dt1 and dt2:
|
||||||
|
# datetime is a subclass of date. So both must be date
|
||||||
|
if not (isinstance(dt1, datetime.date) and
|
||||||
|
isinstance(dt2, datetime.date)):
|
||||||
|
raise TypeError("relativedelta only diffs datetime/date")
|
||||||
|
|
||||||
|
# We allow two dates, or two datetimes, so we coerce them to be
|
||||||
|
# of the same type
|
||||||
|
if (isinstance(dt1, datetime.datetime) !=
|
||||||
|
isinstance(dt2, datetime.datetime)):
|
||||||
|
if not isinstance(dt1, datetime.datetime):
|
||||||
|
dt1 = datetime.datetime.fromordinal(dt1.toordinal())
|
||||||
|
elif not isinstance(dt2, datetime.datetime):
|
||||||
|
dt2 = datetime.datetime.fromordinal(dt2.toordinal())
|
||||||
|
|
||||||
|
self.years = 0
|
||||||
|
self.months = 0
|
||||||
|
self.days = 0
|
||||||
|
self.leapdays = 0
|
||||||
|
self.hours = 0
|
||||||
|
self.minutes = 0
|
||||||
|
self.seconds = 0
|
||||||
|
self.microseconds = 0
|
||||||
|
self.year = None
|
||||||
|
self.month = None
|
||||||
|
self.day = None
|
||||||
|
self.weekday = None
|
||||||
|
self.hour = None
|
||||||
|
self.minute = None
|
||||||
|
self.second = None
|
||||||
|
self.microsecond = None
|
||||||
|
self._has_time = 0
|
||||||
|
|
||||||
|
# Get year / month delta between the two
|
||||||
|
months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month)
|
||||||
|
self._set_months(months)
|
||||||
|
|
||||||
|
# Remove the year/month delta so the timedelta is just well-defined
|
||||||
|
# time units (seconds, days and microseconds)
|
||||||
|
dtm = self.__radd__(dt2)
|
||||||
|
|
||||||
|
# If we've overshot our target, make an adjustment
|
||||||
|
if dt1 < dt2:
|
||||||
|
compare = operator.gt
|
||||||
|
increment = 1
|
||||||
|
else:
|
||||||
|
compare = operator.lt
|
||||||
|
increment = -1
|
||||||
|
|
||||||
|
while compare(dt1, dtm):
|
||||||
|
months += increment
|
||||||
|
self._set_months(months)
|
||||||
|
dtm = self.__radd__(dt2)
|
||||||
|
|
||||||
|
# Get the timedelta between the "months-adjusted" date and dt1
|
||||||
|
delta = dt1 - dtm
|
||||||
|
self.seconds = delta.seconds + delta.days * 86400
|
||||||
|
self.microseconds = delta.microseconds
|
||||||
|
else:
|
||||||
|
# Relative information
|
||||||
|
self.years = years
|
||||||
|
self.months = months
|
||||||
|
self.days = days + weeks * 7
|
||||||
|
self.leapdays = leapdays
|
||||||
|
self.hours = hours
|
||||||
|
self.minutes = minutes
|
||||||
|
self.seconds = seconds
|
||||||
|
self.microseconds = microseconds
|
||||||
|
|
||||||
|
# Absolute information
|
||||||
|
self.year = year
|
||||||
|
self.month = month
|
||||||
|
self.day = day
|
||||||
|
self.hour = hour
|
||||||
|
self.minute = minute
|
||||||
|
self.second = second
|
||||||
|
self.microsecond = microsecond
|
||||||
|
|
||||||
|
if any(x is not None and int(x) != x
|
||||||
|
for x in (year, month, day, hour,
|
||||||
|
minute, second, microsecond)):
|
||||||
|
# For now we'll deprecate floats - later it'll be an error.
|
||||||
|
warn("Non-integer value passed as absolute information. " +
|
||||||
|
"This is not a well-defined condition and will raise " +
|
||||||
|
"errors in future versions.", DeprecationWarning)
|
||||||
|
|
||||||
|
if isinstance(weekday, integer_types):
|
||||||
|
self.weekday = weekdays[weekday]
|
||||||
|
else:
|
||||||
|
self.weekday = weekday
|
||||||
|
|
||||||
|
yday = 0
|
||||||
|
if nlyearday:
|
||||||
|
yday = nlyearday
|
||||||
|
elif yearday:
|
||||||
|
yday = yearday
|
||||||
|
if yearday > 59:
|
||||||
|
self.leapdays = -1
|
||||||
|
if yday:
|
||||||
|
ydayidx = [31, 59, 90, 120, 151, 181, 212,
|
||||||
|
243, 273, 304, 334, 366]
|
||||||
|
for idx, ydays in enumerate(ydayidx):
|
||||||
|
if yday <= ydays:
|
||||||
|
self.month = idx+1
|
||||||
|
if idx == 0:
|
||||||
|
self.day = yday
|
||||||
|
else:
|
||||||
|
self.day = yday-ydayidx[idx-1]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("invalid year day (%d)" % yday)
|
||||||
|
|
||||||
|
self._fix()
|
||||||
|
|
||||||
|
def _fix(self):
|
||||||
|
if abs(self.microseconds) > 999999:
|
||||||
|
s = _sign(self.microseconds)
|
||||||
|
div, mod = divmod(self.microseconds * s, 1000000)
|
||||||
|
self.microseconds = mod * s
|
||||||
|
self.seconds += div * s
|
||||||
|
if abs(self.seconds) > 59:
|
||||||
|
s = _sign(self.seconds)
|
||||||
|
div, mod = divmod(self.seconds * s, 60)
|
||||||
|
self.seconds = mod * s
|
||||||
|
self.minutes += div * s
|
||||||
|
if abs(self.minutes) > 59:
|
||||||
|
s = _sign(self.minutes)
|
||||||
|
div, mod = divmod(self.minutes * s, 60)
|
||||||
|
self.minutes = mod * s
|
||||||
|
self.hours += div * s
|
||||||
|
if abs(self.hours) > 23:
|
||||||
|
s = _sign(self.hours)
|
||||||
|
div, mod = divmod(self.hours * s, 24)
|
||||||
|
self.hours = mod * s
|
||||||
|
self.days += div * s
|
||||||
|
if abs(self.months) > 11:
|
||||||
|
s = _sign(self.months)
|
||||||
|
div, mod = divmod(self.months * s, 12)
|
||||||
|
self.months = mod * s
|
||||||
|
self.years += div * s
|
||||||
|
if (self.hours or self.minutes or self.seconds or self.microseconds
|
||||||
|
or self.hour is not None or self.minute is not None or
|
||||||
|
self.second is not None or self.microsecond is not None):
|
||||||
|
self._has_time = 1
|
||||||
|
else:
|
||||||
|
self._has_time = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def weeks(self):
|
||||||
|
return self.days // 7
|
||||||
|
|
||||||
|
@weeks.setter
|
||||||
|
def weeks(self, value):
|
||||||
|
self.days = self.days - (self.weeks * 7) + value * 7
|
||||||
|
|
||||||
|
def _set_months(self, months):
|
||||||
|
self.months = months
|
||||||
|
if abs(self.months) > 11:
|
||||||
|
s = _sign(self.months)
|
||||||
|
div, mod = divmod(self.months * s, 12)
|
||||||
|
self.months = mod * s
|
||||||
|
self.years = div * s
|
||||||
|
else:
|
||||||
|
self.years = 0
|
||||||
|
|
||||||
|
def normalized(self):
|
||||||
|
"""
|
||||||
|
Return a version of this object represented entirely using integer
|
||||||
|
values for the relative attributes.
|
||||||
|
|
||||||
|
>>> relativedelta(days=1.5, hours=2).normalized()
|
||||||
|
relativedelta(days=1, hours=14)
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`dateutil.relativedelta.relativedelta` object.
|
||||||
|
"""
|
||||||
|
# Cascade remainders down (rounding each to roughly nearest microsecond)
|
||||||
|
days = int(self.days)
|
||||||
|
|
||||||
|
hours_f = round(self.hours + 24 * (self.days - days), 11)
|
||||||
|
hours = int(hours_f)
|
||||||
|
|
||||||
|
minutes_f = round(self.minutes + 60 * (hours_f - hours), 10)
|
||||||
|
minutes = int(minutes_f)
|
||||||
|
|
||||||
|
seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8)
|
||||||
|
seconds = int(seconds_f)
|
||||||
|
|
||||||
|
microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds))
|
||||||
|
|
||||||
|
# Constructor carries overflow back up with call to _fix()
|
||||||
|
return self.__class__(years=self.years, months=self.months,
|
||||||
|
days=days, hours=hours, minutes=minutes,
|
||||||
|
seconds=seconds, microseconds=microseconds,
|
||||||
|
leapdays=self.leapdays, year=self.year,
|
||||||
|
month=self.month, day=self.day,
|
||||||
|
weekday=self.weekday, hour=self.hour,
|
||||||
|
minute=self.minute, second=self.second,
|
||||||
|
microsecond=self.microsecond)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if isinstance(other, relativedelta):
|
||||||
|
return self.__class__(years=other.years + self.years,
|
||||||
|
months=other.months + self.months,
|
||||||
|
days=other.days + self.days,
|
||||||
|
hours=other.hours + self.hours,
|
||||||
|
minutes=other.minutes + self.minutes,
|
||||||
|
seconds=other.seconds + self.seconds,
|
||||||
|
microseconds=(other.microseconds +
|
||||||
|
self.microseconds),
|
||||||
|
leapdays=other.leapdays or self.leapdays,
|
||||||
|
year=(other.year if other.year is not None
|
||||||
|
else self.year),
|
||||||
|
month=(other.month if other.month is not None
|
||||||
|
else self.month),
|
||||||
|
day=(other.day if other.day is not None
|
||||||
|
else self.day),
|
||||||
|
weekday=(other.weekday if other.weekday is not None
|
||||||
|
else self.weekday),
|
||||||
|
hour=(other.hour if other.hour is not None
|
||||||
|
else self.hour),
|
||||||
|
minute=(other.minute if other.minute is not None
|
||||||
|
else self.minute),
|
||||||
|
second=(other.second if other.second is not None
|
||||||
|
else self.second),
|
||||||
|
microsecond=(other.microsecond if other.microsecond
|
||||||
|
is not None else
|
||||||
|
self.microsecond))
|
||||||
|
if isinstance(other, datetime.timedelta):
|
||||||
|
return self.__class__(years=self.years,
|
||||||
|
months=self.months,
|
||||||
|
days=self.days + other.days,
|
||||||
|
hours=self.hours,
|
||||||
|
minutes=self.minutes,
|
||||||
|
seconds=self.seconds + other.seconds,
|
||||||
|
microseconds=self.microseconds + other.microseconds,
|
||||||
|
leapdays=self.leapdays,
|
||||||
|
year=self.year,
|
||||||
|
month=self.month,
|
||||||
|
day=self.day,
|
||||||
|
weekday=self.weekday,
|
||||||
|
hour=self.hour,
|
||||||
|
minute=self.minute,
|
||||||
|
second=self.second,
|
||||||
|
microsecond=self.microsecond)
|
||||||
|
if not isinstance(other, datetime.date):
|
||||||
|
return NotImplemented
|
||||||
|
elif self._has_time and not isinstance(other, datetime.datetime):
|
||||||
|
other = datetime.datetime.fromordinal(other.toordinal())
|
||||||
|
year = (self.year or other.year)+self.years
|
||||||
|
month = self.month or other.month
|
||||||
|
if self.months:
|
||||||
|
assert 1 <= abs(self.months) <= 12
|
||||||
|
month += self.months
|
||||||
|
if month > 12:
|
||||||
|
year += 1
|
||||||
|
month -= 12
|
||||||
|
elif month < 1:
|
||||||
|
year -= 1
|
||||||
|
month += 12
|
||||||
|
day = min(calendar.monthrange(year, month)[1],
|
||||||
|
self.day or other.day)
|
||||||
|
repl = {"year": year, "month": month, "day": day}
|
||||||
|
for attr in ["hour", "minute", "second", "microsecond"]:
|
||||||
|
value = getattr(self, attr)
|
||||||
|
if value is not None:
|
||||||
|
repl[attr] = value
|
||||||
|
days = self.days
|
||||||
|
if self.leapdays and month > 2 and calendar.isleap(year):
|
||||||
|
days += self.leapdays
|
||||||
|
ret = (other.replace(**repl)
|
||||||
|
+ datetime.timedelta(days=days,
|
||||||
|
hours=self.hours,
|
||||||
|
minutes=self.minutes,
|
||||||
|
seconds=self.seconds,
|
||||||
|
microseconds=self.microseconds))
|
||||||
|
if self.weekday:
|
||||||
|
weekday, nth = self.weekday.weekday, self.weekday.n or 1
|
||||||
|
jumpdays = (abs(nth) - 1) * 7
|
||||||
|
if nth > 0:
|
||||||
|
jumpdays += (7 - ret.weekday() + weekday) % 7
|
||||||
|
else:
|
||||||
|
jumpdays += (ret.weekday() - weekday) % 7
|
||||||
|
jumpdays *= -1
|
||||||
|
ret += datetime.timedelta(days=jumpdays)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return self.__add__(other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
return self.__neg__().__radd__(other)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
if not isinstance(other, relativedelta):
|
||||||
|
return NotImplemented # In case the other object defines __rsub__
|
||||||
|
return self.__class__(years=self.years - other.years,
|
||||||
|
months=self.months - other.months,
|
||||||
|
days=self.days - other.days,
|
||||||
|
hours=self.hours - other.hours,
|
||||||
|
minutes=self.minutes - other.minutes,
|
||||||
|
seconds=self.seconds - other.seconds,
|
||||||
|
microseconds=self.microseconds - other.microseconds,
|
||||||
|
leapdays=self.leapdays or other.leapdays,
|
||||||
|
year=(self.year if self.year is not None
|
||||||
|
else other.year),
|
||||||
|
month=(self.month if self.month is not None else
|
||||||
|
other.month),
|
||||||
|
day=(self.day if self.day is not None else
|
||||||
|
other.day),
|
||||||
|
weekday=(self.weekday if self.weekday is not None else
|
||||||
|
other.weekday),
|
||||||
|
hour=(self.hour if self.hour is not None else
|
||||||
|
other.hour),
|
||||||
|
minute=(self.minute if self.minute is not None else
|
||||||
|
other.minute),
|
||||||
|
second=(self.second if self.second is not None else
|
||||||
|
other.second),
|
||||||
|
microsecond=(self.microsecond if self.microsecond
|
||||||
|
is not None else
|
||||||
|
other.microsecond))
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return self.__class__(years=-self.years,
|
||||||
|
months=-self.months,
|
||||||
|
days=-self.days,
|
||||||
|
hours=-self.hours,
|
||||||
|
minutes=-self.minutes,
|
||||||
|
seconds=-self.seconds,
|
||||||
|
microseconds=-self.microseconds,
|
||||||
|
leapdays=self.leapdays,
|
||||||
|
year=self.year,
|
||||||
|
month=self.month,
|
||||||
|
day=self.day,
|
||||||
|
weekday=self.weekday,
|
||||||
|
hour=self.hour,
|
||||||
|
minute=self.minute,
|
||||||
|
second=self.second,
|
||||||
|
microsecond=self.microsecond)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return not (not self.years and
|
||||||
|
not self.months and
|
||||||
|
not self.days and
|
||||||
|
not self.hours and
|
||||||
|
not self.minutes and
|
||||||
|
not self.seconds and
|
||||||
|
not self.microseconds and
|
||||||
|
not self.leapdays and
|
||||||
|
self.year is None and
|
||||||
|
self.month is None and
|
||||||
|
self.day is None and
|
||||||
|
self.weekday is None and
|
||||||
|
self.hour is None and
|
||||||
|
self.minute is None and
|
||||||
|
self.second is None and
|
||||||
|
self.microsecond is None)
|
||||||
|
# Compatibility with Python 2.x
|
||||||
|
__nonzero__ = __bool__
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
try:
|
||||||
|
f = float(other)
|
||||||
|
except TypeError:
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
return self.__class__(years=int(self.years * f),
|
||||||
|
months=int(self.months * f),
|
||||||
|
days=int(self.days * f),
|
||||||
|
hours=int(self.hours * f),
|
||||||
|
minutes=int(self.minutes * f),
|
||||||
|
seconds=int(self.seconds * f),
|
||||||
|
microseconds=int(self.microseconds * f),
|
||||||
|
leapdays=self.leapdays,
|
||||||
|
year=self.year,
|
||||||
|
month=self.month,
|
||||||
|
day=self.day,
|
||||||
|
weekday=self.weekday,
|
||||||
|
hour=self.hour,
|
||||||
|
minute=self.minute,
|
||||||
|
second=self.second,
|
||||||
|
microsecond=self.microsecond)
|
||||||
|
|
||||||
|
__rmul__ = __mul__
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, relativedelta):
|
||||||
|
return NotImplemented
|
||||||
|
if self.weekday or other.weekday:
|
||||||
|
if not self.weekday or not other.weekday:
|
||||||
|
return False
|
||||||
|
if self.weekday.weekday != other.weekday.weekday:
|
||||||
|
return False
|
||||||
|
n1, n2 = self.weekday.n, other.weekday.n
|
||||||
|
if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)):
|
||||||
|
return False
|
||||||
|
return (self.years == other.years and
|
||||||
|
self.months == other.months and
|
||||||
|
self.days == other.days and
|
||||||
|
self.hours == other.hours and
|
||||||
|
self.minutes == other.minutes and
|
||||||
|
self.seconds == other.seconds and
|
||||||
|
self.microseconds == other.microseconds and
|
||||||
|
self.leapdays == other.leapdays and
|
||||||
|
self.year == other.year and
|
||||||
|
self.month == other.month and
|
||||||
|
self.day == other.day and
|
||||||
|
self.hour == other.hour and
|
||||||
|
self.minute == other.minute and
|
||||||
|
self.second == other.second and
|
||||||
|
self.microsecond == other.microsecond)
|
||||||
|
|
||||||
|
__hash__ = None
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __div__(self, other):
|
||||||
|
try:
|
||||||
|
reciprocal = 1 / float(other)
|
||||||
|
except TypeError:
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
return self.__mul__(reciprocal)
|
||||||
|
|
||||||
|
__truediv__ = __div__
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
l = []
|
||||||
|
for attr in ["years", "months", "days", "leapdays",
|
||||||
|
"hours", "minutes", "seconds", "microseconds"]:
|
||||||
|
value = getattr(self, attr)
|
||||||
|
if value:
|
||||||
|
l.append("{attr}={value:+g}".format(attr=attr, value=value))
|
||||||
|
for attr in ["year", "month", "day", "weekday",
|
||||||
|
"hour", "minute", "second", "microsecond"]:
|
||||||
|
value = getattr(self, attr)
|
||||||
|
if value is not None:
|
||||||
|
l.append("{attr}={value}".format(attr=attr, value=repr(value)))
|
||||||
|
return "{classname}({attrs})".format(classname=self.__class__.__name__,
|
||||||
|
attrs=", ".join(l))
|
||||||
|
|
||||||
|
|
||||||
|
def _sign(x):
|
||||||
|
return int(copysign(1, x))
|
||||||
|
|
||||||
|
# vim:ts=4:sw=4:et
|
1610
benchmark/third_party/dateutil/rrule.py
vendored
Normal file
1610
benchmark/third_party/dateutil/rrule.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
0
benchmark/third_party/dateutil/test/__init__.py
vendored
Normal file
0
benchmark/third_party/dateutil/test/__init__.py
vendored
Normal file
292
benchmark/third_party/dateutil/test/_common.py
vendored
Normal file
292
benchmark/third_party/dateutil/test/_common.py
vendored
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
import warnings
|
||||||
|
import tempfile
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
|
||||||
|
class WarningTestMixin(object):
|
||||||
|
# Based on https://stackoverflow.com/a/12935176/467366
|
||||||
|
class _AssertWarnsContext(warnings.catch_warnings):
|
||||||
|
def __init__(self, expected_warnings, parent, **kwargs):
|
||||||
|
super(WarningTestMixin._AssertWarnsContext, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
self.parent = parent
|
||||||
|
try:
|
||||||
|
self.expected_warnings = list(expected_warnings)
|
||||||
|
except TypeError:
|
||||||
|
self.expected_warnings = [expected_warnings]
|
||||||
|
|
||||||
|
self._warning_log = []
|
||||||
|
|
||||||
|
def __enter__(self, *args, **kwargs):
|
||||||
|
rv = super(WarningTestMixin._AssertWarnsContext, self).__enter__(*args, **kwargs)
|
||||||
|
|
||||||
|
if self._showwarning is not self._module.showwarning:
|
||||||
|
super_showwarning = self._module.showwarning
|
||||||
|
else:
|
||||||
|
super_showwarning = None
|
||||||
|
|
||||||
|
def showwarning(*args, **kwargs):
|
||||||
|
if super_showwarning is not None:
|
||||||
|
super_showwarning(*args, **kwargs)
|
||||||
|
|
||||||
|
self._warning_log.append(warnings.WarningMessage(*args, **kwargs))
|
||||||
|
|
||||||
|
self._module.showwarning = showwarning
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def __exit__(self, *args, **kwargs):
|
||||||
|
super(WarningTestMixin._AssertWarnsContext, self).__exit__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
self.parent.assertTrue(any(issubclass(item.category, warning)
|
||||||
|
for warning in self.expected_warnings
|
||||||
|
for item in self._warning_log))
|
||||||
|
|
||||||
|
def assertWarns(self, warning, callable=None, *args, **kwargs):
|
||||||
|
warnings.simplefilter('always')
|
||||||
|
context = self.__class__._AssertWarnsContext(warning, self)
|
||||||
|
if callable is None:
|
||||||
|
return context
|
||||||
|
else:
|
||||||
|
with context:
|
||||||
|
callable(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class PicklableMixin(object):
|
||||||
|
def _get_nobj_bytes(self, obj, dump_kwargs, load_kwargs):
|
||||||
|
"""
|
||||||
|
Pickle and unpickle an object using ``pickle.dumps`` / ``pickle.loads``
|
||||||
|
"""
|
||||||
|
pkl = pickle.dumps(obj, **dump_kwargs)
|
||||||
|
return pickle.loads(pkl, **load_kwargs)
|
||||||
|
|
||||||
|
def _get_nobj_file(self, obj, dump_kwargs, load_kwargs):
|
||||||
|
"""
|
||||||
|
Pickle and unpickle an object using ``pickle.dump`` / ``pickle.load`` on
|
||||||
|
a temporary file.
|
||||||
|
"""
|
||||||
|
with tempfile.TemporaryFile('w+b') as pkl:
|
||||||
|
pickle.dump(obj, pkl, **dump_kwargs)
|
||||||
|
pkl.seek(0) # Reset the file to the beginning to read it
|
||||||
|
nobj = pickle.load(pkl, **load_kwargs)
|
||||||
|
|
||||||
|
return nobj
|
||||||
|
|
||||||
|
def assertPicklable(self, obj, asfile=False,
|
||||||
|
dump_kwargs=None, load_kwargs=None):
|
||||||
|
"""
|
||||||
|
Assert that an object can be pickled and unpickled. This assertion
|
||||||
|
assumes that the desired behavior is that the unpickled object compares
|
||||||
|
equal to the original object, but is not the same object.
|
||||||
|
"""
|
||||||
|
get_nobj = self._get_nobj_file if asfile else self._get_nobj_bytes
|
||||||
|
dump_kwargs = dump_kwargs or {}
|
||||||
|
load_kwargs = load_kwargs or {}
|
||||||
|
|
||||||
|
nobj = get_nobj(obj, dump_kwargs, load_kwargs)
|
||||||
|
self.assertIsNot(obj, nobj)
|
||||||
|
self.assertEqual(obj, nobj)
|
||||||
|
|
||||||
|
|
||||||
|
class TZContextBase(object):
|
||||||
|
"""
|
||||||
|
Base class for a context manager which allows changing of time zones.
|
||||||
|
|
||||||
|
Subclasses may define a guard variable to either block or or allow time
|
||||||
|
zone changes by redefining ``_guard_var_name`` and ``_guard_allows_change``.
|
||||||
|
The default is that the guard variable must be affirmatively set.
|
||||||
|
|
||||||
|
Subclasses must define ``get_current_tz`` and ``set_current_tz``.
|
||||||
|
"""
|
||||||
|
_guard_var_name = "DATEUTIL_MAY_CHANGE_TZ"
|
||||||
|
_guard_allows_change = True
|
||||||
|
|
||||||
|
def __init__(self, tzval):
|
||||||
|
self.tzval = tzval
|
||||||
|
self._old_tz = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tz_change_allowed(cls):
|
||||||
|
"""
|
||||||
|
Class method used to query whether or not this class allows time zone
|
||||||
|
changes.
|
||||||
|
"""
|
||||||
|
guard = bool(os.environ.get(cls._guard_var_name, False))
|
||||||
|
|
||||||
|
# _guard_allows_change gives the "default" behavior - if True, the
|
||||||
|
# guard is overcoming a block. If false, the guard is causing a block.
|
||||||
|
# Whether tz_change is allowed is therefore the XNOR of the two.
|
||||||
|
return guard == cls._guard_allows_change
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tz_change_disallowed_message(cls):
|
||||||
|
""" Generate instructions on how to allow tz changes """
|
||||||
|
msg = ('Changing time zone not allowed. Set {envar} to {gval} '
|
||||||
|
'if you would like to allow this behavior')
|
||||||
|
|
||||||
|
return msg.format(envar=cls._guard_var_name,
|
||||||
|
gval=cls._guard_allows_change)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if not self.tz_change_allowed():
|
||||||
|
raise ValueError(self.tz_change_disallowed_message())
|
||||||
|
|
||||||
|
self._old_tz = self.get_current_tz()
|
||||||
|
self.set_current_tz(self.tzval)
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
if self._old_tz is not None:
|
||||||
|
self.set_current_tz(self._old_tz)
|
||||||
|
|
||||||
|
self._old_tz = None
|
||||||
|
|
||||||
|
def get_current_tz(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def set_current_tz(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class TZEnvContext(TZContextBase):
|
||||||
|
"""
|
||||||
|
Context manager that temporarily sets the `TZ` variable (for use on
|
||||||
|
*nix-like systems). Because the effect is local to the shell anyway, this
|
||||||
|
will apply *unless* a guard is set.
|
||||||
|
|
||||||
|
If you do not want the TZ environment variable set, you may set the
|
||||||
|
``DATEUTIL_MAY_NOT_CHANGE_TZ_VAR`` variable to a truthy value.
|
||||||
|
"""
|
||||||
|
_guard_var_name = "DATEUTIL_MAY_NOT_CHANGE_TZ_VAR"
|
||||||
|
_guard_allows_change = False
|
||||||
|
|
||||||
|
def get_current_tz(self):
|
||||||
|
return os.environ.get('TZ', UnsetTz)
|
||||||
|
|
||||||
|
def set_current_tz(self, tzval):
|
||||||
|
if tzval is UnsetTz and 'TZ' in os.environ:
|
||||||
|
del os.environ['TZ']
|
||||||
|
else:
|
||||||
|
os.environ['TZ'] = tzval
|
||||||
|
|
||||||
|
time.tzset()
|
||||||
|
|
||||||
|
|
||||||
|
class TZWinContext(TZContextBase):
|
||||||
|
"""
|
||||||
|
Context manager for changing local time zone on Windows.
|
||||||
|
|
||||||
|
Because the effect of this is system-wide and global, it may have
|
||||||
|
unintended side effect. Set the ``DATEUTIL_MAY_CHANGE_TZ`` environment
|
||||||
|
variable to a truthy value before using this context manager.
|
||||||
|
"""
|
||||||
|
def get_current_tz(self):
|
||||||
|
p = subprocess.Popen(['tzutil', '/g'], stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
ctzname, err = p.communicate()
|
||||||
|
ctzname = ctzname.decode() # Popen returns
|
||||||
|
|
||||||
|
if p.returncode:
|
||||||
|
raise OSError('Failed to get current time zone: ' + err)
|
||||||
|
|
||||||
|
return ctzname
|
||||||
|
|
||||||
|
def set_current_tz(self, tzname):
|
||||||
|
p = subprocess.Popen('tzutil /s "' + tzname + '"')
|
||||||
|
|
||||||
|
out, err = p.communicate()
|
||||||
|
|
||||||
|
if p.returncode:
|
||||||
|
raise OSError('Failed to set current time zone: ' +
|
||||||
|
(err or 'Unknown error.'))
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# Compatibility functions
|
||||||
|
|
||||||
|
def _total_seconds(td):
|
||||||
|
# Python 2.6 doesn't have a total_seconds() method on timedelta objects
|
||||||
|
return ((td.seconds + td.days * 86400) * 1000000 +
|
||||||
|
td.microseconds) // 1000000
|
||||||
|
|
||||||
|
|
||||||
|
total_seconds = getattr(datetime.timedelta, 'total_seconds', _total_seconds)
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# Utility classes
|
||||||
|
class NotAValueClass(object):
|
||||||
|
"""
|
||||||
|
A class analogous to NaN that has operations defined for any type.
|
||||||
|
"""
|
||||||
|
def _op(self, other):
|
||||||
|
return self # Operation with NotAValue returns NotAValue
|
||||||
|
|
||||||
|
def _cmp(self, other):
|
||||||
|
return False
|
||||||
|
|
||||||
|
__add__ = __radd__ = _op
|
||||||
|
__sub__ = __rsub__ = _op
|
||||||
|
__mul__ = __rmul__ = _op
|
||||||
|
__div__ = __rdiv__ = _op
|
||||||
|
__truediv__ = __rtruediv__ = _op
|
||||||
|
__floordiv__ = __rfloordiv__ = _op
|
||||||
|
|
||||||
|
__lt__ = __rlt__ = _op
|
||||||
|
__gt__ = __rgt__ = _op
|
||||||
|
__eq__ = __req__ = _op
|
||||||
|
__le__ = __rle__ = _op
|
||||||
|
__ge__ = __rge__ = _op
|
||||||
|
|
||||||
|
|
||||||
|
NotAValue = NotAValueClass()
|
||||||
|
|
||||||
|
|
||||||
|
class ComparesEqualClass(object):
|
||||||
|
"""
|
||||||
|
A class that is always equal to whatever you compare it to.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return False
|
||||||
|
|
||||||
|
__req__ = __eq__
|
||||||
|
__rne__ = __ne__
|
||||||
|
__rle__ = __le__
|
||||||
|
__rge__ = __ge__
|
||||||
|
__rlt__ = __lt__
|
||||||
|
__rgt__ = __gt__
|
||||||
|
|
||||||
|
|
||||||
|
ComparesEqual = ComparesEqualClass()
|
||||||
|
|
||||||
|
|
||||||
|
class UnsetTzClass(object):
|
||||||
|
""" Sentinel class for unset time zone variable """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
UnsetTz = UnsetTzClass()
|
99
benchmark/third_party/dateutil/test/test_easter.py
vendored
Normal file
99
benchmark/third_party/dateutil/test/test_easter.py
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
from dateutil.easter import easter
|
||||||
|
from dateutil.easter import EASTER_WESTERN, EASTER_ORTHODOX, EASTER_JULIAN
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
# List of easters between 1990 and 2050
|
||||||
|
western_easter_dates = [
|
||||||
|
date(1990, 4, 15), date(1991, 3, 31), date(1992, 4, 19), date(1993, 4, 11),
|
||||||
|
date(1994, 4, 3), date(1995, 4, 16), date(1996, 4, 7), date(1997, 3, 30),
|
||||||
|
date(1998, 4, 12), date(1999, 4, 4),
|
||||||
|
|
||||||
|
date(2000, 4, 23), date(2001, 4, 15), date(2002, 3, 31), date(2003, 4, 20),
|
||||||
|
date(2004, 4, 11), date(2005, 3, 27), date(2006, 4, 16), date(2007, 4, 8),
|
||||||
|
date(2008, 3, 23), date(2009, 4, 12),
|
||||||
|
|
||||||
|
date(2010, 4, 4), date(2011, 4, 24), date(2012, 4, 8), date(2013, 3, 31),
|
||||||
|
date(2014, 4, 20), date(2015, 4, 5), date(2016, 3, 27), date(2017, 4, 16),
|
||||||
|
date(2018, 4, 1), date(2019, 4, 21),
|
||||||
|
|
||||||
|
date(2020, 4, 12), date(2021, 4, 4), date(2022, 4, 17), date(2023, 4, 9),
|
||||||
|
date(2024, 3, 31), date(2025, 4, 20), date(2026, 4, 5), date(2027, 3, 28),
|
||||||
|
date(2028, 4, 16), date(2029, 4, 1),
|
||||||
|
|
||||||
|
date(2030, 4, 21), date(2031, 4, 13), date(2032, 3, 28), date(2033, 4, 17),
|
||||||
|
date(2034, 4, 9), date(2035, 3, 25), date(2036, 4, 13), date(2037, 4, 5),
|
||||||
|
date(2038, 4, 25), date(2039, 4, 10),
|
||||||
|
|
||||||
|
date(2040, 4, 1), date(2041, 4, 21), date(2042, 4, 6), date(2043, 3, 29),
|
||||||
|
date(2044, 4, 17), date(2045, 4, 9), date(2046, 3, 25), date(2047, 4, 14),
|
||||||
|
date(2048, 4, 5), date(2049, 4, 18), date(2050, 4, 10)
|
||||||
|
]
|
||||||
|
|
||||||
|
orthodox_easter_dates = [
|
||||||
|
date(1990, 4, 15), date(1991, 4, 7), date(1992, 4, 26), date(1993, 4, 18),
|
||||||
|
date(1994, 5, 1), date(1995, 4, 23), date(1996, 4, 14), date(1997, 4, 27),
|
||||||
|
date(1998, 4, 19), date(1999, 4, 11),
|
||||||
|
|
||||||
|
date(2000, 4, 30), date(2001, 4, 15), date(2002, 5, 5), date(2003, 4, 27),
|
||||||
|
date(2004, 4, 11), date(2005, 5, 1), date(2006, 4, 23), date(2007, 4, 8),
|
||||||
|
date(2008, 4, 27), date(2009, 4, 19),
|
||||||
|
|
||||||
|
date(2010, 4, 4), date(2011, 4, 24), date(2012, 4, 15), date(2013, 5, 5),
|
||||||
|
date(2014, 4, 20), date(2015, 4, 12), date(2016, 5, 1), date(2017, 4, 16),
|
||||||
|
date(2018, 4, 8), date(2019, 4, 28),
|
||||||
|
|
||||||
|
date(2020, 4, 19), date(2021, 5, 2), date(2022, 4, 24), date(2023, 4, 16),
|
||||||
|
date(2024, 5, 5), date(2025, 4, 20), date(2026, 4, 12), date(2027, 5, 2),
|
||||||
|
date(2028, 4, 16), date(2029, 4, 8),
|
||||||
|
|
||||||
|
date(2030, 4, 28), date(2031, 4, 13), date(2032, 5, 2), date(2033, 4, 24),
|
||||||
|
date(2034, 4, 9), date(2035, 4, 29), date(2036, 4, 20), date(2037, 4, 5),
|
||||||
|
date(2038, 4, 25), date(2039, 4, 17),
|
||||||
|
|
||||||
|
date(2040, 5, 6), date(2041, 4, 21), date(2042, 4, 13), date(2043, 5, 3),
|
||||||
|
date(2044, 4, 24), date(2045, 4, 9), date(2046, 4, 29), date(2047, 4, 21),
|
||||||
|
date(2048, 4, 5), date(2049, 4, 25), date(2050, 4, 17)
|
||||||
|
]
|
||||||
|
|
||||||
|
# A random smattering of Julian dates.
|
||||||
|
# Pulled values from http://www.kevinlaughery.com/east4099.html
|
||||||
|
julian_easter_dates = [
|
||||||
|
date( 326, 4, 3), date( 375, 4, 5), date( 492, 4, 5), date( 552, 3, 31),
|
||||||
|
date( 562, 4, 9), date( 569, 4, 21), date( 597, 4, 14), date( 621, 4, 19),
|
||||||
|
date( 636, 3, 31), date( 655, 3, 29), date( 700, 4, 11), date( 725, 4, 8),
|
||||||
|
date( 750, 3, 29), date( 782, 4, 7), date( 835, 4, 18), date( 849, 4, 14),
|
||||||
|
date( 867, 3, 30), date( 890, 4, 12), date( 922, 4, 21), date( 934, 4, 6),
|
||||||
|
date(1049, 3, 26), date(1058, 4, 19), date(1113, 4, 6), date(1119, 3, 30),
|
||||||
|
date(1242, 4, 20), date(1255, 3, 28), date(1257, 4, 8), date(1258, 3, 24),
|
||||||
|
date(1261, 4, 24), date(1278, 4, 17), date(1333, 4, 4), date(1351, 4, 17),
|
||||||
|
date(1371, 4, 6), date(1391, 3, 26), date(1402, 3, 26), date(1412, 4, 3),
|
||||||
|
date(1439, 4, 5), date(1445, 3, 28), date(1531, 4, 9), date(1555, 4, 14)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EasterTest(unittest.TestCase):
|
||||||
|
def testEasterWestern(self):
|
||||||
|
for easter_date in western_easter_dates:
|
||||||
|
self.assertEqual(easter_date,
|
||||||
|
easter(easter_date.year, EASTER_WESTERN))
|
||||||
|
|
||||||
|
def testEasterOrthodox(self):
|
||||||
|
for easter_date in orthodox_easter_dates:
|
||||||
|
self.assertEqual(easter_date,
|
||||||
|
easter(easter_date.year, EASTER_ORTHODOX))
|
||||||
|
|
||||||
|
def testEasterJulian(self):
|
||||||
|
for easter_date in julian_easter_dates:
|
||||||
|
self.assertEqual(easter_date,
|
||||||
|
easter(easter_date.year, EASTER_JULIAN))
|
||||||
|
|
||||||
|
def testEasterBadMethod(self):
|
||||||
|
# Invalid methods raise ValueError
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
easter(1975, 4)
|
166
benchmark/third_party/dateutil/test/test_imports.py
vendored
Normal file
166
benchmark/third_party/dateutil/test/test_imports.py
vendored
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class ImportVersionTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.__version__ can be imported"""
|
||||||
|
|
||||||
|
def testImportVersionStr(self):
|
||||||
|
from dateutil import __version__
|
||||||
|
|
||||||
|
def testImportRoot(self):
|
||||||
|
import dateutil
|
||||||
|
|
||||||
|
self.assertTrue(hasattr(dateutil, '__version__'))
|
||||||
|
|
||||||
|
|
||||||
|
class ImportEasterTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.easter-related imports work properly """
|
||||||
|
|
||||||
|
def testEasterDirect(self):
|
||||||
|
import dateutil.easter
|
||||||
|
|
||||||
|
def testEasterFrom(self):
|
||||||
|
from dateutil import easter
|
||||||
|
|
||||||
|
def testEasterStar(self):
|
||||||
|
from dateutil.easter import easter
|
||||||
|
|
||||||
|
|
||||||
|
class ImportParserTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.parser-related imports work properly """
|
||||||
|
def testParserDirect(self):
|
||||||
|
import dateutil.parser
|
||||||
|
|
||||||
|
def testParserFrom(self):
|
||||||
|
from dateutil import parser
|
||||||
|
|
||||||
|
def testParserAll(self):
|
||||||
|
# All interface
|
||||||
|
from dateutil.parser import parse
|
||||||
|
from dateutil.parser import parserinfo
|
||||||
|
|
||||||
|
# Other public classes
|
||||||
|
from dateutil.parser import parser
|
||||||
|
|
||||||
|
for var in (parse, parserinfo, parser):
|
||||||
|
self.assertIsNot(var, None)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportRelativeDeltaTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.relativedelta-related imports work properly """
|
||||||
|
def testRelativeDeltaDirect(self):
|
||||||
|
import dateutil.relativedelta
|
||||||
|
|
||||||
|
def testRelativeDeltaFrom(self):
|
||||||
|
from dateutil import relativedelta
|
||||||
|
|
||||||
|
def testRelativeDeltaAll(self):
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU
|
||||||
|
|
||||||
|
for var in (relativedelta, MO, TU, WE, TH, FR, SA, SU):
|
||||||
|
self.assertIsNot(var, None)
|
||||||
|
|
||||||
|
# In the public interface but not in all
|
||||||
|
from dateutil.relativedelta import weekday
|
||||||
|
self.assertIsNot(weekday, None)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportRRuleTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.rrule related imports work properly """
|
||||||
|
def testRRuleDirect(self):
|
||||||
|
import dateutil.rrule
|
||||||
|
|
||||||
|
def testRRuleFrom(self):
|
||||||
|
from dateutil import rrule
|
||||||
|
|
||||||
|
def testRRuleAll(self):
|
||||||
|
from dateutil.rrule import rrule
|
||||||
|
from dateutil.rrule import rruleset
|
||||||
|
from dateutil.rrule import rrulestr
|
||||||
|
from dateutil.rrule import YEARLY, MONTHLY, WEEKLY, DAILY
|
||||||
|
from dateutil.rrule import HOURLY, MINUTELY, SECONDLY
|
||||||
|
from dateutil.rrule import MO, TU, WE, TH, FR, SA, SU
|
||||||
|
|
||||||
|
rr_all = (rrule, rruleset, rrulestr,
|
||||||
|
YEARLY, MONTHLY, WEEKLY, DAILY,
|
||||||
|
HOURLY, MINUTELY, SECONDLY,
|
||||||
|
MO, TU, WE, TH, FR, SA, SU)
|
||||||
|
|
||||||
|
for var in rr_all:
|
||||||
|
self.assertIsNot(var, None)
|
||||||
|
|
||||||
|
# In the public interface but not in all
|
||||||
|
from dateutil.rrule import weekday
|
||||||
|
self.assertIsNot(weekday, None)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportTZTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.tz related imports work properly """
|
||||||
|
def testTzDirect(self):
|
||||||
|
import dateutil.tz
|
||||||
|
|
||||||
|
def testTzFrom(self):
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
|
def testTzAll(self):
|
||||||
|
from dateutil.tz import tzutc
|
||||||
|
from dateutil.tz import tzoffset
|
||||||
|
from dateutil.tz import tzlocal
|
||||||
|
from dateutil.tz import tzfile
|
||||||
|
from dateutil.tz import tzrange
|
||||||
|
from dateutil.tz import tzstr
|
||||||
|
from dateutil.tz import tzical
|
||||||
|
from dateutil.tz import gettz
|
||||||
|
from dateutil.tz import tzwin
|
||||||
|
from dateutil.tz import tzwinlocal
|
||||||
|
|
||||||
|
tz_all = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
||||||
|
"tzstr", "tzical", "gettz"]
|
||||||
|
|
||||||
|
tz_all += ["tzwin", "tzwinlocal"] if sys.platform.startswith("win") else []
|
||||||
|
lvars = locals()
|
||||||
|
|
||||||
|
for var in tz_all:
|
||||||
|
self.assertIsNot(lvars[var], None)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(sys.platform.startswith('win'), "Requires Windows")
|
||||||
|
class ImportTZWinTest(unittest.TestCase):
|
||||||
|
""" Test that dateutil.tzwin related imports work properly """
|
||||||
|
def testTzwinDirect(self):
|
||||||
|
import dateutil.tzwin
|
||||||
|
|
||||||
|
def testTzwinFrom(self):
|
||||||
|
from dateutil import tzwin
|
||||||
|
|
||||||
|
def testTzwinStar(self):
|
||||||
|
from dateutil.tzwin import tzwin
|
||||||
|
from dateutil.tzwin import tzwinlocal
|
||||||
|
|
||||||
|
tzwin_all = [tzwin, tzwinlocal]
|
||||||
|
|
||||||
|
for var in tzwin_all:
|
||||||
|
self.assertIsNot(var, None)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportZoneInfoTest(unittest.TestCase):
|
||||||
|
def testZoneinfoDirect(self):
|
||||||
|
import dateutil.zoneinfo
|
||||||
|
|
||||||
|
def testZoneinfoFrom(self):
|
||||||
|
from dateutil import zoneinfo
|
||||||
|
|
||||||
|
def testZoneinfoStar(self):
|
||||||
|
from dateutil.zoneinfo import gettz
|
||||||
|
from dateutil.zoneinfo import gettz_db_metadata
|
||||||
|
from dateutil.zoneinfo import rebuild
|
||||||
|
|
||||||
|
zi_all = (gettz, gettz_db_metadata, rebuild)
|
||||||
|
|
||||||
|
for var in zi_all:
|
||||||
|
self.assertIsNot(var, None)
|
897
benchmark/third_party/dateutil/test/test_parser.py
vendored
Normal file
897
benchmark/third_party/dateutil/test/test_parser.py
vendored
Normal file
@@ -0,0 +1,897 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from ._common import unittest
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from dateutil.tz import tzoffset
|
||||||
|
from dateutil.parser import parse, parserinfo
|
||||||
|
|
||||||
|
from six import assertRaisesRegex, PY3
|
||||||
|
from six.moves import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
class ParserTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.tzinfos = {"BRST": -10800}
|
||||||
|
self.brsttz = tzoffset("BRST", -10800)
|
||||||
|
self.default = datetime(2003, 9, 25)
|
||||||
|
|
||||||
|
# Parser should be able to handle bytestring and unicode
|
||||||
|
self.uni_str = '2014-05-01 08:00:00'
|
||||||
|
self.str_str = self.uni_str.encode()
|
||||||
|
|
||||||
|
def testEmptyString(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse('')
|
||||||
|
|
||||||
|
def testNone(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
parse(None)
|
||||||
|
|
||||||
|
def testInvalidType(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
parse(13)
|
||||||
|
|
||||||
|
def testDuckTyping(self):
|
||||||
|
# We want to support arbitrary classes that implement the stream
|
||||||
|
# interface.
|
||||||
|
|
||||||
|
class StringPassThrough(object):
|
||||||
|
def __init__(self, stream):
|
||||||
|
self.stream = stream
|
||||||
|
|
||||||
|
def read(self, *args, **kwargs):
|
||||||
|
return self.stream.read(*args, **kwargs)
|
||||||
|
|
||||||
|
dstr = StringPassThrough(StringIO('2014 January 19'))
|
||||||
|
|
||||||
|
self.assertEqual(parse(dstr), datetime(2014, 1, 19))
|
||||||
|
|
||||||
|
def testParseStream(self):
|
||||||
|
dstr = StringIO('2014 January 19')
|
||||||
|
|
||||||
|
self.assertEqual(parse(dstr), datetime(2014, 1, 19))
|
||||||
|
|
||||||
|
def testParseStr(self):
|
||||||
|
self.assertEqual(parse(self.str_str),
|
||||||
|
parse(self.uni_str))
|
||||||
|
|
||||||
|
def testParserParseStr(self):
|
||||||
|
from dateutil.parser import parser
|
||||||
|
|
||||||
|
self.assertEqual(parser().parse(self.str_str),
|
||||||
|
parser().parse(self.uni_str))
|
||||||
|
|
||||||
|
def testParseUnicodeWords(self):
|
||||||
|
|
||||||
|
class rus_parserinfo(parserinfo):
|
||||||
|
MONTHS = [("янв", "Январь"),
|
||||||
|
("фев", "Февраль"),
|
||||||
|
("мар", "Март"),
|
||||||
|
("апр", "Апрель"),
|
||||||
|
("май", "Май"),
|
||||||
|
("июн", "Июнь"),
|
||||||
|
("июл", "Июль"),
|
||||||
|
("авг", "Август"),
|
||||||
|
("сен", "Сентябрь"),
|
||||||
|
("окт", "Октябрь"),
|
||||||
|
("ноя", "Ноябрь"),
|
||||||
|
("дек", "Декабрь")]
|
||||||
|
|
||||||
|
self.assertEqual(parse('10 Сентябрь 2015 10:20',
|
||||||
|
parserinfo=rus_parserinfo()),
|
||||||
|
datetime(2015, 9, 10, 10, 20))
|
||||||
|
|
||||||
|
def testParseWithNulls(self):
|
||||||
|
# This relies on the from __future__ import unicode_literals, because
|
||||||
|
# explicitly specifying a unicode literal is a syntax error in Py 3.2
|
||||||
|
# May want to switch to u'...' if we ever drop Python 3.2 support.
|
||||||
|
pstring = '\x00\x00August 29, 1924'
|
||||||
|
|
||||||
|
self.assertEqual(parse(pstring),
|
||||||
|
datetime(1924, 8, 29))
|
||||||
|
|
||||||
|
def testDateCommandFormat(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
|
||||||
|
tzinfos=self.tzinfos),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testDateCommandFormatUnicode(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
|
||||||
|
tzinfos=self.tzinfos),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testDateCommandFormatReversed(self):
|
||||||
|
self.assertEqual(parse("2003 10:36:28 BRST 25 Sep Thu",
|
||||||
|
tzinfos=self.tzinfos),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testDateCommandFormatWithLong(self):
|
||||||
|
if not PY3:
|
||||||
|
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
|
||||||
|
tzinfos={"BRST": long(-10800)}),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testDateCommandFormatIgnoreTz(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip1(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 25 10:36:28 2003"),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip2(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 25 10:36:28", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip3(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 10:36:28", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip4(self):
|
||||||
|
self.assertEqual(parse("Thu 10:36:28", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip5(self):
|
||||||
|
self.assertEqual(parse("Sep 10:36:28", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip6(self):
|
||||||
|
self.assertEqual(parse("10:36:28", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip7(self):
|
||||||
|
self.assertEqual(parse("10:36", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip8(self):
|
||||||
|
self.assertEqual(parse("Thu Sep 25 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip9(self):
|
||||||
|
self.assertEqual(parse("Sep 25 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip10(self):
|
||||||
|
self.assertEqual(parse("Sep 2003", default=self.default),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip11(self):
|
||||||
|
self.assertEqual(parse("Sep", default=self.default),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateCommandFormatStrip12(self):
|
||||||
|
self.assertEqual(parse("2003", default=self.default),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateRCommandFormat(self):
|
||||||
|
self.assertEqual(parse("Thu, 25 Sep 2003 10:49:41 -0300"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testISOFormat(self):
|
||||||
|
self.assertEqual(parse("2003-09-25T10:49:41.5-03:00"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41, 500000,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testISOFormatStrip1(self):
|
||||||
|
self.assertEqual(parse("2003-09-25T10:49:41-03:00"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testISOFormatStrip2(self):
|
||||||
|
self.assertEqual(parse("2003-09-25T10:49:41"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41))
|
||||||
|
|
||||||
|
def testISOFormatStrip3(self):
|
||||||
|
self.assertEqual(parse("2003-09-25T10:49"),
|
||||||
|
datetime(2003, 9, 25, 10, 49))
|
||||||
|
|
||||||
|
def testISOFormatStrip4(self):
|
||||||
|
self.assertEqual(parse("2003-09-25T10"),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testISOFormatStrip5(self):
|
||||||
|
self.assertEqual(parse("2003-09-25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testISOStrippedFormat(self):
|
||||||
|
self.assertEqual(parse("20030925T104941.5-0300"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41, 500000,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testISOStrippedFormatStrip1(self):
|
||||||
|
self.assertEqual(parse("20030925T104941-0300"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testISOStrippedFormatStrip2(self):
|
||||||
|
self.assertEqual(parse("20030925T104941"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41))
|
||||||
|
|
||||||
|
def testISOStrippedFormatStrip3(self):
|
||||||
|
self.assertEqual(parse("20030925T1049"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 0))
|
||||||
|
|
||||||
|
def testISOStrippedFormatStrip4(self):
|
||||||
|
self.assertEqual(parse("20030925T10"),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testISOStrippedFormatStrip5(self):
|
||||||
|
self.assertEqual(parse("20030925"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testPythonLoggerFormat(self):
|
||||||
|
self.assertEqual(parse("2003-09-25 10:49:41,502"),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41, 502000))
|
||||||
|
|
||||||
|
def testNoSeparator1(self):
|
||||||
|
self.assertEqual(parse("199709020908"),
|
||||||
|
datetime(1997, 9, 2, 9, 8))
|
||||||
|
|
||||||
|
def testNoSeparator2(self):
|
||||||
|
self.assertEqual(parse("19970902090807"),
|
||||||
|
datetime(1997, 9, 2, 9, 8, 7))
|
||||||
|
|
||||||
|
def testDateWithDash1(self):
|
||||||
|
self.assertEqual(parse("2003-09-25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash2(self):
|
||||||
|
self.assertEqual(parse("2003-Sep-25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash3(self):
|
||||||
|
self.assertEqual(parse("25-Sep-2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash4(self):
|
||||||
|
self.assertEqual(parse("25-Sep-2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash5(self):
|
||||||
|
self.assertEqual(parse("Sep-25-2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash6(self):
|
||||||
|
self.assertEqual(parse("09-25-2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash7(self):
|
||||||
|
self.assertEqual(parse("25-09-2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDash8(self):
|
||||||
|
self.assertEqual(parse("10-09-2003", dayfirst=True),
|
||||||
|
datetime(2003, 9, 10))
|
||||||
|
|
||||||
|
def testDateWithDash9(self):
|
||||||
|
self.assertEqual(parse("10-09-2003"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithDash10(self):
|
||||||
|
self.assertEqual(parse("10-09-03"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithDash11(self):
|
||||||
|
self.assertEqual(parse("10-09-03", yearfirst=True),
|
||||||
|
datetime(2010, 9, 3))
|
||||||
|
|
||||||
|
def testDateWithDot1(self):
|
||||||
|
self.assertEqual(parse("2003.09.25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot2(self):
|
||||||
|
self.assertEqual(parse("2003.Sep.25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot3(self):
|
||||||
|
self.assertEqual(parse("25.Sep.2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot4(self):
|
||||||
|
self.assertEqual(parse("25.Sep.2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot5(self):
|
||||||
|
self.assertEqual(parse("Sep.25.2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot6(self):
|
||||||
|
self.assertEqual(parse("09.25.2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot7(self):
|
||||||
|
self.assertEqual(parse("25.09.2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithDot8(self):
|
||||||
|
self.assertEqual(parse("10.09.2003", dayfirst=True),
|
||||||
|
datetime(2003, 9, 10))
|
||||||
|
|
||||||
|
def testDateWithDot9(self):
|
||||||
|
self.assertEqual(parse("10.09.2003"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithDot10(self):
|
||||||
|
self.assertEqual(parse("10.09.03"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithDot11(self):
|
||||||
|
self.assertEqual(parse("10.09.03", yearfirst=True),
|
||||||
|
datetime(2010, 9, 3))
|
||||||
|
|
||||||
|
def testDateWithSlash1(self):
|
||||||
|
self.assertEqual(parse("2003/09/25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash2(self):
|
||||||
|
self.assertEqual(parse("2003/Sep/25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash3(self):
|
||||||
|
self.assertEqual(parse("25/Sep/2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash4(self):
|
||||||
|
self.assertEqual(parse("25/Sep/2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash5(self):
|
||||||
|
self.assertEqual(parse("Sep/25/2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash6(self):
|
||||||
|
self.assertEqual(parse("09/25/2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash7(self):
|
||||||
|
self.assertEqual(parse("25/09/2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSlash8(self):
|
||||||
|
self.assertEqual(parse("10/09/2003", dayfirst=True),
|
||||||
|
datetime(2003, 9, 10))
|
||||||
|
|
||||||
|
def testDateWithSlash9(self):
|
||||||
|
self.assertEqual(parse("10/09/2003"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithSlash10(self):
|
||||||
|
self.assertEqual(parse("10/09/03"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithSlash11(self):
|
||||||
|
self.assertEqual(parse("10/09/03", yearfirst=True),
|
||||||
|
datetime(2010, 9, 3))
|
||||||
|
|
||||||
|
def testDateWithSpace1(self):
|
||||||
|
self.assertEqual(parse("2003 09 25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace2(self):
|
||||||
|
self.assertEqual(parse("2003 Sep 25"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace3(self):
|
||||||
|
self.assertEqual(parse("25 Sep 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace4(self):
|
||||||
|
self.assertEqual(parse("25 Sep 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace5(self):
|
||||||
|
self.assertEqual(parse("Sep 25 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace6(self):
|
||||||
|
self.assertEqual(parse("09 25 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace7(self):
|
||||||
|
self.assertEqual(parse("25 09 2003"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testDateWithSpace8(self):
|
||||||
|
self.assertEqual(parse("10 09 2003", dayfirst=True),
|
||||||
|
datetime(2003, 9, 10))
|
||||||
|
|
||||||
|
def testDateWithSpace9(self):
|
||||||
|
self.assertEqual(parse("10 09 2003"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithSpace10(self):
|
||||||
|
self.assertEqual(parse("10 09 03"),
|
||||||
|
datetime(2003, 10, 9))
|
||||||
|
|
||||||
|
def testDateWithSpace11(self):
|
||||||
|
self.assertEqual(parse("10 09 03", yearfirst=True),
|
||||||
|
datetime(2010, 9, 3))
|
||||||
|
|
||||||
|
def testDateWithSpace12(self):
|
||||||
|
self.assertEqual(parse("25 09 03"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testStrangelyOrderedDate1(self):
|
||||||
|
self.assertEqual(parse("03 25 Sep"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testStrangelyOrderedDate2(self):
|
||||||
|
self.assertEqual(parse("2003 25 Sep"),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testStrangelyOrderedDate3(self):
|
||||||
|
self.assertEqual(parse("25 03 Sep"),
|
||||||
|
datetime(2025, 9, 3))
|
||||||
|
|
||||||
|
def testHourWithLetters(self):
|
||||||
|
self.assertEqual(parse("10h36m28.5s", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28, 500000))
|
||||||
|
|
||||||
|
def testHourWithLettersStrip1(self):
|
||||||
|
self.assertEqual(parse("10h36m28s", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 28))
|
||||||
|
|
||||||
|
def testHourWithLettersStrip2(self):
|
||||||
|
self.assertEqual(parse("10h36m", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36))
|
||||||
|
|
||||||
|
def testHourWithLettersStrip3(self):
|
||||||
|
self.assertEqual(parse("10h", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourWithLettersStrip4(self):
|
||||||
|
self.assertEqual(parse("10 h 36", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36))
|
||||||
|
|
||||||
|
def testHourWithLetterStrip5(self):
|
||||||
|
self.assertEqual(parse("10 h 36.5", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10, 36, 30))
|
||||||
|
|
||||||
|
def testMinuteWithLettersSpaces1(self):
|
||||||
|
self.assertEqual(parse("36 m 5", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 36, 5))
|
||||||
|
|
||||||
|
def testMinuteWithLettersSpaces2(self):
|
||||||
|
self.assertEqual(parse("36 m 5 s", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 36, 5))
|
||||||
|
|
||||||
|
def testMinuteWithLettersSpaces3(self):
|
||||||
|
self.assertEqual(parse("36 m 05", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 36, 5))
|
||||||
|
|
||||||
|
def testMinuteWithLettersSpaces4(self):
|
||||||
|
self.assertEqual(parse("36 m 05 s", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 36, 5))
|
||||||
|
|
||||||
|
def testAMPMNoHour(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse("AM")
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse("Jan 20, 2015 PM")
|
||||||
|
|
||||||
|
def testHourAmPm1(self):
|
||||||
|
self.assertEqual(parse("10h am", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourAmPm2(self):
|
||||||
|
self.assertEqual(parse("10h pm", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 22))
|
||||||
|
|
||||||
|
def testHourAmPm3(self):
|
||||||
|
self.assertEqual(parse("10am", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourAmPm4(self):
|
||||||
|
self.assertEqual(parse("10pm", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 22))
|
||||||
|
|
||||||
|
def testHourAmPm5(self):
|
||||||
|
self.assertEqual(parse("10:00 am", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourAmPm6(self):
|
||||||
|
self.assertEqual(parse("10:00 pm", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 22))
|
||||||
|
|
||||||
|
def testHourAmPm7(self):
|
||||||
|
self.assertEqual(parse("10:00am", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourAmPm8(self):
|
||||||
|
self.assertEqual(parse("10:00pm", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 22))
|
||||||
|
|
||||||
|
def testHourAmPm9(self):
|
||||||
|
self.assertEqual(parse("10:00a.m", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourAmPm10(self):
|
||||||
|
self.assertEqual(parse("10:00p.m", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 22))
|
||||||
|
|
||||||
|
def testHourAmPm11(self):
|
||||||
|
self.assertEqual(parse("10:00a.m.", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 10))
|
||||||
|
|
||||||
|
def testHourAmPm12(self):
|
||||||
|
self.assertEqual(parse("10:00p.m.", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 22))
|
||||||
|
|
||||||
|
def testAMPMRange(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse("13:44 AM")
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse("January 25, 1921 23:13 PM")
|
||||||
|
|
||||||
|
def testPertain(self):
|
||||||
|
self.assertEqual(parse("Sep 03", default=self.default),
|
||||||
|
datetime(2003, 9, 3))
|
||||||
|
self.assertEqual(parse("Sep of 03", default=self.default),
|
||||||
|
datetime(2003, 9, 25))
|
||||||
|
|
||||||
|
def testWeekdayAlone(self):
|
||||||
|
self.assertEqual(parse("Wed", default=self.default),
|
||||||
|
datetime(2003, 10, 1))
|
||||||
|
|
||||||
|
def testLongWeekday(self):
|
||||||
|
self.assertEqual(parse("Wednesday", default=self.default),
|
||||||
|
datetime(2003, 10, 1))
|
||||||
|
|
||||||
|
def testLongMonth(self):
|
||||||
|
self.assertEqual(parse("October", default=self.default),
|
||||||
|
datetime(2003, 10, 25))
|
||||||
|
|
||||||
|
def testZeroYear(self):
|
||||||
|
self.assertEqual(parse("31-Dec-00", default=self.default),
|
||||||
|
datetime(2000, 12, 31))
|
||||||
|
|
||||||
|
def testFuzzy(self):
|
||||||
|
s = "Today is 25 of September of 2003, exactly " \
|
||||||
|
"at 10:49:41 with timezone -03:00."
|
||||||
|
self.assertEqual(parse(s, fuzzy=True),
|
||||||
|
datetime(2003, 9, 25, 10, 49, 41,
|
||||||
|
tzinfo=self.brsttz))
|
||||||
|
|
||||||
|
def testFuzzyWithTokens(self):
|
||||||
|
s1 = "Today is 25 of September of 2003, exactly " \
|
||||||
|
"at 10:49:41 with timezone -03:00."
|
||||||
|
self.assertEqual(parse(s1, fuzzy_with_tokens=True),
|
||||||
|
(datetime(2003, 9, 25, 10, 49, 41,
|
||||||
|
tzinfo=self.brsttz),
|
||||||
|
('Today is ', 'of ', ', exactly at ',
|
||||||
|
' with timezone ', '.')))
|
||||||
|
|
||||||
|
s2 = "http://biz.yahoo.com/ipo/p/600221.html"
|
||||||
|
self.assertEqual(parse(s2, fuzzy_with_tokens=True),
|
||||||
|
(datetime(2060, 2, 21, 0, 0, 0),
|
||||||
|
('http://biz.yahoo.com/ipo/p/', '.html')))
|
||||||
|
|
||||||
|
def testFuzzyAMPMProblem(self):
|
||||||
|
# Sometimes fuzzy parsing results in AM/PM flag being set without
|
||||||
|
# hours - if it's fuzzy it should ignore that.
|
||||||
|
s1 = "I have a meeting on March 1, 1974."
|
||||||
|
s2 = "On June 8th, 2020, I am going to be the first man on Mars"
|
||||||
|
|
||||||
|
# Also don't want any erroneous AM or PMs changing the parsed time
|
||||||
|
s3 = "Meet me at the AM/PM on Sunset at 3:00 AM on December 3rd, 2003"
|
||||||
|
s4 = "Meet me at 3:00AM on December 3rd, 2003 at the AM/PM on Sunset"
|
||||||
|
|
||||||
|
self.assertEqual(parse(s1, fuzzy=True), datetime(1974, 3, 1))
|
||||||
|
self.assertEqual(parse(s2, fuzzy=True), datetime(2020, 6, 8))
|
||||||
|
self.assertEqual(parse(s3, fuzzy=True), datetime(2003, 12, 3, 3))
|
||||||
|
self.assertEqual(parse(s4, fuzzy=True), datetime(2003, 12, 3, 3))
|
||||||
|
|
||||||
|
def testFuzzyIgnoreAMPM(self):
|
||||||
|
s1 = "Jan 29, 1945 14:45 AM I going to see you there?"
|
||||||
|
|
||||||
|
self.assertEqual(parse(s1, fuzzy=True), datetime(1945, 1, 29, 14, 45))
|
||||||
|
|
||||||
|
def testExtraSpace(self):
|
||||||
|
self.assertEqual(parse(" July 4 , 1976 12:01:02 am "),
|
||||||
|
datetime(1976, 7, 4, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat1(self):
|
||||||
|
self.assertEqual(parse("Wed, July 10, '96"),
|
||||||
|
datetime(1996, 7, 10, 0, 0))
|
||||||
|
|
||||||
|
def testRandomFormat2(self):
|
||||||
|
self.assertEqual(parse("1996.07.10 AD at 15:08:56 PDT",
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(1996, 7, 10, 15, 8, 56))
|
||||||
|
|
||||||
|
def testRandomFormat3(self):
|
||||||
|
self.assertEqual(parse("1996.July.10 AD 12:08 PM"),
|
||||||
|
datetime(1996, 7, 10, 12, 8))
|
||||||
|
|
||||||
|
def testRandomFormat4(self):
|
||||||
|
self.assertEqual(parse("Tuesday, April 12, 1952 AD 3:30:42pm PST",
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(1952, 4, 12, 15, 30, 42))
|
||||||
|
|
||||||
|
def testRandomFormat5(self):
|
||||||
|
self.assertEqual(parse("November 5, 1994, 8:15:30 am EST",
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(1994, 11, 5, 8, 15, 30))
|
||||||
|
|
||||||
|
def testRandomFormat6(self):
|
||||||
|
self.assertEqual(parse("1994-11-05T08:15:30-05:00",
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(1994, 11, 5, 8, 15, 30))
|
||||||
|
|
||||||
|
def testRandomFormat7(self):
|
||||||
|
self.assertEqual(parse("1994-11-05T08:15:30Z",
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(1994, 11, 5, 8, 15, 30))
|
||||||
|
|
||||||
|
def testRandomFormat8(self):
|
||||||
|
self.assertEqual(parse("July 4, 1976"), datetime(1976, 7, 4))
|
||||||
|
|
||||||
|
def testRandomFormat9(self):
|
||||||
|
self.assertEqual(parse("7 4 1976"), datetime(1976, 7, 4))
|
||||||
|
|
||||||
|
def testRandomFormat10(self):
|
||||||
|
self.assertEqual(parse("4 jul 1976"), datetime(1976, 7, 4))
|
||||||
|
|
||||||
|
def testRandomFormat11(self):
|
||||||
|
self.assertEqual(parse("7-4-76"), datetime(1976, 7, 4))
|
||||||
|
|
||||||
|
def testRandomFormat12(self):
|
||||||
|
self.assertEqual(parse("19760704"), datetime(1976, 7, 4))
|
||||||
|
|
||||||
|
def testRandomFormat13(self):
|
||||||
|
self.assertEqual(parse("0:01:02", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat14(self):
|
||||||
|
self.assertEqual(parse("12h 01m02s am", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat15(self):
|
||||||
|
self.assertEqual(parse("0:01:02 on July 4, 1976"),
|
||||||
|
datetime(1976, 7, 4, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat16(self):
|
||||||
|
self.assertEqual(parse("0:01:02 on July 4, 1976"),
|
||||||
|
datetime(1976, 7, 4, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat17(self):
|
||||||
|
self.assertEqual(parse("1976-07-04T00:01:02Z", ignoretz=True),
|
||||||
|
datetime(1976, 7, 4, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat18(self):
|
||||||
|
self.assertEqual(parse("July 4, 1976 12:01:02 am"),
|
||||||
|
datetime(1976, 7, 4, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat19(self):
|
||||||
|
self.assertEqual(parse("Mon Jan 2 04:24:27 1995"),
|
||||||
|
datetime(1995, 1, 2, 4, 24, 27))
|
||||||
|
|
||||||
|
def testRandomFormat20(self):
|
||||||
|
self.assertEqual(parse("Tue Apr 4 00:22:12 PDT 1995", ignoretz=True),
|
||||||
|
datetime(1995, 4, 4, 0, 22, 12))
|
||||||
|
|
||||||
|
def testRandomFormat21(self):
|
||||||
|
self.assertEqual(parse("04.04.95 00:22"),
|
||||||
|
datetime(1995, 4, 4, 0, 22))
|
||||||
|
|
||||||
|
def testRandomFormat22(self):
|
||||||
|
self.assertEqual(parse("Jan 1 1999 11:23:34.578"),
|
||||||
|
datetime(1999, 1, 1, 11, 23, 34, 578000))
|
||||||
|
|
||||||
|
def testRandomFormat23(self):
|
||||||
|
self.assertEqual(parse("950404 122212"),
|
||||||
|
datetime(1995, 4, 4, 12, 22, 12))
|
||||||
|
|
||||||
|
def testRandomFormat24(self):
|
||||||
|
self.assertEqual(parse("0:00 PM, PST", default=self.default,
|
||||||
|
ignoretz=True),
|
||||||
|
datetime(2003, 9, 25, 12, 0))
|
||||||
|
|
||||||
|
def testRandomFormat25(self):
|
||||||
|
self.assertEqual(parse("12:08 PM", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 12, 8))
|
||||||
|
|
||||||
|
def testRandomFormat26(self):
|
||||||
|
self.assertEqual(parse("5:50 A.M. on June 13, 1990"),
|
||||||
|
datetime(1990, 6, 13, 5, 50))
|
||||||
|
|
||||||
|
def testRandomFormat27(self):
|
||||||
|
self.assertEqual(parse("3rd of May 2001"), datetime(2001, 5, 3))
|
||||||
|
|
||||||
|
def testRandomFormat28(self):
|
||||||
|
self.assertEqual(parse("5th of March 2001"), datetime(2001, 3, 5))
|
||||||
|
|
||||||
|
def testRandomFormat29(self):
|
||||||
|
self.assertEqual(parse("1st of May 2003"), datetime(2003, 5, 1))
|
||||||
|
|
||||||
|
def testRandomFormat30(self):
|
||||||
|
self.assertEqual(parse("01h02m03", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 1, 2, 3))
|
||||||
|
|
||||||
|
def testRandomFormat31(self):
|
||||||
|
self.assertEqual(parse("01h02", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat32(self):
|
||||||
|
self.assertEqual(parse("01h02s", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 1, 0, 2))
|
||||||
|
|
||||||
|
def testRandomFormat33(self):
|
||||||
|
self.assertEqual(parse("01m02", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 0, 1, 2))
|
||||||
|
|
||||||
|
def testRandomFormat34(self):
|
||||||
|
self.assertEqual(parse("01m02h", default=self.default),
|
||||||
|
datetime(2003, 9, 25, 2, 1))
|
||||||
|
|
||||||
|
def testRandomFormat35(self):
|
||||||
|
self.assertEqual(parse("2004 10 Apr 11h30m", default=self.default),
|
||||||
|
datetime(2004, 4, 10, 11, 30))
|
||||||
|
|
||||||
|
def test_99_ad(self):
|
||||||
|
self.assertEqual(parse('0099-01-01T00:00:00'),
|
||||||
|
datetime(99, 1, 1, 0, 0))
|
||||||
|
|
||||||
|
def test_31_ad(self):
|
||||||
|
self.assertEqual(parse('0031-01-01T00:00:00'),
|
||||||
|
datetime(31, 1, 1, 0, 0))
|
||||||
|
|
||||||
|
def testInvalidDay(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parse("Feb 30, 2007")
|
||||||
|
|
||||||
|
def testUnspecifiedDayFallback(self):
|
||||||
|
# Test that for an unspecified day, the fallback behavior is correct.
|
||||||
|
self.assertEqual(parse("April 2009", default=datetime(2010, 1, 31)),
|
||||||
|
datetime(2009, 4, 30))
|
||||||
|
|
||||||
|
def testUnspecifiedDayFallbackFebNoLeapYear(self):
|
||||||
|
self.assertEqual(parse("Feb 2007", default=datetime(2010, 1, 31)),
|
||||||
|
datetime(2007, 2, 28))
|
||||||
|
|
||||||
|
def testUnspecifiedDayFallbackFebLeapYear(self):
|
||||||
|
self.assertEqual(parse("Feb 2008", default=datetime(2010, 1, 31)),
|
||||||
|
datetime(2008, 2, 29))
|
||||||
|
|
||||||
|
def testErrorType01(self):
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
parse, 'shouldfail')
|
||||||
|
|
||||||
|
def testCorrectErrorOnFuzzyWithTokens(self):
|
||||||
|
assertRaisesRegex(self, ValueError, 'Unknown string format',
|
||||||
|
parse, '04/04/32/423', fuzzy_with_tokens=True)
|
||||||
|
assertRaisesRegex(self, ValueError, 'Unknown string format',
|
||||||
|
parse, '04/04/04 +32423', fuzzy_with_tokens=True)
|
||||||
|
assertRaisesRegex(self, ValueError, 'Unknown string format',
|
||||||
|
parse, '04/04/0d4', fuzzy_with_tokens=True)
|
||||||
|
|
||||||
|
def testIncreasingCTime(self):
|
||||||
|
# This test will check 200 different years, every month, every day,
|
||||||
|
# every hour, every minute, every second, and every weekday, using
|
||||||
|
# a delta of more or less 1 year, 1 month, 1 day, 1 minute and
|
||||||
|
# 1 second.
|
||||||
|
delta = timedelta(days=365+31+1, seconds=1+60+60*60)
|
||||||
|
dt = datetime(1900, 1, 1, 0, 0, 0, 0)
|
||||||
|
for i in range(200):
|
||||||
|
self.assertEqual(parse(dt.ctime()), dt)
|
||||||
|
dt += delta
|
||||||
|
|
||||||
|
def testIncreasingISOFormat(self):
|
||||||
|
delta = timedelta(days=365+31+1, seconds=1+60+60*60)
|
||||||
|
dt = datetime(1900, 1, 1, 0, 0, 0, 0)
|
||||||
|
for i in range(200):
|
||||||
|
self.assertEqual(parse(dt.isoformat()), dt)
|
||||||
|
dt += delta
|
||||||
|
|
||||||
|
def testMicrosecondsPrecisionError(self):
|
||||||
|
# Skip found out that sad precision problem. :-(
|
||||||
|
dt1 = parse("00:11:25.01")
|
||||||
|
dt2 = parse("00:12:10.01")
|
||||||
|
self.assertEqual(dt1.microsecond, 10000)
|
||||||
|
self.assertEqual(dt2.microsecond, 10000)
|
||||||
|
|
||||||
|
def testMicrosecondPrecisionErrorReturns(self):
|
||||||
|
# One more precision issue, discovered by Eric Brown. This should
|
||||||
|
# be the last one, as we're no longer using floating points.
|
||||||
|
for ms in [100001, 100000, 99999, 99998,
|
||||||
|
10001, 10000, 9999, 9998,
|
||||||
|
1001, 1000, 999, 998,
|
||||||
|
101, 100, 99, 98]:
|
||||||
|
dt = datetime(2008, 2, 27, 21, 26, 1, ms)
|
||||||
|
self.assertEqual(parse(dt.isoformat()), dt)
|
||||||
|
|
||||||
|
def testHighPrecisionSeconds(self):
|
||||||
|
self.assertEqual(parse("20080227T21:26:01.123456789"),
|
||||||
|
datetime(2008, 2, 27, 21, 26, 1, 123456))
|
||||||
|
|
||||||
|
def testCustomParserInfo(self):
|
||||||
|
# Custom parser info wasn't working, as Michael Elsdörfer discovered.
|
||||||
|
from dateutil.parser import parserinfo, parser
|
||||||
|
|
||||||
|
class myparserinfo(parserinfo):
|
||||||
|
MONTHS = parserinfo.MONTHS[:]
|
||||||
|
MONTHS[0] = ("Foo", "Foo")
|
||||||
|
myparser = parser(myparserinfo())
|
||||||
|
dt = myparser.parse("01/Foo/2007")
|
||||||
|
self.assertEqual(dt, datetime(2007, 1, 1))
|
||||||
|
|
||||||
|
def testCustomParserShortDaynames(self):
|
||||||
|
# Horacio Hoyos discovered that day names shorter than 3 characters,
|
||||||
|
# for example two letter German day name abbreviations, don't work:
|
||||||
|
# https://github.com/dateutil/dateutil/issues/343
|
||||||
|
from dateutil.parser import parserinfo, parser
|
||||||
|
|
||||||
|
class GermanParserInfo(parserinfo):
|
||||||
|
WEEKDAYS = [("Mo", "Montag"),
|
||||||
|
("Di", "Dienstag"),
|
||||||
|
("Mi", "Mittwoch"),
|
||||||
|
("Do", "Donnerstag"),
|
||||||
|
("Fr", "Freitag"),
|
||||||
|
("Sa", "Samstag"),
|
||||||
|
("So", "Sonntag")]
|
||||||
|
|
||||||
|
myparser = parser(GermanParserInfo())
|
||||||
|
dt = myparser.parse("Sa 21. Jan 2017")
|
||||||
|
self.assertEqual(dt, datetime(2017, 1, 21))
|
||||||
|
|
||||||
|
def testNoYearFirstNoDayFirst(self):
|
||||||
|
dtstr = '090107'
|
||||||
|
|
||||||
|
# Should be MMDDYY
|
||||||
|
self.assertEqual(parse(dtstr),
|
||||||
|
datetime(2007, 9, 1))
|
||||||
|
|
||||||
|
self.assertEqual(parse(dtstr, yearfirst=False, dayfirst=False),
|
||||||
|
datetime(2007, 9, 1))
|
||||||
|
|
||||||
|
def testYearFirst(self):
|
||||||
|
dtstr = '090107'
|
||||||
|
|
||||||
|
# Should be MMDDYY
|
||||||
|
self.assertEqual(parse(dtstr, yearfirst=True),
|
||||||
|
datetime(2009, 1, 7))
|
||||||
|
|
||||||
|
self.assertEqual(parse(dtstr, yearfirst=True, dayfirst=False),
|
||||||
|
datetime(2009, 1, 7))
|
||||||
|
|
||||||
|
def testDayFirst(self):
|
||||||
|
dtstr = '090107'
|
||||||
|
|
||||||
|
# Should be DDMMYY
|
||||||
|
self.assertEqual(parse(dtstr, dayfirst=True),
|
||||||
|
datetime(2007, 1, 9))
|
||||||
|
|
||||||
|
self.assertEqual(parse(dtstr, yearfirst=False, dayfirst=True),
|
||||||
|
datetime(2007, 1, 9))
|
||||||
|
|
||||||
|
def testDayFirstYearFirst(self):
|
||||||
|
dtstr = '090107'
|
||||||
|
# Should be YYDDMM
|
||||||
|
self.assertEqual(parse(dtstr, yearfirst=True, dayfirst=True),
|
||||||
|
datetime(2009, 7, 1))
|
||||||
|
|
||||||
|
def testUnambiguousYearFirst(self):
|
||||||
|
dtstr = '2015 09 25'
|
||||||
|
self.assertEqual(parse(dtstr, yearfirst=True),
|
||||||
|
datetime(2015, 9, 25))
|
||||||
|
|
||||||
|
def testUnambiguousDayFirst(self):
|
||||||
|
dtstr = '2015 09 25'
|
||||||
|
self.assertEqual(parse(dtstr, dayfirst=True),
|
||||||
|
datetime(2015, 9, 25))
|
||||||
|
|
||||||
|
def testUnambiguousDayFirstYearFirst(self):
|
||||||
|
dtstr = '2015 09 25'
|
||||||
|
self.assertEqual(parse(dtstr, dayfirst=True, yearfirst=True),
|
||||||
|
datetime(2015, 9, 25))
|
577
benchmark/third_party/dateutil/test/test_relativedelta.py
vendored
Normal file
577
benchmark/third_party/dateutil/test/test_relativedelta.py
vendored
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from ._common import unittest, WarningTestMixin, NotAValue
|
||||||
|
|
||||||
|
import calendar
|
||||||
|
from datetime import datetime, date, timedelta
|
||||||
|
|
||||||
|
from dateutil.relativedelta import relativedelta, MO, TU, WE, FR, SU
|
||||||
|
|
||||||
|
|
||||||
|
class RelativeDeltaTest(WarningTestMixin, unittest.TestCase):
|
||||||
|
now = datetime(2003, 9, 17, 20, 54, 47, 282310)
|
||||||
|
today = date(2003, 9, 17)
|
||||||
|
|
||||||
|
def testInheritance(self):
|
||||||
|
# Ensure that relativedelta is inheritance-friendly.
|
||||||
|
class rdChildClass(relativedelta):
|
||||||
|
pass
|
||||||
|
|
||||||
|
ccRD = rdChildClass(years=1, months=1, days=1, leapdays=1, weeks=1,
|
||||||
|
hours=1, minutes=1, seconds=1, microseconds=1)
|
||||||
|
|
||||||
|
rd = relativedelta(years=1, months=1, days=1, leapdays=1, weeks=1,
|
||||||
|
hours=1, minutes=1, seconds=1, microseconds=1)
|
||||||
|
|
||||||
|
self.assertEqual(type(ccRD + rd), type(ccRD),
|
||||||
|
msg='Addition does not inherit type.')
|
||||||
|
|
||||||
|
self.assertEqual(type(ccRD - rd), type(ccRD),
|
||||||
|
msg='Subtraction does not inherit type.')
|
||||||
|
|
||||||
|
self.assertEqual(type(-ccRD), type(ccRD),
|
||||||
|
msg='Negation does not inherit type.')
|
||||||
|
|
||||||
|
self.assertEqual(type(ccRD * 5.0), type(ccRD),
|
||||||
|
msg='Multiplication does not inherit type.')
|
||||||
|
|
||||||
|
self.assertEqual(type(ccRD / 5.0), type(ccRD),
|
||||||
|
msg='Division does not inherit type.')
|
||||||
|
|
||||||
|
def testMonthEndMonthBeginning(self):
|
||||||
|
self.assertEqual(relativedelta(datetime(2003, 1, 31, 23, 59, 59),
|
||||||
|
datetime(2003, 3, 1, 0, 0, 0)),
|
||||||
|
relativedelta(months=-1, seconds=-1))
|
||||||
|
|
||||||
|
self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0),
|
||||||
|
datetime(2003, 1, 31, 23, 59, 59)),
|
||||||
|
relativedelta(months=1, seconds=1))
|
||||||
|
|
||||||
|
def testMonthEndMonthBeginningLeapYear(self):
|
||||||
|
self.assertEqual(relativedelta(datetime(2012, 1, 31, 23, 59, 59),
|
||||||
|
datetime(2012, 3, 1, 0, 0, 0)),
|
||||||
|
relativedelta(months=-1, seconds=-1))
|
||||||
|
|
||||||
|
self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0),
|
||||||
|
datetime(2003, 1, 31, 23, 59, 59)),
|
||||||
|
relativedelta(months=1, seconds=1))
|
||||||
|
|
||||||
|
def testNextMonth(self):
|
||||||
|
self.assertEqual(self.now+relativedelta(months=+1),
|
||||||
|
datetime(2003, 10, 17, 20, 54, 47, 282310))
|
||||||
|
|
||||||
|
def testNextMonthPlusOneWeek(self):
|
||||||
|
self.assertEqual(self.now+relativedelta(months=+1, weeks=+1),
|
||||||
|
datetime(2003, 10, 24, 20, 54, 47, 282310))
|
||||||
|
|
||||||
|
def testNextMonthPlusOneWeek10am(self):
|
||||||
|
self.assertEqual(self.today +
|
||||||
|
relativedelta(months=+1, weeks=+1, hour=10),
|
||||||
|
datetime(2003, 10, 24, 10, 0))
|
||||||
|
|
||||||
|
def testNextMonthPlusOneWeek10amDiff(self):
|
||||||
|
self.assertEqual(relativedelta(datetime(2003, 10, 24, 10, 0),
|
||||||
|
self.today),
|
||||||
|
relativedelta(months=+1, days=+7, hours=+10))
|
||||||
|
|
||||||
|
def testOneMonthBeforeOneYear(self):
|
||||||
|
self.assertEqual(self.now+relativedelta(years=+1, months=-1),
|
||||||
|
datetime(2004, 8, 17, 20, 54, 47, 282310))
|
||||||
|
|
||||||
|
def testMonthsOfDiffNumOfDays(self):
|
||||||
|
self.assertEqual(date(2003, 1, 27)+relativedelta(months=+1),
|
||||||
|
date(2003, 2, 27))
|
||||||
|
self.assertEqual(date(2003, 1, 31)+relativedelta(months=+1),
|
||||||
|
date(2003, 2, 28))
|
||||||
|
self.assertEqual(date(2003, 1, 31)+relativedelta(months=+2),
|
||||||
|
date(2003, 3, 31))
|
||||||
|
|
||||||
|
def testMonthsOfDiffNumOfDaysWithYears(self):
|
||||||
|
self.assertEqual(date(2000, 2, 28)+relativedelta(years=+1),
|
||||||
|
date(2001, 2, 28))
|
||||||
|
self.assertEqual(date(2000, 2, 29)+relativedelta(years=+1),
|
||||||
|
date(2001, 2, 28))
|
||||||
|
|
||||||
|
self.assertEqual(date(1999, 2, 28)+relativedelta(years=+1),
|
||||||
|
date(2000, 2, 28))
|
||||||
|
self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1),
|
||||||
|
date(2000, 3, 1))
|
||||||
|
self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1),
|
||||||
|
date(2000, 3, 1))
|
||||||
|
|
||||||
|
self.assertEqual(date(2001, 2, 28)+relativedelta(years=-1),
|
||||||
|
date(2000, 2, 28))
|
||||||
|
self.assertEqual(date(2001, 3, 1)+relativedelta(years=-1),
|
||||||
|
date(2000, 3, 1))
|
||||||
|
|
||||||
|
def testNextFriday(self):
|
||||||
|
self.assertEqual(self.today+relativedelta(weekday=FR),
|
||||||
|
date(2003, 9, 19))
|
||||||
|
|
||||||
|
def testNextFridayInt(self):
|
||||||
|
self.assertEqual(self.today+relativedelta(weekday=calendar.FRIDAY),
|
||||||
|
date(2003, 9, 19))
|
||||||
|
|
||||||
|
def testLastFridayInThisMonth(self):
|
||||||
|
self.assertEqual(self.today+relativedelta(day=31, weekday=FR(-1)),
|
||||||
|
date(2003, 9, 26))
|
||||||
|
|
||||||
|
def testNextWednesdayIsToday(self):
|
||||||
|
self.assertEqual(self.today+relativedelta(weekday=WE),
|
||||||
|
date(2003, 9, 17))
|
||||||
|
|
||||||
|
def testNextWenesdayNotToday(self):
|
||||||
|
self.assertEqual(self.today+relativedelta(days=+1, weekday=WE),
|
||||||
|
date(2003, 9, 24))
|
||||||
|
|
||||||
|
def test15thISOYearWeek(self):
|
||||||
|
self.assertEqual(date(2003, 1, 1) +
|
||||||
|
relativedelta(day=4, weeks=+14, weekday=MO(-1)),
|
||||||
|
date(2003, 4, 7))
|
||||||
|
|
||||||
|
def testMillenniumAge(self):
|
||||||
|
self.assertEqual(relativedelta(self.now, date(2001, 1, 1)),
|
||||||
|
relativedelta(years=+2, months=+8, days=+16,
|
||||||
|
hours=+20, minutes=+54, seconds=+47,
|
||||||
|
microseconds=+282310))
|
||||||
|
|
||||||
|
def testJohnAge(self):
|
||||||
|
self.assertEqual(relativedelta(self.now,
|
||||||
|
datetime(1978, 4, 5, 12, 0)),
|
||||||
|
relativedelta(years=+25, months=+5, days=+12,
|
||||||
|
hours=+8, minutes=+54, seconds=+47,
|
||||||
|
microseconds=+282310))
|
||||||
|
|
||||||
|
def testJohnAgeWithDate(self):
|
||||||
|
self.assertEqual(relativedelta(self.today,
|
||||||
|
datetime(1978, 4, 5, 12, 0)),
|
||||||
|
relativedelta(years=+25, months=+5, days=+11,
|
||||||
|
hours=+12))
|
||||||
|
|
||||||
|
def testYearDay(self):
|
||||||
|
self.assertEqual(date(2003, 1, 1)+relativedelta(yearday=260),
|
||||||
|
date(2003, 9, 17))
|
||||||
|
self.assertEqual(date(2002, 1, 1)+relativedelta(yearday=260),
|
||||||
|
date(2002, 9, 17))
|
||||||
|
self.assertEqual(date(2000, 1, 1)+relativedelta(yearday=260),
|
||||||
|
date(2000, 9, 16))
|
||||||
|
self.assertEqual(self.today+relativedelta(yearday=261),
|
||||||
|
date(2003, 9, 18))
|
||||||
|
|
||||||
|
def testYearDayBug(self):
|
||||||
|
# Tests a problem reported by Adam Ryan.
|
||||||
|
self.assertEqual(date(2010, 1, 1)+relativedelta(yearday=15),
|
||||||
|
date(2010, 1, 15))
|
||||||
|
|
||||||
|
def testNonLeapYearDay(self):
|
||||||
|
self.assertEqual(date(2003, 1, 1)+relativedelta(nlyearday=260),
|
||||||
|
date(2003, 9, 17))
|
||||||
|
self.assertEqual(date(2002, 1, 1)+relativedelta(nlyearday=260),
|
||||||
|
date(2002, 9, 17))
|
||||||
|
self.assertEqual(date(2000, 1, 1)+relativedelta(nlyearday=260),
|
||||||
|
date(2000, 9, 17))
|
||||||
|
self.assertEqual(self.today+relativedelta(yearday=261),
|
||||||
|
date(2003, 9, 18))
|
||||||
|
|
||||||
|
def testAddition(self):
|
||||||
|
self.assertEqual(relativedelta(days=10) +
|
||||||
|
relativedelta(years=1, months=2, days=3, hours=4,
|
||||||
|
minutes=5, microseconds=6),
|
||||||
|
relativedelta(years=1, months=2, days=13, hours=4,
|
||||||
|
minutes=5, microseconds=6))
|
||||||
|
|
||||||
|
def testAbsoluteAddition(self):
|
||||||
|
self.assertEqual(relativedelta() + relativedelta(day=0, hour=0),
|
||||||
|
relativedelta(day=0, hour=0))
|
||||||
|
self.assertEqual(relativedelta(day=0, hour=0) + relativedelta(),
|
||||||
|
relativedelta(day=0, hour=0))
|
||||||
|
|
||||||
|
def testAdditionToDatetime(self):
|
||||||
|
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1),
|
||||||
|
datetime(2000, 1, 2))
|
||||||
|
|
||||||
|
def testRightAdditionToDatetime(self):
|
||||||
|
self.assertEqual(relativedelta(days=1) + datetime(2000, 1, 1),
|
||||||
|
datetime(2000, 1, 2))
|
||||||
|
|
||||||
|
def testAdditionInvalidType(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
relativedelta(days=3) + 9
|
||||||
|
|
||||||
|
def testAdditionUnsupportedType(self):
|
||||||
|
# For unsupported types that define their own comparators, etc.
|
||||||
|
self.assertIs(relativedelta(days=1) + NotAValue, NotAValue)
|
||||||
|
|
||||||
|
def testSubtraction(self):
|
||||||
|
self.assertEqual(relativedelta(days=10) -
|
||||||
|
relativedelta(years=1, months=2, days=3, hours=4,
|
||||||
|
minutes=5, microseconds=6),
|
||||||
|
relativedelta(years=-1, months=-2, days=7, hours=-4,
|
||||||
|
minutes=-5, microseconds=-6))
|
||||||
|
|
||||||
|
def testRightSubtractionFromDatetime(self):
|
||||||
|
self.assertEqual(datetime(2000, 1, 2) - relativedelta(days=1),
|
||||||
|
datetime(2000, 1, 1))
|
||||||
|
|
||||||
|
def testSubractionWithDatetime(self):
|
||||||
|
self.assertRaises(TypeError, lambda x, y: x - y,
|
||||||
|
(relativedelta(days=1), datetime(2000, 1, 1)))
|
||||||
|
|
||||||
|
def testSubtractionInvalidType(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
relativedelta(hours=12) - 14
|
||||||
|
|
||||||
|
def testSubtractionUnsupportedType(self):
|
||||||
|
self.assertIs(relativedelta(days=1) + NotAValue, NotAValue)
|
||||||
|
|
||||||
|
def testMultiplication(self):
|
||||||
|
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1) * 28,
|
||||||
|
datetime(2000, 1, 29))
|
||||||
|
self.assertEqual(datetime(2000, 1, 1) + 28 * relativedelta(days=1),
|
||||||
|
datetime(2000, 1, 29))
|
||||||
|
|
||||||
|
def testMultiplicationUnsupportedType(self):
|
||||||
|
self.assertIs(relativedelta(days=1) * NotAValue, NotAValue)
|
||||||
|
|
||||||
|
def testDivision(self):
|
||||||
|
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=28) / 28,
|
||||||
|
datetime(2000, 1, 2))
|
||||||
|
|
||||||
|
def testDivisionUnsupportedType(self):
|
||||||
|
self.assertIs(relativedelta(days=1) / NotAValue, NotAValue)
|
||||||
|
|
||||||
|
def testBoolean(self):
|
||||||
|
self.assertFalse(relativedelta(days=0))
|
||||||
|
self.assertTrue(relativedelta(days=1))
|
||||||
|
|
||||||
|
def testComparison(self):
|
||||||
|
d1 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
|
||||||
|
minutes=1, seconds=1, microseconds=1)
|
||||||
|
d2 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
|
||||||
|
minutes=1, seconds=1, microseconds=1)
|
||||||
|
d3 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
|
||||||
|
minutes=1, seconds=1, microseconds=2)
|
||||||
|
|
||||||
|
self.assertEqual(d1, d2)
|
||||||
|
self.assertNotEqual(d1, d3)
|
||||||
|
|
||||||
|
def testInequalityTypeMismatch(self):
|
||||||
|
# Different type
|
||||||
|
self.assertFalse(relativedelta(year=1) == 19)
|
||||||
|
|
||||||
|
def testInequalityUnsupportedType(self):
|
||||||
|
self.assertIs(relativedelta(hours=3) == NotAValue, NotAValue)
|
||||||
|
|
||||||
|
def testInequalityWeekdays(self):
|
||||||
|
# Different weekdays
|
||||||
|
no_wday = relativedelta(year=1997, month=4)
|
||||||
|
wday_mo_1 = relativedelta(year=1997, month=4, weekday=MO(+1))
|
||||||
|
wday_mo_2 = relativedelta(year=1997, month=4, weekday=MO(+2))
|
||||||
|
wday_tu = relativedelta(year=1997, month=4, weekday=TU)
|
||||||
|
|
||||||
|
self.assertTrue(wday_mo_1 == wday_mo_1)
|
||||||
|
|
||||||
|
self.assertFalse(no_wday == wday_mo_1)
|
||||||
|
self.assertFalse(wday_mo_1 == no_wday)
|
||||||
|
|
||||||
|
self.assertFalse(wday_mo_1 == wday_mo_2)
|
||||||
|
self.assertFalse(wday_mo_2 == wday_mo_1)
|
||||||
|
|
||||||
|
self.assertFalse(wday_mo_1 == wday_tu)
|
||||||
|
self.assertFalse(wday_tu == wday_mo_1)
|
||||||
|
|
||||||
|
def testMonthOverflow(self):
|
||||||
|
self.assertEqual(relativedelta(months=273),
|
||||||
|
relativedelta(years=22, months=9))
|
||||||
|
|
||||||
|
def testWeeks(self):
|
||||||
|
# Test that the weeks property is working properly.
|
||||||
|
rd = relativedelta(years=4, months=2, weeks=8, days=6)
|
||||||
|
self.assertEqual((rd.weeks, rd.days), (8, 8 * 7 + 6))
|
||||||
|
|
||||||
|
rd.weeks = 3
|
||||||
|
self.assertEqual((rd.weeks, rd.days), (3, 3 * 7 + 6))
|
||||||
|
|
||||||
|
def testRelativeDeltaRepr(self):
|
||||||
|
self.assertEqual(repr(relativedelta(years=1, months=-1, days=15)),
|
||||||
|
'relativedelta(years=+1, months=-1, days=+15)')
|
||||||
|
|
||||||
|
self.assertEqual(repr(relativedelta(months=14, seconds=-25)),
|
||||||
|
'relativedelta(years=+1, months=+2, seconds=-25)')
|
||||||
|
|
||||||
|
self.assertEqual(repr(relativedelta(month=3, hour=3, weekday=SU(3))),
|
||||||
|
'relativedelta(month=3, weekday=SU(+3), hour=3)')
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalYear(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
relativedelta(years=1.5)
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalMonth(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
relativedelta(months=1.5)
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalAbsolutes(self):
|
||||||
|
# Fractional absolute values will soon be unsupported,
|
||||||
|
# check for the deprecation warning.
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(year=2.86)
|
||||||
|
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(month=1.29)
|
||||||
|
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(day=0.44)
|
||||||
|
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(hour=23.98)
|
||||||
|
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(minute=45.21)
|
||||||
|
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(second=13.2)
|
||||||
|
|
||||||
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
relativedelta(microsecond=157221.93)
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalRepr(self):
|
||||||
|
rd = relativedelta(years=3, months=-2, days=1.25)
|
||||||
|
|
||||||
|
self.assertEqual(repr(rd),
|
||||||
|
'relativedelta(years=+3, months=-2, days=+1.25)')
|
||||||
|
|
||||||
|
rd = relativedelta(hours=0.5, seconds=9.22)
|
||||||
|
self.assertEqual(repr(rd),
|
||||||
|
'relativedelta(hours=+0.5, seconds=+9.22)')
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalWeeks(self):
|
||||||
|
# Equivalent to days=8, hours=18
|
||||||
|
rd = relativedelta(weeks=1.25)
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd,
|
||||||
|
datetime(2009, 9, 11, 18))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalDays(self):
|
||||||
|
rd1 = relativedelta(days=1.48)
|
||||||
|
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd1,
|
||||||
|
datetime(2009, 9, 4, 11, 31, 12))
|
||||||
|
|
||||||
|
rd2 = relativedelta(days=1.5)
|
||||||
|
self.assertEqual(d1 + rd2,
|
||||||
|
datetime(2009, 9, 4, 12, 0, 0))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalHours(self):
|
||||||
|
rd = relativedelta(days=1, hours=12.5)
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd,
|
||||||
|
datetime(2009, 9, 4, 12, 30, 0))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalMinutes(self):
|
||||||
|
rd = relativedelta(hours=1, minutes=30.5)
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd,
|
||||||
|
datetime(2009, 9, 3, 1, 30, 30))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalSeconds(self):
|
||||||
|
rd = relativedelta(hours=5, minutes=30, seconds=30.5)
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd,
|
||||||
|
datetime(2009, 9, 3, 5, 30, 30, 500000))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalPositiveOverflow(self):
|
||||||
|
# Equivalent to (days=1, hours=14)
|
||||||
|
rd1 = relativedelta(days=1.5, hours=2)
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd1,
|
||||||
|
datetime(2009, 9, 4, 14, 0, 0))
|
||||||
|
|
||||||
|
# Equivalent to (days=1, hours=14, minutes=45)
|
||||||
|
rd2 = relativedelta(days=1.5, hours=2.5, minutes=15)
|
||||||
|
d1 = datetime(2009, 9, 3, 0, 0)
|
||||||
|
self.assertEqual(d1 + rd2,
|
||||||
|
datetime(2009, 9, 4, 14, 45))
|
||||||
|
|
||||||
|
# Carry back up - equivalent to (days=2, hours=2, minutes=0, seconds=1)
|
||||||
|
rd3 = relativedelta(days=1.5, hours=13, minutes=59.5, seconds=31)
|
||||||
|
self.assertEqual(d1 + rd3,
|
||||||
|
datetime(2009, 9, 5, 2, 0, 1))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalNegativeDays(self):
|
||||||
|
# Equivalent to (days=-1, hours=-1)
|
||||||
|
rd1 = relativedelta(days=-1.5, hours=11)
|
||||||
|
d1 = datetime(2009, 9, 3, 12, 0)
|
||||||
|
self.assertEqual(d1 + rd1,
|
||||||
|
datetime(2009, 9, 2, 11, 0, 0))
|
||||||
|
|
||||||
|
# Equivalent to (days=-1, hours=-9)
|
||||||
|
rd2 = relativedelta(days=-1.25, hours=-3)
|
||||||
|
self.assertEqual(d1 + rd2,
|
||||||
|
datetime(2009, 9, 2, 3))
|
||||||
|
|
||||||
|
def testRelativeDeltaNormalizeFractionalDays(self):
|
||||||
|
# Equivalent to (days=2, hours=18)
|
||||||
|
rd1 = relativedelta(days=2.75)
|
||||||
|
|
||||||
|
self.assertEqual(rd1.normalized(), relativedelta(days=2, hours=18))
|
||||||
|
|
||||||
|
# Equvalent to (days=1, hours=11, minutes=31, seconds=12)
|
||||||
|
rd2 = relativedelta(days=1.48)
|
||||||
|
|
||||||
|
self.assertEqual(rd2.normalized(),
|
||||||
|
relativedelta(days=1, hours=11, minutes=31, seconds=12))
|
||||||
|
|
||||||
|
def testRelativeDeltaNormalizeFractionalDays2(self):
|
||||||
|
# Equivalent to (hours=1, minutes=30)
|
||||||
|
rd1 = relativedelta(hours=1.5)
|
||||||
|
|
||||||
|
self.assertEqual(rd1.normalized(), relativedelta(hours=1, minutes=30))
|
||||||
|
|
||||||
|
# Equivalent to (hours=3, minutes=17, seconds=5, microseconds=100)
|
||||||
|
rd2 = relativedelta(hours=3.28472225)
|
||||||
|
|
||||||
|
self.assertEqual(rd2.normalized(),
|
||||||
|
relativedelta(hours=3, minutes=17, seconds=5, microseconds=100))
|
||||||
|
|
||||||
|
def testRelativeDeltaNormalizeFractionalMinutes(self):
|
||||||
|
# Equivalent to (minutes=15, seconds=36)
|
||||||
|
rd1 = relativedelta(minutes=15.6)
|
||||||
|
|
||||||
|
self.assertEqual(rd1.normalized(),
|
||||||
|
relativedelta(minutes=15, seconds=36))
|
||||||
|
|
||||||
|
# Equivalent to (minutes=25, seconds=20, microseconds=25000)
|
||||||
|
rd2 = relativedelta(minutes=25.33375)
|
||||||
|
|
||||||
|
self.assertEqual(rd2.normalized(),
|
||||||
|
relativedelta(minutes=25, seconds=20, microseconds=25000))
|
||||||
|
|
||||||
|
def testRelativeDeltaNormalizeFractionalSeconds(self):
|
||||||
|
# Equivalent to (seconds=45, microseconds=25000)
|
||||||
|
rd1 = relativedelta(seconds=45.025)
|
||||||
|
self.assertEqual(rd1.normalized(),
|
||||||
|
relativedelta(seconds=45, microseconds=25000))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalPositiveOverflow2(self):
|
||||||
|
# Equivalent to (days=1, hours=14)
|
||||||
|
rd1 = relativedelta(days=1.5, hours=2)
|
||||||
|
self.assertEqual(rd1.normalized(),
|
||||||
|
relativedelta(days=1, hours=14))
|
||||||
|
|
||||||
|
# Equivalent to (days=1, hours=14, minutes=45)
|
||||||
|
rd2 = relativedelta(days=1.5, hours=2.5, minutes=15)
|
||||||
|
self.assertEqual(rd2.normalized(),
|
||||||
|
relativedelta(days=1, hours=14, minutes=45))
|
||||||
|
|
||||||
|
# Carry back up - equivalent to:
|
||||||
|
# (days=2, hours=2, minutes=0, seconds=2, microseconds=3)
|
||||||
|
rd3 = relativedelta(days=1.5, hours=13, minutes=59.50045,
|
||||||
|
seconds=31.473, microseconds=500003)
|
||||||
|
self.assertEqual(rd3.normalized(),
|
||||||
|
relativedelta(days=2, hours=2, minutes=0,
|
||||||
|
seconds=2, microseconds=3))
|
||||||
|
|
||||||
|
def testRelativeDeltaFractionalNegativeOverflow(self):
|
||||||
|
# Equivalent to (days=-1)
|
||||||
|
rd1 = relativedelta(days=-0.5, hours=-12)
|
||||||
|
self.assertEqual(rd1.normalized(),
|
||||||
|
relativedelta(days=-1))
|
||||||
|
|
||||||
|
# Equivalent to (days=-1)
|
||||||
|
rd2 = relativedelta(days=-1.5, hours=12)
|
||||||
|
self.assertEqual(rd2.normalized(),
|
||||||
|
relativedelta(days=-1))
|
||||||
|
|
||||||
|
# Equivalent to (days=-1, hours=-14, minutes=-45)
|
||||||
|
rd3 = relativedelta(days=-1.5, hours=-2.5, minutes=-15)
|
||||||
|
self.assertEqual(rd3.normalized(),
|
||||||
|
relativedelta(days=-1, hours=-14, minutes=-45))
|
||||||
|
|
||||||
|
# Equivalent to (days=-1, hours=-14, minutes=+15)
|
||||||
|
rd4 = relativedelta(days=-1.5, hours=-2.5, minutes=45)
|
||||||
|
self.assertEqual(rd4.normalized(),
|
||||||
|
relativedelta(days=-1, hours=-14, minutes=+15))
|
||||||
|
|
||||||
|
# Carry back up - equivalent to:
|
||||||
|
# (days=-2, hours=-2, minutes=0, seconds=-2, microseconds=-3)
|
||||||
|
rd3 = relativedelta(days=-1.5, hours=-13, minutes=-59.50045,
|
||||||
|
seconds=-31.473, microseconds=-500003)
|
||||||
|
self.assertEqual(rd3.normalized(),
|
||||||
|
relativedelta(days=-2, hours=-2, minutes=0,
|
||||||
|
seconds=-2, microseconds=-3))
|
||||||
|
|
||||||
|
def testInvalidYearDay(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
relativedelta(yearday=367)
|
||||||
|
|
||||||
|
def testAddTimedeltaToUnpopulatedRelativedelta(self):
|
||||||
|
td = timedelta(
|
||||||
|
days=1,
|
||||||
|
seconds=1,
|
||||||
|
microseconds=1,
|
||||||
|
milliseconds=1,
|
||||||
|
minutes=1,
|
||||||
|
hours=1,
|
||||||
|
weeks=1
|
||||||
|
)
|
||||||
|
|
||||||
|
expected = relativedelta(
|
||||||
|
weeks=1,
|
||||||
|
days=1,
|
||||||
|
hours=1,
|
||||||
|
minutes=1,
|
||||||
|
seconds=1,
|
||||||
|
microseconds=1001
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(expected, relativedelta() + td)
|
||||||
|
|
||||||
|
def testAddTimedeltaToPopulatedRelativeDelta(self):
|
||||||
|
td = timedelta(
|
||||||
|
days=1,
|
||||||
|
seconds=1,
|
||||||
|
microseconds=1,
|
||||||
|
milliseconds=1,
|
||||||
|
minutes=1,
|
||||||
|
hours=1,
|
||||||
|
weeks=1
|
||||||
|
)
|
||||||
|
|
||||||
|
rd = relativedelta(
|
||||||
|
year=1,
|
||||||
|
month=1,
|
||||||
|
day=1,
|
||||||
|
hour=1,
|
||||||
|
minute=1,
|
||||||
|
second=1,
|
||||||
|
microsecond=1,
|
||||||
|
years=1,
|
||||||
|
months=1,
|
||||||
|
days=1,
|
||||||
|
weeks=1,
|
||||||
|
hours=1,
|
||||||
|
minutes=1,
|
||||||
|
seconds=1,
|
||||||
|
microseconds=1
|
||||||
|
)
|
||||||
|
|
||||||
|
expected = relativedelta(
|
||||||
|
year=1,
|
||||||
|
month=1,
|
||||||
|
day=1,
|
||||||
|
hour=1,
|
||||||
|
minute=1,
|
||||||
|
second=1,
|
||||||
|
microsecond=1,
|
||||||
|
years=1,
|
||||||
|
months=1,
|
||||||
|
weeks=2,
|
||||||
|
days=2,
|
||||||
|
hours=2,
|
||||||
|
minutes=2,
|
||||||
|
seconds=2,
|
||||||
|
microseconds=1002,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(expected, rd + td)
|
4704
benchmark/third_party/dateutil/test/test_rrule.py
vendored
Normal file
4704
benchmark/third_party/dateutil/test/test_rrule.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2161
benchmark/third_party/dateutil/test/test_tz.py
vendored
Normal file
2161
benchmark/third_party/dateutil/test/test_tz.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
benchmark/third_party/dateutil/tz/__init__.py
vendored
Normal file
5
benchmark/third_party/dateutil/tz/__init__.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .tz import *
|
||||||
|
|
||||||
|
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
||||||
|
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz",
|
||||||
|
"enfold", "datetime_ambiguous", "datetime_exists"]
|
394
benchmark/third_party/dateutil/tz/_common.py
vendored
Normal file
394
benchmark/third_party/dateutil/tz/_common.py
vendored
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
from six import PY3
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, tzinfo
|
||||||
|
|
||||||
|
|
||||||
|
ZERO = timedelta(0)
|
||||||
|
|
||||||
|
__all__ = ['tzname_in_python2', 'enfold']
|
||||||
|
|
||||||
|
|
||||||
|
def tzname_in_python2(namefunc):
|
||||||
|
"""Change unicode output into bytestrings in Python 2
|
||||||
|
|
||||||
|
tzname() API changed in Python 3. It used to return bytes, but was changed
|
||||||
|
to unicode strings
|
||||||
|
"""
|
||||||
|
def adjust_encoding(*args, **kwargs):
|
||||||
|
name = namefunc(*args, **kwargs)
|
||||||
|
if name is not None and not PY3:
|
||||||
|
name = name.encode()
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
return adjust_encoding
|
||||||
|
|
||||||
|
|
||||||
|
# The following is adapted from Alexander Belopolsky's tz library
|
||||||
|
# https://github.com/abalkin/tz
|
||||||
|
if hasattr(datetime, 'fold'):
|
||||||
|
# This is the pre-python 3.6 fold situation
|
||||||
|
def enfold(dt, fold=1):
|
||||||
|
"""
|
||||||
|
Provides a unified interface for assigning the ``fold`` attribute to
|
||||||
|
datetimes both before and after the implementation of PEP-495.
|
||||||
|
|
||||||
|
:param fold:
|
||||||
|
The value for the ``fold`` attribute in the returned datetime. This
|
||||||
|
should be either 0 or 1.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns an object for which ``getattr(dt, 'fold', 0)`` returns
|
||||||
|
``fold`` for all versions of Python. In versions prior to
|
||||||
|
Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
|
||||||
|
subclass of :py:class:`datetime.datetime` with the ``fold``
|
||||||
|
attribute added, if ``fold`` is 1.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
return dt.replace(fold=fold)
|
||||||
|
|
||||||
|
else:
|
||||||
|
class _DatetimeWithFold(datetime):
|
||||||
|
"""
|
||||||
|
This is a class designed to provide a PEP 495-compliant interface for
|
||||||
|
Python versions before 3.6. It is used only for dates in a fold, so
|
||||||
|
the ``fold`` attribute is fixed at ``1``.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fold(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def enfold(dt, fold=1):
|
||||||
|
"""
|
||||||
|
Provides a unified interface for assigning the ``fold`` attribute to
|
||||||
|
datetimes both before and after the implementation of PEP-495.
|
||||||
|
|
||||||
|
:param fold:
|
||||||
|
The value for the ``fold`` attribute in the returned datetime. This
|
||||||
|
should be either 0 or 1.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns an object for which ``getattr(dt, 'fold', 0)`` returns
|
||||||
|
``fold`` for all versions of Python. In versions prior to
|
||||||
|
Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
|
||||||
|
subclass of :py:class:`datetime.datetime` with the ``fold``
|
||||||
|
attribute added, if ``fold`` is 1.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
if getattr(dt, 'fold', 0) == fold:
|
||||||
|
return dt
|
||||||
|
|
||||||
|
args = dt.timetuple()[:6]
|
||||||
|
args += (dt.microsecond, dt.tzinfo)
|
||||||
|
|
||||||
|
if fold:
|
||||||
|
return _DatetimeWithFold(*args)
|
||||||
|
else:
|
||||||
|
return datetime(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_fromutc_inputs(f):
|
||||||
|
"""
|
||||||
|
The CPython version of ``fromutc`` checks that the input is a ``datetime``
|
||||||
|
object and that ``self`` is attached as its ``tzinfo``.
|
||||||
|
"""
|
||||||
|
@wraps(f)
|
||||||
|
def fromutc(self, dt):
|
||||||
|
if not isinstance(dt, datetime):
|
||||||
|
raise TypeError("fromutc() requires a datetime argument")
|
||||||
|
if dt.tzinfo is not self:
|
||||||
|
raise ValueError("dt.tzinfo is not self")
|
||||||
|
|
||||||
|
return f(self, dt)
|
||||||
|
|
||||||
|
return fromutc
|
||||||
|
|
||||||
|
|
||||||
|
class _tzinfo(tzinfo):
|
||||||
|
"""
|
||||||
|
Base class for all ``dateutil`` ``tzinfo`` objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def is_ambiguous(self, dt):
|
||||||
|
"""
|
||||||
|
Whether or not the "wall time" of a given datetime is ambiguous in this
|
||||||
|
zone.
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A :py:class:`datetime.datetime`, naive or time zone aware.
|
||||||
|
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns ``True`` if ambiguous, ``False`` otherwise.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=self)
|
||||||
|
|
||||||
|
wall_0 = enfold(dt, fold=0)
|
||||||
|
wall_1 = enfold(dt, fold=1)
|
||||||
|
|
||||||
|
same_offset = wall_0.utcoffset() == wall_1.utcoffset()
|
||||||
|
same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None)
|
||||||
|
|
||||||
|
return same_dt and not same_offset
|
||||||
|
|
||||||
|
def _fold_status(self, dt_utc, dt_wall):
|
||||||
|
"""
|
||||||
|
Determine the fold status of a "wall" datetime, given a representation
|
||||||
|
of the same datetime as a (naive) UTC datetime. This is calculated based
|
||||||
|
on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all
|
||||||
|
datetimes, and that this offset is the actual number of hours separating
|
||||||
|
``dt_utc`` and ``dt_wall``.
|
||||||
|
|
||||||
|
:param dt_utc:
|
||||||
|
Representation of the datetime as UTC
|
||||||
|
|
||||||
|
:param dt_wall:
|
||||||
|
Representation of the datetime as "wall time". This parameter must
|
||||||
|
either have a `fold` attribute or have a fold-naive
|
||||||
|
:class:`datetime.tzinfo` attached, otherwise the calculation may
|
||||||
|
fail.
|
||||||
|
"""
|
||||||
|
if self.is_ambiguous(dt_wall):
|
||||||
|
delta_wall = dt_wall - dt_utc
|
||||||
|
_fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst()))
|
||||||
|
else:
|
||||||
|
_fold = 0
|
||||||
|
|
||||||
|
return _fold
|
||||||
|
|
||||||
|
def _fold(self, dt):
|
||||||
|
return getattr(dt, 'fold', 0)
|
||||||
|
|
||||||
|
def _fromutc(self, dt):
|
||||||
|
"""
|
||||||
|
Given a timezone-aware datetime in a given timezone, calculates a
|
||||||
|
timezone-aware datetime in a new timezone.
|
||||||
|
|
||||||
|
Since this is the one time that we *know* we have an unambiguous
|
||||||
|
datetime object, we take this opportunity to determine whether the
|
||||||
|
datetime is ambiguous and in a "fold" state (e.g. if it's the first
|
||||||
|
occurence, chronologically, of the ambiguous datetime).
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A timezone-aware :class:`datetime.datetime` object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Re-implement the algorithm from Python's datetime.py
|
||||||
|
dtoff = dt.utcoffset()
|
||||||
|
if dtoff is None:
|
||||||
|
raise ValueError("fromutc() requires a non-None utcoffset() "
|
||||||
|
"result")
|
||||||
|
|
||||||
|
# The original datetime.py code assumes that `dst()` defaults to
|
||||||
|
# zero during ambiguous times. PEP 495 inverts this presumption, so
|
||||||
|
# for pre-PEP 495 versions of python, we need to tweak the algorithm.
|
||||||
|
dtdst = dt.dst()
|
||||||
|
if dtdst is None:
|
||||||
|
raise ValueError("fromutc() requires a non-None dst() result")
|
||||||
|
delta = dtoff - dtdst
|
||||||
|
|
||||||
|
dt += delta
|
||||||
|
# Set fold=1 so we can default to being in the fold for
|
||||||
|
# ambiguous dates.
|
||||||
|
dtdst = enfold(dt, fold=1).dst()
|
||||||
|
if dtdst is None:
|
||||||
|
raise ValueError("fromutc(): dt.dst gave inconsistent "
|
||||||
|
"results; cannot convert")
|
||||||
|
return dt + dtdst
|
||||||
|
|
||||||
|
@_validate_fromutc_inputs
|
||||||
|
def fromutc(self, dt):
|
||||||
|
"""
|
||||||
|
Given a timezone-aware datetime in a given timezone, calculates a
|
||||||
|
timezone-aware datetime in a new timezone.
|
||||||
|
|
||||||
|
Since this is the one time that we *know* we have an unambiguous
|
||||||
|
datetime object, we take this opportunity to determine whether the
|
||||||
|
datetime is ambiguous and in a "fold" state (e.g. if it's the first
|
||||||
|
occurance, chronologically, of the ambiguous datetime).
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A timezone-aware :class:`datetime.datetime` object.
|
||||||
|
"""
|
||||||
|
dt_wall = self._fromutc(dt)
|
||||||
|
|
||||||
|
# Calculate the fold status given the two datetimes.
|
||||||
|
_fold = self._fold_status(dt, dt_wall)
|
||||||
|
|
||||||
|
# Set the default fold value for ambiguous dates
|
||||||
|
return enfold(dt_wall, fold=_fold)
|
||||||
|
|
||||||
|
|
||||||
|
class tzrangebase(_tzinfo):
|
||||||
|
"""
|
||||||
|
This is an abstract base class for time zones represented by an annual
|
||||||
|
transition into and out of DST. Child classes should implement the following
|
||||||
|
methods:
|
||||||
|
|
||||||
|
* ``__init__(self, *args, **kwargs)``
|
||||||
|
* ``transitions(self, year)`` - this is expected to return a tuple of
|
||||||
|
datetimes representing the DST on and off transitions in standard
|
||||||
|
time.
|
||||||
|
|
||||||
|
A fully initialized ``tzrangebase`` subclass should also provide the
|
||||||
|
following attributes:
|
||||||
|
* ``hasdst``: Boolean whether or not the zone uses DST.
|
||||||
|
* ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects
|
||||||
|
representing the respective UTC offsets.
|
||||||
|
* ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short
|
||||||
|
abbreviations in DST and STD, respectively.
|
||||||
|
* ``_hasdst``: Whether or not the zone has DST.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
raise NotImplementedError('tzrangebase is an abstract base class')
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
isdst = self._isdst(dt)
|
||||||
|
|
||||||
|
if isdst is None:
|
||||||
|
return None
|
||||||
|
elif isdst:
|
||||||
|
return self._dst_offset
|
||||||
|
else:
|
||||||
|
return self._std_offset
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
isdst = self._isdst(dt)
|
||||||
|
|
||||||
|
if isdst is None:
|
||||||
|
return None
|
||||||
|
elif isdst:
|
||||||
|
return self._dst_base_offset
|
||||||
|
else:
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
@tzname_in_python2
|
||||||
|
def tzname(self, dt):
|
||||||
|
if self._isdst(dt):
|
||||||
|
return self._dst_abbr
|
||||||
|
else:
|
||||||
|
return self._std_abbr
|
||||||
|
|
||||||
|
def fromutc(self, dt):
|
||||||
|
""" Given a datetime in UTC, return local time """
|
||||||
|
if not isinstance(dt, datetime):
|
||||||
|
raise TypeError("fromutc() requires a datetime argument")
|
||||||
|
|
||||||
|
if dt.tzinfo is not self:
|
||||||
|
raise ValueError("dt.tzinfo is not self")
|
||||||
|
|
||||||
|
# Get transitions - if there are none, fixed offset
|
||||||
|
transitions = self.transitions(dt.year)
|
||||||
|
if transitions is None:
|
||||||
|
return dt + self.utcoffset(dt)
|
||||||
|
|
||||||
|
# Get the transition times in UTC
|
||||||
|
dston, dstoff = transitions
|
||||||
|
|
||||||
|
dston -= self._std_offset
|
||||||
|
dstoff -= self._std_offset
|
||||||
|
|
||||||
|
utc_transitions = (dston, dstoff)
|
||||||
|
dt_utc = dt.replace(tzinfo=None)
|
||||||
|
|
||||||
|
isdst = self._naive_isdst(dt_utc, utc_transitions)
|
||||||
|
|
||||||
|
if isdst:
|
||||||
|
dt_wall = dt + self._dst_offset
|
||||||
|
else:
|
||||||
|
dt_wall = dt + self._std_offset
|
||||||
|
|
||||||
|
_fold = int(not isdst and self.is_ambiguous(dt_wall))
|
||||||
|
|
||||||
|
return enfold(dt_wall, fold=_fold)
|
||||||
|
|
||||||
|
def is_ambiguous(self, dt):
|
||||||
|
"""
|
||||||
|
Whether or not the "wall time" of a given datetime is ambiguous in this
|
||||||
|
zone.
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A :py:class:`datetime.datetime`, naive or time zone aware.
|
||||||
|
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns ``True`` if ambiguous, ``False`` otherwise.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
if not self.hasdst:
|
||||||
|
return False
|
||||||
|
|
||||||
|
start, end = self.transitions(dt.year)
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
return (end <= dt < end + self._dst_base_offset)
|
||||||
|
|
||||||
|
def _isdst(self, dt):
|
||||||
|
if not self.hasdst:
|
||||||
|
return False
|
||||||
|
elif dt is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
transitions = self.transitions(dt.year)
|
||||||
|
|
||||||
|
if transitions is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
|
||||||
|
isdst = self._naive_isdst(dt, transitions)
|
||||||
|
|
||||||
|
# Handle ambiguous dates
|
||||||
|
if not isdst and self.is_ambiguous(dt):
|
||||||
|
return not self._fold(dt)
|
||||||
|
else:
|
||||||
|
return isdst
|
||||||
|
|
||||||
|
def _naive_isdst(self, dt, transitions):
|
||||||
|
dston, dstoff = transitions
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
|
||||||
|
if dston < dstoff:
|
||||||
|
isdst = dston <= dt < dstoff
|
||||||
|
else:
|
||||||
|
isdst = not dstoff <= dt < dston
|
||||||
|
|
||||||
|
return isdst
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _dst_base_offset(self):
|
||||||
|
return self._dst_offset - self._std_offset
|
||||||
|
|
||||||
|
__hash__ = None
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not (self == other)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(...)" % self.__class__.__name__
|
||||||
|
|
||||||
|
__reduce__ = object.__reduce__
|
||||||
|
|
||||||
|
|
||||||
|
def _total_seconds(td):
|
||||||
|
# Python 2.6 doesn't have a total_seconds() method on timedelta objects
|
||||||
|
return ((td.seconds + td.days * 86400) * 1000000 +
|
||||||
|
td.microseconds) // 1000000
|
||||||
|
|
||||||
|
|
||||||
|
_total_seconds = getattr(timedelta, 'total_seconds', _total_seconds)
|
1511
benchmark/third_party/dateutil/tz/tz.py
vendored
Normal file
1511
benchmark/third_party/dateutil/tz/tz.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
332
benchmark/third_party/dateutil/tz/win.py
vendored
Normal file
332
benchmark/third_party/dateutil/tz/win.py
vendored
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
# This code was originally contributed by Jeffrey Harris.
|
||||||
|
import datetime
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from six.moves import winreg
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
from ctypes import wintypes
|
||||||
|
except ValueError:
|
||||||
|
# ValueError is raised on non-Windows systems for some horrible reason.
|
||||||
|
raise ImportError("Running tzwin on non-Windows system")
|
||||||
|
|
||||||
|
from ._common import tzrangebase
|
||||||
|
|
||||||
|
__all__ = ["tzwin", "tzwinlocal", "tzres"]
|
||||||
|
|
||||||
|
ONEWEEK = datetime.timedelta(7)
|
||||||
|
|
||||||
|
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
||||||
|
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
||||||
|
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
||||||
|
|
||||||
|
|
||||||
|
def _settzkeyname():
|
||||||
|
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||||
|
try:
|
||||||
|
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
||||||
|
TZKEYNAME = TZKEYNAMENT
|
||||||
|
except WindowsError:
|
||||||
|
TZKEYNAME = TZKEYNAME9X
|
||||||
|
handle.Close()
|
||||||
|
return TZKEYNAME
|
||||||
|
|
||||||
|
|
||||||
|
TZKEYNAME = _settzkeyname()
|
||||||
|
|
||||||
|
|
||||||
|
class tzres(object):
|
||||||
|
"""
|
||||||
|
Class for accessing `tzres.dll`, which contains timezone name related
|
||||||
|
resources.
|
||||||
|
|
||||||
|
.. versionadded:: 2.5.0
|
||||||
|
"""
|
||||||
|
p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char
|
||||||
|
|
||||||
|
def __init__(self, tzres_loc='tzres.dll'):
|
||||||
|
# Load the user32 DLL so we can load strings from tzres
|
||||||
|
user32 = ctypes.WinDLL('user32')
|
||||||
|
|
||||||
|
# Specify the LoadStringW function
|
||||||
|
user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
|
||||||
|
wintypes.UINT,
|
||||||
|
wintypes.LPWSTR,
|
||||||
|
ctypes.c_int)
|
||||||
|
|
||||||
|
self.LoadStringW = user32.LoadStringW
|
||||||
|
self._tzres = ctypes.WinDLL(tzres_loc)
|
||||||
|
self.tzres_loc = tzres_loc
|
||||||
|
|
||||||
|
def load_name(self, offset):
|
||||||
|
"""
|
||||||
|
Load a timezone name from a DLL offset (integer).
|
||||||
|
|
||||||
|
>>> from dateutil.tzwin import tzres
|
||||||
|
>>> tzr = tzres()
|
||||||
|
>>> print(tzr.load_name(112))
|
||||||
|
'Eastern Standard Time'
|
||||||
|
|
||||||
|
:param offset:
|
||||||
|
A positive integer value referring to a string from the tzres dll.
|
||||||
|
|
||||||
|
..note:
|
||||||
|
Offsets found in the registry are generally of the form
|
||||||
|
`@tzres.dll,-114`. The offset in this case if 114, not -114.
|
||||||
|
|
||||||
|
"""
|
||||||
|
resource = self.p_wchar()
|
||||||
|
lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
|
||||||
|
nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
|
||||||
|
return resource[:nchar]
|
||||||
|
|
||||||
|
def name_from_string(self, tzname_str):
|
||||||
|
"""
|
||||||
|
Parse strings as returned from the Windows registry into the time zone
|
||||||
|
name as defined in the registry.
|
||||||
|
|
||||||
|
>>> from dateutil.tzwin import tzres
|
||||||
|
>>> tzr = tzres()
|
||||||
|
>>> print(tzr.name_from_string('@tzres.dll,-251'))
|
||||||
|
'Dateline Daylight Time'
|
||||||
|
>>> print(tzr.name_from_string('Eastern Standard Time'))
|
||||||
|
'Eastern Standard Time'
|
||||||
|
|
||||||
|
:param tzname_str:
|
||||||
|
A timezone name string as returned from a Windows registry key.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns the localized timezone string from tzres.dll if the string
|
||||||
|
is of the form `@tzres.dll,-offset`, else returns the input string.
|
||||||
|
"""
|
||||||
|
if not tzname_str.startswith('@'):
|
||||||
|
return tzname_str
|
||||||
|
|
||||||
|
name_splt = tzname_str.split(',-')
|
||||||
|
try:
|
||||||
|
offset = int(name_splt[1])
|
||||||
|
except:
|
||||||
|
raise ValueError("Malformed timezone string.")
|
||||||
|
|
||||||
|
return self.load_name(offset)
|
||||||
|
|
||||||
|
|
||||||
|
class tzwinbase(tzrangebase):
|
||||||
|
"""tzinfo class based on win32's timezones available in the registry."""
|
||||||
|
def __init__(self):
|
||||||
|
raise NotImplementedError('tzwinbase is an abstract base class')
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
# Compare on all relevant dimensions, including name.
|
||||||
|
if not isinstance(other, tzwinbase):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
return (self._std_offset == other._std_offset and
|
||||||
|
self._dst_offset == other._dst_offset and
|
||||||
|
self._stddayofweek == other._stddayofweek and
|
||||||
|
self._dstdayofweek == other._dstdayofweek and
|
||||||
|
self._stdweeknumber == other._stdweeknumber and
|
||||||
|
self._dstweeknumber == other._dstweeknumber and
|
||||||
|
self._stdhour == other._stdhour and
|
||||||
|
self._dsthour == other._dsthour and
|
||||||
|
self._stdminute == other._stdminute and
|
||||||
|
self._dstminute == other._dstminute and
|
||||||
|
self._std_abbr == other._std_abbr and
|
||||||
|
self._dst_abbr == other._dst_abbr)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list():
|
||||||
|
"""Return a list of all time zones known to the system."""
|
||||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
|
with winreg.OpenKey(handle, TZKEYNAME) as tzkey:
|
||||||
|
result = [winreg.EnumKey(tzkey, i)
|
||||||
|
for i in range(winreg.QueryInfoKey(tzkey)[0])]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def display(self):
|
||||||
|
return self._display
|
||||||
|
|
||||||
|
def transitions(self, year):
|
||||||
|
"""
|
||||||
|
For a given year, get the DST on and off transition times, expressed
|
||||||
|
always on the standard time side. For zones with no transitions, this
|
||||||
|
function returns ``None``.
|
||||||
|
|
||||||
|
:param year:
|
||||||
|
The year whose transitions you would like to query.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`tuple` of :class:`datetime.datetime` objects,
|
||||||
|
``(dston, dstoff)`` for zones with an annual DST transition, or
|
||||||
|
``None`` for fixed offset zones.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.hasdst:
|
||||||
|
return None
|
||||||
|
|
||||||
|
dston = picknthweekday(year, self._dstmonth, self._dstdayofweek,
|
||||||
|
self._dsthour, self._dstminute,
|
||||||
|
self._dstweeknumber)
|
||||||
|
|
||||||
|
dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek,
|
||||||
|
self._stdhour, self._stdminute,
|
||||||
|
self._stdweeknumber)
|
||||||
|
|
||||||
|
# Ambiguous dates default to the STD side
|
||||||
|
dstoff -= self._dst_base_offset
|
||||||
|
|
||||||
|
return dston, dstoff
|
||||||
|
|
||||||
|
def _get_hasdst(self):
|
||||||
|
return self._dstmonth != 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _dst_base_offset(self):
|
||||||
|
return self._dst_base_offset_
|
||||||
|
|
||||||
|
|
||||||
|
class tzwin(tzwinbase):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
# multiple contexts only possible in 2.7 and 3.1, we still support 2.6
|
||||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
|
tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name)
|
||||||
|
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
||||||
|
keydict = valuestodict(tzkey)
|
||||||
|
|
||||||
|
self._std_abbr = keydict["Std"]
|
||||||
|
self._dst_abbr = keydict["Dlt"]
|
||||||
|
|
||||||
|
self._display = keydict["Display"]
|
||||||
|
|
||||||
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
||||||
|
tup = struct.unpack("=3l16h", keydict["TZI"])
|
||||||
|
stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
||||||
|
dstoffset = stdoffset-tup[2] # + DaylightBias * -1
|
||||||
|
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
||||||
|
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
||||||
|
|
||||||
|
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
|
||||||
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
|
||||||
|
(self._stdmonth,
|
||||||
|
self._stddayofweek, # Sunday = 0
|
||||||
|
self._stdweeknumber, # Last = 5
|
||||||
|
self._stdhour,
|
||||||
|
self._stdminute) = tup[4:9]
|
||||||
|
|
||||||
|
(self._dstmonth,
|
||||||
|
self._dstdayofweek, # Sunday = 0
|
||||||
|
self._dstweeknumber, # Last = 5
|
||||||
|
self._dsthour,
|
||||||
|
self._dstminute) = tup[12:17]
|
||||||
|
|
||||||
|
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
||||||
|
self.hasdst = self._get_hasdst()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "tzwin(%s)" % repr(self._name)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
return (self.__class__, (self._name,))
|
||||||
|
|
||||||
|
|
||||||
|
class tzwinlocal(tzwinbase):
|
||||||
|
def __init__(self):
|
||||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
|
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||||
|
keydict = valuestodict(tzlocalkey)
|
||||||
|
|
||||||
|
self._std_abbr = keydict["StandardName"]
|
||||||
|
self._dst_abbr = keydict["DaylightName"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME,
|
||||||
|
sn=self._std_abbr)
|
||||||
|
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
||||||
|
_keydict = valuestodict(tzkey)
|
||||||
|
self._display = _keydict["Display"]
|
||||||
|
except OSError:
|
||||||
|
self._display = None
|
||||||
|
|
||||||
|
stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
||||||
|
dstoffset = stdoffset-keydict["DaylightBias"]
|
||||||
|
|
||||||
|
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
||||||
|
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
||||||
|
|
||||||
|
# For reasons unclear, in this particular key, the day of week has been
|
||||||
|
# moved to the END of the SYSTEMTIME structure.
|
||||||
|
tup = struct.unpack("=8h", keydict["StandardStart"])
|
||||||
|
|
||||||
|
(self._stdmonth,
|
||||||
|
self._stdweeknumber, # Last = 5
|
||||||
|
self._stdhour,
|
||||||
|
self._stdminute) = tup[1:5]
|
||||||
|
|
||||||
|
self._stddayofweek = tup[7]
|
||||||
|
|
||||||
|
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
||||||
|
|
||||||
|
(self._dstmonth,
|
||||||
|
self._dstweeknumber, # Last = 5
|
||||||
|
self._dsthour,
|
||||||
|
self._dstminute) = tup[1:5]
|
||||||
|
|
||||||
|
self._dstdayofweek = tup[7]
|
||||||
|
|
||||||
|
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
||||||
|
self.hasdst = self._get_hasdst()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "tzwinlocal()"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# str will return the standard name, not the daylight name.
|
||||||
|
return "tzwinlocal(%s)" % repr(self._std_abbr)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
return (self.__class__, ())
|
||||||
|
|
||||||
|
|
||||||
|
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
||||||
|
""" dayofweek == 0 means Sunday, whichweek 5 means last instance """
|
||||||
|
first = datetime.datetime(year, month, 1, hour, minute)
|
||||||
|
|
||||||
|
# This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6),
|
||||||
|
# Because 7 % 7 = 0
|
||||||
|
weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1)
|
||||||
|
wd = weekdayone + ((whichweek - 1) * ONEWEEK)
|
||||||
|
if (wd.month != month):
|
||||||
|
wd -= ONEWEEK
|
||||||
|
|
||||||
|
return wd
|
||||||
|
|
||||||
|
|
||||||
|
def valuestodict(key):
|
||||||
|
"""Convert a registry key's values to a dictionary."""
|
||||||
|
dout = {}
|
||||||
|
size = winreg.QueryInfoKey(key)[1]
|
||||||
|
tz_res = None
|
||||||
|
|
||||||
|
for i in range(size):
|
||||||
|
key_name, value, dtype = winreg.EnumValue(key, i)
|
||||||
|
if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN:
|
||||||
|
# If it's a DWORD (32-bit integer), it's stored as unsigned - convert
|
||||||
|
# that to a proper signed integer
|
||||||
|
if value & (1 << 31):
|
||||||
|
value = value - (1 << 32)
|
||||||
|
elif dtype == winreg.REG_SZ:
|
||||||
|
# If it's a reference to the tzres DLL, load the actual string
|
||||||
|
if value.startswith('@tzres'):
|
||||||
|
tz_res = tz_res or tzres()
|
||||||
|
value = tz_res.name_from_string(value)
|
||||||
|
|
||||||
|
value = value.rstrip('\x00') # Remove trailing nulls
|
||||||
|
|
||||||
|
dout[key_name] = value
|
||||||
|
|
||||||
|
return dout
|
2
benchmark/third_party/dateutil/tzwin.py
vendored
Normal file
2
benchmark/third_party/dateutil/tzwin.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# tzwin has moved to dateutil.tz.win
|
||||||
|
from .tz.win import *
|
183
benchmark/third_party/dateutil/zoneinfo/__init__.py
vendored
Normal file
183
benchmark/third_party/dateutil/zoneinfo/__init__.py
vendored
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import warnings
|
||||||
|
import json
|
||||||
|
|
||||||
|
from tarfile import TarFile
|
||||||
|
from pkgutil import get_data
|
||||||
|
from io import BytesIO
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
from dateutil.tz import tzfile
|
||||||
|
|
||||||
|
__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata", "rebuild"]
|
||||||
|
|
||||||
|
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||||
|
METADATA_FN = 'METADATA'
|
||||||
|
|
||||||
|
# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but
|
||||||
|
# it's close enough for python2.6
|
||||||
|
tar_open = TarFile.open
|
||||||
|
if not hasattr(TarFile, '__exit__'):
|
||||||
|
def tar_open(*args, **kwargs):
|
||||||
|
return closing(TarFile.open(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
class tzfile(tzfile):
|
||||||
|
def __reduce__(self):
|
||||||
|
return (gettz, (self._filename,))
|
||||||
|
|
||||||
|
|
||||||
|
def getzoneinfofile_stream():
|
||||||
|
try:
|
||||||
|
return BytesIO(get_data(__name__, ZONEFILENAME))
|
||||||
|
except IOError as e: # TODO switch to FileNotFoundError?
|
||||||
|
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ZoneInfoFile(object):
|
||||||
|
def __init__(self, zonefile_stream=None):
|
||||||
|
if zonefile_stream is not None:
|
||||||
|
with tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
||||||
|
# dict comprehension does not work on python2.6
|
||||||
|
# TODO: get back to the nicer syntax when we ditch python2.6
|
||||||
|
# self.zones = {zf.name: tzfile(tf.extractfile(zf),
|
||||||
|
# filename = zf.name)
|
||||||
|
# for zf in tf.getmembers() if zf.isfile()}
|
||||||
|
self.zones = dict((zf.name, tzfile(tf.extractfile(zf),
|
||||||
|
filename=zf.name))
|
||||||
|
for zf in tf.getmembers()
|
||||||
|
if zf.isfile() and zf.name != METADATA_FN)
|
||||||
|
# deal with links: They'll point to their parent object. Less
|
||||||
|
# waste of memory
|
||||||
|
# links = {zl.name: self.zones[zl.linkname]
|
||||||
|
# for zl in tf.getmembers() if zl.islnk() or zl.issym()}
|
||||||
|
links = dict((zl.name, self.zones[zl.linkname])
|
||||||
|
for zl in tf.getmembers() if
|
||||||
|
zl.islnk() or zl.issym())
|
||||||
|
self.zones.update(links)
|
||||||
|
try:
|
||||||
|
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
||||||
|
metadata_str = metadata_json.read().decode('UTF-8')
|
||||||
|
self.metadata = json.loads(metadata_str)
|
||||||
|
except KeyError:
|
||||||
|
# no metadata in tar file
|
||||||
|
self.metadata = None
|
||||||
|
else:
|
||||||
|
self.zones = dict()
|
||||||
|
self.metadata = None
|
||||||
|
|
||||||
|
def get(self, name, default=None):
|
||||||
|
"""
|
||||||
|
Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method
|
||||||
|
for retrieving zones from the zone dictionary.
|
||||||
|
|
||||||
|
:param name:
|
||||||
|
The name of the zone to retrieve. (Generally IANA zone names)
|
||||||
|
|
||||||
|
:param default:
|
||||||
|
The value to return in the event of a missing key.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.zones.get(name, default)
|
||||||
|
|
||||||
|
|
||||||
|
# The current API has gettz as a module function, although in fact it taps into
|
||||||
|
# a stateful class. So as a workaround for now, without changing the API, we
|
||||||
|
# will create a new "global" class instance the first time a user requests a
|
||||||
|
# timezone. Ugly, but adheres to the api.
|
||||||
|
#
|
||||||
|
# TODO: Remove after deprecation period.
|
||||||
|
_CLASS_ZONE_INSTANCE = list()
|
||||||
|
|
||||||
|
|
||||||
|
def get_zonefile_instance(new_instance=False):
|
||||||
|
"""
|
||||||
|
This is a convenience function which provides a :class:`ZoneInfoFile`
|
||||||
|
instance using the data provided by the ``dateutil`` package. By default, it
|
||||||
|
caches a single instance of the ZoneInfoFile object and returns that.
|
||||||
|
|
||||||
|
:param new_instance:
|
||||||
|
If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and
|
||||||
|
used as the cached instance for the next call. Otherwise, new instances
|
||||||
|
are created only as necessary.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`ZoneInfoFile` object.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
"""
|
||||||
|
if new_instance:
|
||||||
|
zif = None
|
||||||
|
else:
|
||||||
|
zif = getattr(get_zonefile_instance, '_cached_instance', None)
|
||||||
|
|
||||||
|
if zif is None:
|
||||||
|
zif = ZoneInfoFile(getzoneinfofile_stream())
|
||||||
|
|
||||||
|
get_zonefile_instance._cached_instance = zif
|
||||||
|
|
||||||
|
return zif
|
||||||
|
|
||||||
|
|
||||||
|
def gettz(name):
|
||||||
|
"""
|
||||||
|
This retrieves a time zone from the local zoneinfo tarball that is packaged
|
||||||
|
with dateutil.
|
||||||
|
|
||||||
|
:param name:
|
||||||
|
An IANA-style time zone name, as found in the zoneinfo file.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`dateutil.tz.tzfile` time zone object.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
It is generally inadvisable to use this function, and it is only
|
||||||
|
provided for API compatibility with earlier versions. This is *not*
|
||||||
|
equivalent to ``dateutil.tz.gettz()``, which selects an appropriate
|
||||||
|
time zone based on the inputs, favoring system zoneinfo. This is ONLY
|
||||||
|
for accessing the dateutil-specific zoneinfo (which may be out of
|
||||||
|
date compared to the system zoneinfo).
|
||||||
|
|
||||||
|
.. deprecated:: 2.6
|
||||||
|
If you need to use a specific zoneinfofile over the system zoneinfo,
|
||||||
|
instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call
|
||||||
|
:func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead.
|
||||||
|
|
||||||
|
Use :func:`get_zonefile_instance` to retrieve an instance of the
|
||||||
|
dateutil-provided zoneinfo.
|
||||||
|
"""
|
||||||
|
warnings.warn("zoneinfo.gettz() will be removed in future versions, "
|
||||||
|
"to use the dateutil-provided zoneinfo files, instantiate a "
|
||||||
|
"ZoneInfoFile object and use ZoneInfoFile.zones.get() "
|
||||||
|
"instead. See the documentation for details.",
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||||
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||||
|
return _CLASS_ZONE_INSTANCE[0].zones.get(name)
|
||||||
|
|
||||||
|
|
||||||
|
def gettz_db_metadata():
|
||||||
|
""" Get the zonefile metadata
|
||||||
|
|
||||||
|
See `zonefile_metadata`_
|
||||||
|
|
||||||
|
:returns:
|
||||||
|
A dictionary with the database metadata
|
||||||
|
|
||||||
|
.. deprecated:: 2.6
|
||||||
|
See deprecation warning in :func:`zoneinfo.gettz`. To get metadata,
|
||||||
|
query the attribute ``zoneinfo.ZoneInfoFile.metadata``.
|
||||||
|
"""
|
||||||
|
warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future "
|
||||||
|
"versions, to use the dateutil-provided zoneinfo files, "
|
||||||
|
"ZoneInfoFile object and query the 'metadata' attribute "
|
||||||
|
"instead. See the documentation for details.",
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||||
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||||
|
return _CLASS_ZONE_INSTANCE[0].metadata
|
BIN
benchmark/third_party/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz
vendored
Normal file
BIN
benchmark/third_party/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz
vendored
Normal file
Binary file not shown.
52
benchmark/third_party/dateutil/zoneinfo/rebuild.py
vendored
Normal file
52
benchmark/third_party/dateutil/zoneinfo/rebuild.py
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
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:
|
||||||
|
_print_on_nosuchfile(e)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def _print_on_nosuchfile(e):
|
||||||
|
"""Print helpful troubleshooting message
|
||||||
|
|
||||||
|
e is an exception raised by subprocess.check_call()
|
||||||
|
|
||||||
|
"""
|
||||||
|
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?")
|
Reference in New Issue
Block a user