summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/lint.yml12
-rw-r--r--.github/workflows/test.yml46
-rw-r--r--.gitignore1
-rw-r--r--.pre-commit-config.yaml26
-rw-r--r--.travis.yml24
-rw-r--r--CHANGES.txt18
-rw-r--r--LICENSE26
-rw-r--r--README.rst18
-rw-r--r--TODO.txt2
-rw-r--r--setup.cfg2
-rw-r--r--setup.py106
-rw-r--r--src/isodate/__init__.py95
-rw-r--r--src/isodate/duration.py178
-rw-r--r--src/isodate/isodates.py171
-rw-r--r--src/isodate/isodatetime.py51
-rw-r--r--src/isodate/isoduration.py129
-rw-r--r--src/isodate/isoerror.py32
-rw-r--r--src/isodate/isostrf.py222
-rw-r--r--src/isodate/isotime.py138
-rw-r--r--src/isodate/isotzinfo.py90
-rw-r--r--src/isodate/tests/__init__.py69
-rw-r--r--src/isodate/tests/test_date.py130
-rw-r--r--src/isodate/tests/test_datetime.py224
-rw-r--r--src/isodate/tests/test_duration.py635
-rw-r--r--src/isodate/tests/test_pickle.py35
-rw-r--r--src/isodate/tests/test_strf.py125
-rw-r--r--src/isodate/tests/test_time.py184
-rw-r--r--src/isodate/tzinfo.py82
-rw-r--r--tox.ini23
29 files changed, 1392 insertions, 1502 deletions
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..769ea4e
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,12 @@
+name: Lint
+
+on: [push, pull_request, workflow_dispatch]
+
+jobs:
+ lint:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ - uses: pre-commit/action@v2.0.3
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..edf58a9
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,46 @@
+name: Test
+
+on: [push, pull_request, workflow_dispatch]
+
+env:
+ FORCE_COLOR: 1
+
+jobs:
+ test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["pypy-3.8", "3.7", "3.8", "3.9", "3.10"]
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ include:
+ # Include new variables for Codecov
+ - { codecov-flag: GHA_Ubuntu, os: ubuntu-latest }
+ - { codecov-flag: GHA_macOS, os: macos-latest }
+ - { codecov-flag: GHA_Windows, os: windows-latest }
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ cache: pip
+ cache-dependency-path: "setup.py"
+
+ - name: Install dependencies
+ run: |
+ python -m pip install -U pip
+ python -m pip install -U wheel
+ python -m pip install -U tox
+
+ - name: Tox tests
+ run: |
+ tox -e py
+
+ - name: Upload coverage
+ uses: codecov/codecov-action@v2
+ with:
+ flags: ${{ matrix.codecov-flag }}
+ name: ${{ matrix.os }} Python ${{ matrix.python-version }}
diff --git a/.gitignore b/.gitignore
index 8b04075..fdacade 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,5 @@ develop-eggs
dist
parts
.coverage
+coverage.xml
htmlcov
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..1f04e3f
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,26 @@
+repos:
+ - repo: https://github.com/asottile/pyupgrade
+ rev: v2.29.1
+ hooks:
+ - id: pyupgrade
+ args: [--py37-plus]
+
+ - repo: https://github.com/pycqa/flake8
+ rev: 4.0.1
+ hooks:
+ - id: flake8
+ args:
+ - "--max-line-length=88"
+
+ - repo: https://github.com/pre-commit/pygrep-hooks
+ rev: v1.9.0
+ hooks:
+ - id: python-check-blanket-noqa
+
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.0.1
+ hooks:
+ - id: check-merge-conflict
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ab80767..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: python
-matrix:
- include:
- - python: 2.7
- env: TOXENV=py27
- - python: 3.4
- env: TOXENV=py34
- - python: 3.5
- env: TOXENV=py35
- - python: 3.6
- env: TOXENV=py36
- - python: 3.5
- env: TOXENV=flake
- - python: 3.5
- env: TOXENV=cover
-install:
- - pip install tox
-script:
- - tox
-after_script:
- - if [ $TOXENV == "cover" ]; then
- pip install --quiet coveralls;
- coveralls;
- fi
diff --git a/CHANGES.txt b/CHANGES.txt
index e55bd37..c526264 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -2,10 +2,20 @@
CHANGES
=======
-0.7.0.dev (unreleased)
-----------------------
+0.7.0 (unreleased)
+------------------
+
+- drop end of life python versions
+- Don't match garbage characters at the end of parsed strings #16 (Gabriel de Perthuis)
+- Breaking: fractional seconds are cut off to microseconds (round down)
+- Allow control over return type of parse_duration #64 (Felix Claessen)
+
+
+0.6.1 (2021-12-13)
+------------------
-- no changes yet
+- support python 3.10 (Hugo van Kemenade)
+- last version to support py 2.7
0.6.0 (2017-10-13)
@@ -80,7 +90,7 @@ CHANGES
0.4.3 (2010-10-29)
------------------
-- Fixed problem with %P formating and fractions (supplied by David Brooks)
+- Fixed problem with %P formatting and fractions (supplied by David Brooks)
0.4.2 (2010-10-28)
------------------
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ccd1d66
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2021, Hugo van Kemenade and contributors
+Copyright (c) 2009-2018, Gerhard Weis and contributors
+Copyright (c) 2009, Gerhard Weis
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.rst b/README.rst
index 5b41162..d030a4a 100644
--- a/README.rst
+++ b/README.rst
@@ -9,10 +9,10 @@ ISO 8601 date/time parser
:target: https://coveralls.io/r/gweis/isodate?branch=master
:alt: Coveralls
.. image:: https://img.shields.io/pypi/v/isodate.svg
- :target: https://pypi.python.org/pypi/isodate/
+ :target: https://pypi.python.org/pypi/isodate/
:alt: Latest Version
.. image:: https://img.shields.io/pypi/l/isodate.svg
- :target: https://pypi.python.org/pypi/isodate/
+ :target: https://pypi.python.org/pypi/isodate/
:alt: License
@@ -38,7 +38,7 @@ for instance nanoseconds it will round it to microseconds.
Documentation
-------------
-Currently there are four parsing methods available.
+The following parsing methods are available.
* parse_time:
parses an ISO 8601 time string into a *time* object
* parse_date:
@@ -83,16 +83,16 @@ always the ISO 8601 expanded format. This is the same format used by
prior 1900. This method also understands how to format *datetime* and
*Duration* instances.
-Installation:
--------------
+Installation
+------------
This module can easily be installed with Python standard installation methods.
Either use *python setup.py install* or in case you have *setuptools* or
*distribute* available, you can also use *easy_install*.
-Limitations:
-------------
+Limitations
+-----------
* The parser accepts several date/time representation which should be invalid
according to ISO 8601 standard.
@@ -104,8 +104,8 @@ Limitations:
1901-01-01.
3. negative *Duration* and *timedelta* value are not fully supported yet.
-Further information:
---------------------
+Further information
+-------------------
The doc strings and unit tests should provide rather detailed information about
the methods and their limitations.
diff --git a/TODO.txt b/TODO.txt
index 6843fa3..b72a651 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -9,7 +9,7 @@ not complete.
Missing features:
-----------------
- * time formating does not allow to create fractional representations.
+ * time formatting does not allow to create fractional representations.
* parser for ISO intervals.
* currently microseconds are always padded to a length of 6 characters.
trailing 0s should be optional
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 2a9acf1..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[bdist_wheel]
-universal = 1
diff --git a/setup.py b/setup.py
index e39446f..f95e8c2 100644
--- a/setup.py
+++ b/setup.py
@@ -1,75 +1,43 @@
#!/usr/bin/env python
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
import os
from setuptools import setup
def read(*rnames):
- return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-
-
-setup(name='isodate',
- version='0.6.0.dev',
- packages=['isodate', 'isodate.tests'],
- package_dir={'': 'src'},
-
- # dependencies:
- install_requires=[
- 'six'
- ],
-
- # PyPI metadata
- author='Gerhard Weis',
- author_email='gerhard.weis@proclos.com',
- description='An ISO 8601 date/time/duration parser and formatter',
- license='BSD',
- # keywords = '',
- url='https://github.com/gweis/isodate/',
-
- long_description=(read('README.rst') +
- read('CHANGES.txt') +
- read('TODO.txt')),
-
- classifiers=['Development Status :: 4 - Beta',
- # 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: Implementation :: PyPy',
- 'Topic :: Internet',
- ('Topic :: Software Development :'
- ': Libraries :: Python Modules'),
- ],
- test_suite='isodate.tests.test_suite')
+ with open(os.path.join(os.path.dirname(__file__), *rnames)) as read_file:
+ return read_file.read()
+
+
+setup(
+ name="isodate",
+ version="0.7.0.dev0",
+ packages=["isodate", "isodate.tests"],
+ package_dir={"": "src"},
+ # PyPI metadata
+ author="Gerhard Weis",
+ author_email="gerhard.weis@proclos.com",
+ description="An ISO 8601 date/time/duration parser and formatter",
+ license="BSD-3-Clause",
+ license_files=("LICENSE",),
+ # keywords = '',
+ url="https://github.com/gweis/isodate/",
+ long_description=(read("README.rst") + read("CHANGES.txt") + read("TODO.txt")),
+ python_requires=">=3.7",
+ classifiers=[
+ "Development Status :: 4 - Beta",
+ # 'Environment :: Web Environment',
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Internet",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ ],
+ test_suite="isodate.tests.test_suite",
+)
diff --git a/src/isodate/__init__.py b/src/isodate/__init__.py
index 50862e1..3b1e147 100644
--- a/src/isodate/__init__.py
+++ b/src/isodate/__init__.py
@@ -1,35 +1,9 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Import all essential functions and constants to re-export them here for easy
access.
This module contains also various pre-defined ISO 8601 format strings.
-'''
+"""
from isodate.isodates import parse_date, date_isoformat
from isodate.isotime import parse_time, time_isoformat
from isodate.isodatetime import parse_datetime, datetime_isoformat
@@ -55,18 +29,53 @@ from isodate.isostrf import DT_BAS_WEEK_COMPLETE, DT_EXT_WEEK_COMPLETE
from isodate.isostrf import D_DEFAULT, D_WEEK, D_ALT_EXT, D_ALT_BAS
from isodate.isostrf import D_ALT_BAS_ORD, D_ALT_EXT_ORD
-__all__ = ['parse_date', 'date_isoformat', 'parse_time', 'time_isoformat',
- 'parse_datetime', 'datetime_isoformat', 'parse_duration',
- 'duration_isoformat', 'ISO8601Error', 'parse_tzinfo',
- 'tz_isoformat', 'UTC', 'FixedOffset', 'LOCAL', 'Duration',
- 'strftime', 'DATE_BAS_COMPLETE', 'DATE_BAS_ORD_COMPLETE',
- 'DATE_BAS_WEEK', 'DATE_BAS_WEEK_COMPLETE', 'DATE_CENTURY',
- 'DATE_EXT_COMPLETE', 'DATE_EXT_ORD_COMPLETE', 'DATE_EXT_WEEK',
- 'DATE_EXT_WEEK_COMPLETE', 'DATE_YEAR',
- 'DATE_BAS_MONTH', 'DATE_EXT_MONTH',
- 'TIME_BAS_COMPLETE', 'TIME_BAS_MINUTE', 'TIME_EXT_COMPLETE',
- 'TIME_EXT_MINUTE', 'TIME_HOUR', 'TZ_BAS', 'TZ_EXT', 'TZ_HOUR',
- 'DT_BAS_COMPLETE', 'DT_EXT_COMPLETE', 'DT_BAS_ORD_COMPLETE',
- 'DT_EXT_ORD_COMPLETE', 'DT_BAS_WEEK_COMPLETE',
- 'DT_EXT_WEEK_COMPLETE', 'D_DEFAULT', 'D_WEEK', 'D_ALT_EXT',
- 'D_ALT_BAS', 'D_ALT_BAS_ORD', 'D_ALT_EXT_ORD']
+__all__ = [
+ "parse_date",
+ "date_isoformat",
+ "parse_time",
+ "time_isoformat",
+ "parse_datetime",
+ "datetime_isoformat",
+ "parse_duration",
+ "duration_isoformat",
+ "ISO8601Error",
+ "parse_tzinfo",
+ "tz_isoformat",
+ "UTC",
+ "FixedOffset",
+ "LOCAL",
+ "Duration",
+ "strftime",
+ "DATE_BAS_COMPLETE",
+ "DATE_BAS_ORD_COMPLETE",
+ "DATE_BAS_WEEK",
+ "DATE_BAS_WEEK_COMPLETE",
+ "DATE_CENTURY",
+ "DATE_EXT_COMPLETE",
+ "DATE_EXT_ORD_COMPLETE",
+ "DATE_EXT_WEEK",
+ "DATE_EXT_WEEK_COMPLETE",
+ "DATE_YEAR",
+ "DATE_BAS_MONTH",
+ "DATE_EXT_MONTH",
+ "TIME_BAS_COMPLETE",
+ "TIME_BAS_MINUTE",
+ "TIME_EXT_COMPLETE",
+ "TIME_EXT_MINUTE",
+ "TIME_HOUR",
+ "TZ_BAS",
+ "TZ_EXT",
+ "TZ_HOUR",
+ "DT_BAS_COMPLETE",
+ "DT_EXT_COMPLETE",
+ "DT_BAS_ORD_COMPLETE",
+ "DT_EXT_ORD_COMPLETE",
+ "DT_BAS_WEEK_COMPLETE",
+ "DT_EXT_WEEK_COMPLETE",
+ "D_DEFAULT",
+ "D_WEEK",
+ "D_ALT_EXT",
+ "D_ALT_BAS",
+ "D_ALT_BAS_ORD",
+ "D_ALT_EXT_ORD",
+]
diff --git a/src/isodate/duration.py b/src/isodate/duration.py
index 6d1848c..d533a6f 100644
--- a/src/isodate/duration.py
+++ b/src/isodate/duration.py
@@ -1,60 +1,34 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This module defines a Duration class.
The class Duration allows to define durations in years and months and can be
used as limited replacement for timedelta objects.
-'''
+"""
from datetime import timedelta
from decimal import Decimal, ROUND_FLOOR
def fquotmod(val, low, high):
- '''
+ """
A divmod function with boundaries.
- '''
+ """
# assumes that all the maths is done with Decimals.
# divmod for Decimal uses truncate instead of floor as builtin
# divmod, so we have to do it manually here.
a, b = val - low, high - low
div = (a / b).to_integral(ROUND_FLOOR)
mod = a - div * b
- # if we were not usig Decimal, it would look like this.
+ # if we were not using Decimal, it would look like this.
# div, mod = divmod(val - low, high - low)
mod += low
return int(div), mod
def max_days_in_month(year, month):
- '''
+ """
Determines the number of days of a specific month in a specific year.
- '''
+ """
if month in (1, 3, 5, 7, 8, 10, 12):
return 31
if month in (4, 6, 9, 11):
@@ -64,8 +38,8 @@ def max_days_in_month(year, month):
return 28
-class Duration(object):
- '''
+class Duration:
+ """
A class which represents a duration.
The difference to datetime.timedelta is, that this class handles also
@@ -85,21 +59,32 @@ class Duration(object):
The algorithm to add a duration to a date is defined at
http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
- '''
-
- def __init__(self, days=0, seconds=0, microseconds=0, milliseconds=0,
- minutes=0, hours=0, weeks=0, months=0, years=0):
- '''
+ """
+
+ def __init__(
+ self,
+ days=0,
+ seconds=0,
+ microseconds=0,
+ milliseconds=0,
+ minutes=0,
+ hours=0,
+ weeks=0,
+ months=0,
+ years=0,
+ ):
+ """
Initialise this Duration instance with the given parameters.
- '''
+ """
if not isinstance(months, Decimal):
months = Decimal(str(months))
if not isinstance(years, Decimal):
years = Decimal(str(years))
self.months = months
self.years = years
- self.tdelta = timedelta(days, seconds, microseconds, milliseconds,
- minutes, hours, weeks)
+ self.tdelta = timedelta(
+ days, seconds, microseconds, milliseconds, minutes, hours, weeks
+ )
def __getstate__(self):
return self.__dict__
@@ -108,40 +93,45 @@ class Duration(object):
self.__dict__.update(state)
def __getattr__(self, name):
- '''
+ """
Provide direct access to attributes of included timedelta instance.
- '''
+ """
return getattr(self.tdelta, name)
def __str__(self):
- '''
+ """
Return a string representation of this duration similar to timedelta.
- '''
+ """
params = []
if self.years:
- params.append('%d years' % self.years)
+ params.append("%d years" % self.years)
if self.months:
fmt = "%d months"
if self.months <= 1:
fmt = "%d month"
params.append(fmt % self.months)
params.append(str(self.tdelta))
- return ', '.join(params)
+ return ", ".join(params)
def __repr__(self):
- '''
+ """
Return a string suitable for repr(x) calls.
- '''
+ """
return "%s.%s(%d, %d, %d, years=%d, months=%d)" % (
- self.__class__.__module__, self.__class__.__name__,
- self.tdelta.days, self.tdelta.seconds,
- self.tdelta.microseconds, self.years, self.months)
+ self.__class__.__module__,
+ self.__class__.__name__,
+ self.tdelta.days,
+ self.tdelta.seconds,
+ self.tdelta.microseconds,
+ self.years,
+ self.months,
+ )
def __hash__(self):
- '''
+ """
Return a hash of this instance so that it can be used in, for
example, dicts and sets.
- '''
+ """
return hash((self.tdelta, self.months, self.years))
def __neg__(self):
@@ -155,23 +145,24 @@ class Duration(object):
return negduration
def __add__(self, other):
- '''
+ """
Durations can be added with Duration, timedelta, date and datetime
objects.
- '''
+ """
if isinstance(other, Duration):
- newduration = Duration(years=self.years + other.years,
- months=self.months + other.months)
+ newduration = Duration(
+ years=self.years + other.years, months=self.months + other.months
+ )
newduration.tdelta = self.tdelta + other.tdelta
return newduration
try:
# try anything that looks like a date or datetime
# 'other' has attributes year, month, day
# and relies on 'timedelta + other' being implemented
- if (not(float(self.years).is_integer() and
- float(self.months).is_integer())):
- raise ValueError('fractional years or months not supported'
- ' for date calculations')
+ if not (float(self.years).is_integer() and float(self.months).is_integer()):
+ raise ValueError(
+ "fractional years or months not supported" " for date calculations"
+ )
newmonth = other.month + self.months
carry, newmonth = fquotmod(newmonth, 1, 13)
newyear = other.year + self.years + carry
@@ -180,7 +171,9 @@ class Duration(object):
newday = maxdays
else:
newday = other.day
- newdt = other.replace(year=newyear, month=newmonth, day=newday)
+ newdt = other.replace(
+ year=int(newyear), month=int(newmonth), day=int(newday)
+ )
# does a timedelta + date/datetime
return self.tdelta + newdt
except AttributeError:
@@ -202,9 +195,7 @@ class Duration(object):
def __mul__(self, other):
if isinstance(other, int):
- newduration = Duration(
- years=self.years * other,
- months=self.months * other)
+ newduration = Duration(years=self.years * other, months=self.months * other)
newduration.tdelta = self.tdelta * other
return newduration
return NotImplemented
@@ -212,13 +203,14 @@ class Duration(object):
__rmul__ = __mul__
def __sub__(self, other):
- '''
+ """
It is possible to subtract Duration and timedelta objects from Duration
objects.
- '''
+ """
if isinstance(other, Duration):
- newduration = Duration(years=self.years - other.years,
- months=self.months - other.months)
+ newduration = Duration(
+ years=self.years - other.years, months=self.months - other.months
+ )
newduration.tdelta = self.tdelta - other.tdelta
return newduration
try:
@@ -232,8 +224,8 @@ class Duration(object):
return NotImplemented
def __rsub__(self, other):
- '''
- It is possible to subtract Duration objecs from date, datetime and
+ """
+ It is possible to subtract Duration objects from date, datetime and
timedelta objects.
TODO: there is some weird behaviour in date - timedelta ...
@@ -244,7 +236,7 @@ class Duration(object):
the stdlib we can just do:
return -self + other
instead of all the current code
- '''
+ """
if isinstance(other, timedelta):
tmpdur = Duration()
tmpdur.tdelta = other
@@ -252,10 +244,10 @@ class Duration(object):
try:
# check if other behaves like a date/datetime object
# does it have year, month, day and replace?
- if (not(float(self.years).is_integer() and
- float(self.months).is_integer())):
- raise ValueError('fractional years or months not supported'
- ' for date calculations')
+ if not (float(self.years).is_integer() and float(self.months).is_integer()):
+ raise ValueError(
+ "fractional years or months not supported" " for date calculations"
+ )
newmonth = other.month - self.months
carry, newmonth = fquotmod(newmonth, 1, 13)
newyear = other.year - self.years + carry
@@ -264,7 +256,9 @@ class Duration(object):
newday = maxdays
else:
newday = other.day
- newdt = other.replace(year=newyear, month=newmonth, day=newday)
+ newdt = other.replace(
+ year=int(newyear), month=int(newmonth), day=int(newday)
+ )
return newdt - self.tdelta
except AttributeError:
# other probably was not compatible with data/datetime
@@ -272,14 +266,14 @@ class Duration(object):
return NotImplemented
def __eq__(self, other):
- '''
+ """
If the years, month part and the timedelta part are both equal, then
the two Durations are considered equal.
- '''
+ """
if isinstance(other, Duration):
- if (((self.years * 12 + self.months) ==
- (other.years * 12 + other.months) and
- self.tdelta == other.tdelta)):
+ if (self.years * 12 + self.months) == (
+ other.years * 12 + other.months
+ ) and self.tdelta == other.tdelta:
return True
return False
# check if other con be compared against timedelta object
@@ -289,14 +283,14 @@ class Duration(object):
return False
def __ne__(self, other):
- '''
+ """
If the years, month part or the timedelta part is not equal, then
the two Durations are considered not equal.
- '''
+ """
if isinstance(other, Duration):
- if (((self.years * 12 + self.months) !=
- (other.years * 12 + other.months) or
- self.tdelta != other.tdelta)):
+ if (self.years * 12 + self.months) != (
+ other.years * 12 + other.months
+ ) or self.tdelta != other.tdelta:
return True
return False
# check if other can be compared against timedelta object
@@ -306,12 +300,12 @@ class Duration(object):
return True
def totimedelta(self, start=None, end=None):
- '''
+ """
Convert this duration into a timedelta object.
This method requires a start datetime or end datetimem, but raises
an exception if both are given.
- '''
+ """
if start is None and end is None:
raise ValueError("start or end required")
if start is not None and end is not None:
diff --git a/src/isodate/isodates.py b/src/isodate/isodates.py
index aab5684..3c5c095 100644
--- a/src/isodate/isodates.py
+++ b/src/isodate/isodates.py
@@ -1,37 +1,11 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This modules provides a method to parse an ISO 8601:2004 date string to a
python datetime.date instance.
It supports all basic, extended and expanded formats as described in the ISO
standard. The only limitations it has, are given by the Python datetime.date
implementation, which does not support dates before 0001-01-01.
-'''
+"""
import re
from datetime import date, timedelta
@@ -46,7 +20,7 @@ DATE_REGEX_CACHE = {}
def build_date_regexps(yeardigits=4, expanded=False):
- '''
+ """
Compile set of regular expressions to parse ISO dates. The expressions will
be created only if they are not already in REGEX_CACHE.
@@ -56,7 +30,7 @@ def build_date_regexps(yeardigits=4, expanded=False):
ISO 8601 allows more than 4 digit years, on prior agreement, but then a +/-
sign is required (expanded format). To support +/- sign for 4 digit years,
the expanded parameter needs to be set to True.
- '''
+ """
if yeardigits != 4:
expanded = True
if (yeardigits, expanded) not in DATE_REGEX_CACHE:
@@ -67,69 +41,82 @@ def build_date_regexps(yeardigits=4, expanded=False):
sign = 1
else:
sign = 0
+
+ def add_re(regex_text):
+ cache_entry.append(re.compile(r"\A" + regex_text + r"\Z"))
+
# 1. complete dates:
# YYYY-MM-DD or +- YYYYYY-MM-DD... extended date format
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})" % (sign, yeardigits)
+ )
# YYYYMMDD or +- YYYYYYMMDD... basic date format
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"(?P<month>[0-9]{2})(?P<day>[0-9]{2})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"(?P<month>[0-9]{2})(?P<day>[0-9]{2})" % (sign, yeardigits)
+ )
# 2. complete week dates:
# YYYY-Www-D or +-YYYYYY-Www-D ... extended week date
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"-W(?P<week>[0-9]{2})-(?P<day>[0-9]{1})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"-W(?P<week>[0-9]{2})-(?P<day>[0-9]{1})" % (sign, yeardigits)
+ )
# YYYYWwwD or +-YYYYYYWwwD ... basic week date
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
- r"(?P<week>[0-9]{2})(?P<day>[0-9]{1})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
+ r"(?P<week>[0-9]{2})(?P<day>[0-9]{1})" % (sign, yeardigits)
+ )
# 3. ordinal dates:
# YYYY-DDD or +-YYYYYY-DDD ... extended format
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"-(?P<day>[0-9]{3})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"-(?P<day>[0-9]{3})" % (sign, yeardigits)
+ )
# YYYYDDD or +-YYYYYYDDD ... basic format
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"(?P<day>[0-9]{3})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"(?P<day>[0-9]{3})" % (sign, yeardigits)
+ )
+ # 4. week dates:
+ # YYYY-Www or +-YYYYYY-Www ... extended reduced accuracy week date
# 4. week dates:
# YYYY-Www or +-YYYYYY-Www ... extended reduced accuracy week date
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"-W(?P<week>[0-9]{2})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"-W(?P<week>[0-9]{2})" % (sign, yeardigits)
+ )
# YYYYWww or +-YYYYYYWww ... basic reduced accuracy week date
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
- r"(?P<week>[0-9]{2})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})W"
+ r"(?P<week>[0-9]{2})" % (sign, yeardigits)
+ )
+ # 5. month dates:
+ # YYY-MM or +-YYYYYY-MM ... reduced accuracy specific month
# 5. month dates:
# YYY-MM or +-YYYYYY-MM ... reduced accuracy specific month
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"-(?P<month>[0-9]{2})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"-(?P<month>[0-9]{2})" % (sign, yeardigits)
+ )
# YYYMM or +-YYYYYYMM ... basic incomplete month date format
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- r"(?P<month>[0-9]{2})"
- % (sign, yeardigits)))
+ add_re(
+ r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
+ r"(?P<month>[0-9]{2})" % (sign, yeardigits)
+ )
# 6. year dates:
# YYYY or +-YYYYYY ... reduced accuracy specific year
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})"
- % (sign, yeardigits)))
+ add_re(r"(?P<sign>[+-]){%d}(?P<year>[0-9]{%d})" % (sign, yeardigits))
# 7. century dates:
# YY or +-YYYY ... reduced accuracy specific century
- cache_entry.append(re.compile(r"(?P<sign>[+-]){%d}"
- r"(?P<century>[0-9]{%d})"
- % (sign, yeardigits - 2)))
+ add_re(r"(?P<sign>[+-]){%d}" r"(?P<century>[0-9]{%d})" % (sign, yeardigits - 2))
DATE_REGEX_CACHE[(yeardigits, expanded)] = cache_entry
return DATE_REGEX_CACHE[(yeardigits, expanded)]
-def parse_date(
- datestring,
- yeardigits=4, expanded=False, defaultmonth=1, defaultday=1):
- '''
+def parse_date(datestring, yeardigits=4, expanded=False, defaultmonth=1, defaultday=1):
+ """
Parse an ISO 8601 date string into a datetime.date object.
As the datetime.date implementation is limited to dates starting from
@@ -162,7 +149,7 @@ def parse_date(
@return: a datetime.date instance represented by datestring
@raise ISO8601Error: if this function can not parse the datestring
@raise ValueError: if datestring can not be represented by datetime.date
- '''
+ """
if yeardigits != 4:
expanded = True
isodates = build_date_regexps(yeardigits, expanded)
@@ -172,42 +159,44 @@ def parse_date(
groups = match.groupdict()
# sign, century, year, month, week, day,
# FIXME: negative dates not possible with python standard types
- sign = (groups['sign'] == '-' and -1) or 1
- if 'century' in groups:
+ sign = (groups["sign"] == "-" and -1) or 1
+ if "century" in groups:
return date(
- sign * (int(groups['century']) * 100 + 1),
- defaultmonth, defaultday)
- if 'month' not in groups: # weekdate or ordinal date
- ret = date(sign * int(groups['year']), 1, 1)
- if 'week' in groups:
+ sign * (int(groups["century"]) * 100 + 1), defaultmonth, defaultday
+ )
+ if "month" not in groups: # weekdate or ordinal date
+ ret = date(sign * int(groups["year"]), 1, 1)
+ if "week" in groups:
isotuple = ret.isocalendar()
- if 'day' in groups:
- days = int(groups['day'] or 1)
+ if "day" in groups:
+ days = int(groups["day"] or 1)
else:
days = 1
# if first week in year, do weeks-1
- return ret + timedelta(weeks=int(groups['week']) -
- (((isotuple[1] == 1) and 1) or 0),
- days=-isotuple[2] + days)
- elif 'day' in groups: # ordinal date
- return ret + timedelta(days=int(groups['day']) - 1)
+ return ret + timedelta(
+ weeks=int(groups["week"]) - (((isotuple[1] == 1) and 1) or 0),
+ days=-isotuple[2] + days,
+ )
+ elif "day" in groups: # ordinal date
+ return ret + timedelta(days=int(groups["day"]) - 1)
else: # year date
return ret.replace(month=defaultmonth, day=defaultday)
# year-, month-, or complete date
- if 'day' not in groups or groups['day'] is None:
+ if "day" not in groups or groups["day"] is None:
day = defaultday
else:
- day = int(groups['day'])
- return date(sign * int(groups['year']),
- int(groups['month']) or defaultmonth, day)
- raise ISO8601Error('Unrecognised ISO 8601 date format: %r' % datestring)
+ day = int(groups["day"])
+ return date(
+ sign * int(groups["year"]), int(groups["month"]) or defaultmonth, day
+ )
+ raise ISO8601Error("Unrecognised ISO 8601 date format: %r" % datestring)
def date_isoformat(tdate, format=DATE_EXT_COMPLETE, yeardigits=4):
- '''
+ """
Format date strings.
This method is just a wrapper around isodate.isostrf.strftime and uses
Date-Extended-Complete as default format.
- '''
+ """
return strftime(tdate, format, yeardigits)
diff --git a/src/isodate/isodatetime.py b/src/isodate/isodatetime.py
index 9a5fce0..68adc7e 100644
--- a/src/isodate/isodatetime.py
+++ b/src/isodate/isodatetime.py
@@ -1,35 +1,9 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This module defines a method to parse an ISO 8601:2004 date time string.
For this job it uses the parse_date and parse_time methods defined in date
and time module.
-'''
+"""
from datetime import datetime
from isodate.isostrf import strftime
@@ -40,29 +14,32 @@ from isodate.isotime import parse_time
def parse_datetime(datetimestring):
- '''
+ """
Parses ISO 8601 date-times into datetime.datetime objects.
This function uses parse_date and parse_time to do the job, so it allows
more combinations of date and time representations, than the actual
ISO 8601:2004 standard allows.
- '''
+ """
try:
- datestring, timestring = datetimestring.split('T')
+ datestring, timestring = datetimestring.split("T")
except ValueError:
- raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to"
- " parse datetime string %r" % datetimestring)
+ raise ISO8601Error(
+ "ISO 8601 time designator 'T' missing. Unable to"
+ " parse datetime string %r" % datetimestring
+ )
tmpdate = parse_date(datestring)
tmptime = parse_time(timestring)
return datetime.combine(tmpdate, tmptime)
-def datetime_isoformat(tdt, format=DATE_EXT_COMPLETE + 'T' +
- TIME_EXT_COMPLETE + TZ_EXT):
- '''
+def datetime_isoformat(
+ tdt, format=DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
+):
+ """
Format datetime strings.
This method is just a wrapper around isodate.isostrf.strftime and uses
Extended-Complete as default format.
- '''
+ """
return strftime(tdt, format)
diff --git a/src/isodate/isoduration.py b/src/isodate/isoduration.py
index 88829f7..fc536a1 100644
--- a/src/isodate/isoduration.py
+++ b/src/isodate/isoduration.py
@@ -1,41 +1,13 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This module provides an ISO 8601:2004 duration parser.
It also provides a wrapper to strftime. This wrapper makes it easier to
format timedelta or Duration instances as ISO conforming strings.
-'''
+"""
from datetime import timedelta
from decimal import Decimal
import re
-from six import string_types
-
from isodate.duration import Duration
from isodate.isoerror import ISO8601Error
from isodate.isodatetime import parse_datetime
@@ -50,11 +22,12 @@ ISO8601_PERIOD_REGEX = re.compile(
r"(?P<days>[0-9]+([,.][0-9]+)?D)?"
r"((?P<separator>T)(?P<hours>[0-9]+([,.][0-9]+)?H)?"
r"(?P<minutes>[0-9]+([,.][0-9]+)?M)?"
- r"(?P<seconds>[0-9]+([,.][0-9]+)?S)?)?$")
-# regular expression to parse ISO duartion strings.
+ r"(?P<seconds>[0-9]+([,.][0-9]+)?S)?)?$"
+)
+# regular expression to parse ISO duration strings.
-def parse_duration(datestring):
+def parse_duration(datestring, as_timedelta_if_possible=True):
"""
Parses an ISO 8601 durations into datetime.timedelta or Duration objects.
@@ -82,70 +55,92 @@ def parse_duration(datestring):
The alternative format does not support durations with years, months or
days set to 0.
"""
- if not isinstance(datestring, string_types):
+ if not isinstance(datestring, str):
raise TypeError("Expecting a string %r" % datestring)
match = ISO8601_PERIOD_REGEX.match(datestring)
if not match:
# try alternative format:
if datestring.startswith("P"):
durdt = parse_datetime(datestring[1:])
- if durdt.year != 0 or durdt.month != 0:
- # create Duration
- ret = Duration(days=durdt.day, seconds=durdt.second,
- microseconds=durdt.microsecond,
- minutes=durdt.minute, hours=durdt.hour,
- months=durdt.month, years=durdt.year)
- else: # FIXME: currently not possible in alternative format
+ if as_timedelta_if_possible and durdt.year == 0 and durdt.month == 0:
+ # FIXME: currently not possible in alternative format
# create timedelta
- ret = timedelta(days=durdt.day, seconds=durdt.second,
- microseconds=durdt.microsecond,
- minutes=durdt.minute, hours=durdt.hour)
+ ret = timedelta(
+ days=durdt.day,
+ seconds=durdt.second,
+ microseconds=durdt.microsecond,
+ minutes=durdt.minute,
+ hours=durdt.hour,
+ )
+ else:
+ # create Duration
+ ret = Duration(
+ days=durdt.day,
+ seconds=durdt.second,
+ microseconds=durdt.microsecond,
+ minutes=durdt.minute,
+ hours=durdt.hour,
+ months=durdt.month,
+ years=durdt.year,
+ )
return ret
raise ISO8601Error("Unable to parse duration string %r" % datestring)
groups = match.groupdict()
for key, val in groups.items():
- if key not in ('separator', 'sign'):
+ if key not in ("separator", "sign"):
if val is None:
groups[key] = "0n"
# print groups[key]
- if key in ('years', 'months'):
- groups[key] = Decimal(groups[key][:-1].replace(',', '.'))
+ if key in ("years", "months"):
+ groups[key] = Decimal(groups[key][:-1].replace(",", "."))
else:
# these values are passed into a timedelta object,
# which works with floats.
- groups[key] = float(groups[key][:-1].replace(',', '.'))
- if groups["years"] == 0 and groups["months"] == 0:
- ret = timedelta(days=groups["days"], hours=groups["hours"],
- minutes=groups["minutes"], seconds=groups["seconds"],
- weeks=groups["weeks"])
- if groups["sign"] == '-':
+ groups[key] = float(groups[key][:-1].replace(",", "."))
+ if as_timedelta_if_possible and groups["years"] == 0 and groups["months"] == 0:
+ ret = timedelta(
+ days=groups["days"],
+ hours=groups["hours"],
+ minutes=groups["minutes"],
+ seconds=groups["seconds"],
+ weeks=groups["weeks"],
+ )
+ if groups["sign"] == "-":
ret = timedelta(0) - ret
else:
- ret = Duration(years=groups["years"], months=groups["months"],
- days=groups["days"], hours=groups["hours"],
- minutes=groups["minutes"], seconds=groups["seconds"],
- weeks=groups["weeks"])
- if groups["sign"] == '-':
+ ret = Duration(
+ years=groups["years"],
+ months=groups["months"],
+ days=groups["days"],
+ hours=groups["hours"],
+ minutes=groups["minutes"],
+ seconds=groups["seconds"],
+ weeks=groups["weeks"],
+ )
+ if groups["sign"] == "-":
ret = Duration(0) - ret
return ret
def duration_isoformat(tduration, format=D_DEFAULT):
- '''
+ """
Format duration strings.
This method is just a wrapper around isodate.isostrf.strftime and uses
P%P (D_DEFAULT) as default format.
- '''
+ """
# TODO: implement better decision for negative Durations.
# should be done in Duration class in consistent way with timedelta.
- if (((isinstance(tduration, Duration) and
- (tduration.years < 0 or tduration.months < 0 or
- tduration.tdelta < timedelta(0))) or
- (isinstance(tduration, timedelta) and
- (tduration < timedelta(0))))):
- ret = '-'
+ if (
+ isinstance(tduration, Duration)
+ and (
+ tduration.years < 0
+ or tduration.months < 0
+ or tduration.tdelta < timedelta(0)
+ )
+ ) or (isinstance(tduration, timedelta) and (tduration < timedelta(0))):
+ ret = "-"
else:
- ret = ''
+ ret = ""
ret += strftime(tduration, format)
return ret
diff --git a/src/isodate/isoerror.py b/src/isodate/isoerror.py
index e7b211b..068429f 100644
--- a/src/isodate/isoerror.py
+++ b/src/isodate/isoerror.py
@@ -1,33 +1,7 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This module defines all exception classes in the whole package.
-'''
+"""
class ISO8601Error(ValueError):
- '''Raised when the given ISO string can not be parsed.'''
+ """Raised when the given ISO string can not be parsed."""
diff --git a/src/isodate/isostrf.py b/src/isodate/isostrf.py
index 642044b..94ce93f 100644
--- a/src/isodate/isostrf.py
+++ b/src/isodate/isostrf.py
@@ -1,29 +1,3 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
"""
This module provides an alternative strftime method.
@@ -40,153 +14,153 @@ from isodate.duration import Duration
from isodate.isotzinfo import tz_isoformat
# Date specific format strings
-DATE_BAS_COMPLETE = '%Y%m%d'
-DATE_EXT_COMPLETE = '%Y-%m-%d'
-DATE_BAS_WEEK_COMPLETE = '%YW%W%w'
-DATE_EXT_WEEK_COMPLETE = '%Y-W%W-%w'
-DATE_BAS_ORD_COMPLETE = '%Y%j'
-DATE_EXT_ORD_COMPLETE = '%Y-%j'
-DATE_BAS_WEEK = '%YW%W'
-DATE_EXT_WEEK = '%Y-W%W'
-DATE_BAS_MONTH = '%Y%m'
-DATE_EXT_MONTH = '%Y-%m'
-DATE_YEAR = '%Y'
-DATE_CENTURY = '%C'
+DATE_BAS_COMPLETE = "%Y%m%d"
+DATE_EXT_COMPLETE = "%Y-%m-%d"
+DATE_BAS_WEEK_COMPLETE = "%YW%W%w"
+DATE_EXT_WEEK_COMPLETE = "%Y-W%W-%w"
+DATE_BAS_ORD_COMPLETE = "%Y%j"
+DATE_EXT_ORD_COMPLETE = "%Y-%j"
+DATE_BAS_WEEK = "%YW%W"
+DATE_EXT_WEEK = "%Y-W%W"
+DATE_BAS_MONTH = "%Y%m"
+DATE_EXT_MONTH = "%Y-%m"
+DATE_YEAR = "%Y"
+DATE_CENTURY = "%C"
# Time specific format strings
-TIME_BAS_COMPLETE = '%H%M%S'
-TIME_EXT_COMPLETE = '%H:%M:%S'
-TIME_BAS_MINUTE = '%H%M'
-TIME_EXT_MINUTE = '%H:%M'
-TIME_HOUR = '%H'
+TIME_BAS_COMPLETE = "%H%M%S"
+TIME_EXT_COMPLETE = "%H:%M:%S"
+TIME_BAS_MINUTE = "%H%M"
+TIME_EXT_MINUTE = "%H:%M"
+TIME_HOUR = "%H"
# Time zone formats
-TZ_BAS = '%z'
-TZ_EXT = '%Z'
-TZ_HOUR = '%h'
+TZ_BAS = "%z"
+TZ_EXT = "%Z"
+TZ_HOUR = "%h"
# DateTime formats
-DT_EXT_COMPLETE = DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + TZ_EXT
-DT_BAS_COMPLETE = DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE + TZ_BAS
-DT_EXT_ORD_COMPLETE = DATE_EXT_ORD_COMPLETE + 'T' + TIME_EXT_COMPLETE + TZ_EXT
-DT_BAS_ORD_COMPLETE = DATE_BAS_ORD_COMPLETE + 'T' + TIME_BAS_COMPLETE + TZ_BAS
-DT_EXT_WEEK_COMPLETE = (DATE_EXT_WEEK_COMPLETE + 'T' +
- TIME_EXT_COMPLETE + TZ_EXT)
-DT_BAS_WEEK_COMPLETE = (DATE_BAS_WEEK_COMPLETE + 'T' +
- TIME_BAS_COMPLETE + TZ_BAS)
+DT_EXT_COMPLETE = DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
+DT_BAS_COMPLETE = DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
+DT_EXT_ORD_COMPLETE = DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
+DT_BAS_ORD_COMPLETE = DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
+DT_EXT_WEEK_COMPLETE = DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
+DT_BAS_WEEK_COMPLETE = DATE_BAS_WEEK_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
# Duration formts
-D_DEFAULT = 'P%P'
-D_WEEK = 'P%p'
-D_ALT_EXT = 'P' + DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE
-D_ALT_BAS = 'P' + DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE
-D_ALT_EXT_ORD = 'P' + DATE_EXT_ORD_COMPLETE + 'T' + TIME_EXT_COMPLETE
-D_ALT_BAS_ORD = 'P' + DATE_BAS_ORD_COMPLETE + 'T' + TIME_BAS_COMPLETE
-
-STRF_DT_MAP = {'%d': lambda tdt, yds: '%02d' % tdt.day,
- '%f': lambda tdt, yds: '%06d' % tdt.microsecond,
- '%H': lambda tdt, yds: '%02d' % tdt.hour,
- '%j': lambda tdt, yds: '%03d' % (tdt.toordinal() -
- date(tdt.year,
- 1, 1).toordinal() +
- 1),
- '%m': lambda tdt, yds: '%02d' % tdt.month,
- '%M': lambda tdt, yds: '%02d' % tdt.minute,
- '%S': lambda tdt, yds: '%02d' % tdt.second,
- '%w': lambda tdt, yds: '%1d' % tdt.isoweekday(),
- '%W': lambda tdt, yds: '%02d' % tdt.isocalendar()[1],
- '%Y': lambda tdt, yds: (((yds != 4) and '+') or '') +
- (('%%0%dd' % yds) % tdt.year),
- '%C': lambda tdt, yds: (((yds != 4) and '+') or '') +
- (('%%0%dd' % (yds - 2)) %
- (tdt.year / 100)),
- '%h': lambda tdt, yds: tz_isoformat(tdt, '%h'),
- '%Z': lambda tdt, yds: tz_isoformat(tdt, '%Z'),
- '%z': lambda tdt, yds: tz_isoformat(tdt, '%z'),
- '%%': lambda tdt, yds: '%'}
-
-STRF_D_MAP = {'%d': lambda tdt, yds: '%02d' % tdt.days,
- '%f': lambda tdt, yds: '%06d' % tdt.microseconds,
- '%H': lambda tdt, yds: '%02d' % (tdt.seconds / 60 / 60),
- '%m': lambda tdt, yds: '%02d' % tdt.months,
- '%M': lambda tdt, yds: '%02d' % ((tdt.seconds / 60) % 60),
- '%S': lambda tdt, yds: '%02d' % (tdt.seconds % 60),
- '%W': lambda tdt, yds: '%02d' % (abs(tdt.days / 7)),
- '%Y': lambda tdt, yds: (((yds != 4) and '+') or '') +
- (('%%0%dd' % yds) % tdt.years),
- '%C': lambda tdt, yds: (((yds != 4) and '+') or '') +
- (('%%0%dd' % (yds - 2)) %
- (tdt.years / 100)),
- '%%': lambda tdt, yds: '%'}
+D_DEFAULT = "P%P"
+D_WEEK = "P%p"
+D_ALT_EXT = "P" + DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE
+D_ALT_BAS = "P" + DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE
+D_ALT_EXT_ORD = "P" + DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE
+D_ALT_BAS_ORD = "P" + DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE
+
+STRF_DT_MAP = {
+ "%d": lambda tdt, yds: "%02d" % tdt.day,
+ "%f": lambda tdt, yds: "%06d" % tdt.microsecond,
+ "%H": lambda tdt, yds: "%02d" % tdt.hour,
+ "%j": lambda tdt, yds: "%03d"
+ % (tdt.toordinal() - date(tdt.year, 1, 1).toordinal() + 1),
+ "%m": lambda tdt, yds: "%02d" % tdt.month,
+ "%M": lambda tdt, yds: "%02d" % tdt.minute,
+ "%S": lambda tdt, yds: "%02d" % tdt.second,
+ "%w": lambda tdt, yds: "%1d" % tdt.isoweekday(),
+ "%W": lambda tdt, yds: "%02d" % tdt.isocalendar()[1],
+ "%Y": lambda tdt, yds: (((yds != 4) and "+") or "") + (("%%0%dd" % yds) % tdt.year),
+ "%C": lambda tdt, yds: (((yds != 4) and "+") or "")
+ + (("%%0%dd" % (yds - 2)) % (tdt.year / 100)),
+ "%h": lambda tdt, yds: tz_isoformat(tdt, "%h"),
+ "%Z": lambda tdt, yds: tz_isoformat(tdt, "%Z"),
+ "%z": lambda tdt, yds: tz_isoformat(tdt, "%z"),
+ "%%": lambda tdt, yds: "%",
+}
+
+STRF_D_MAP = {
+ "%d": lambda tdt, yds: "%02d" % tdt.days,
+ "%f": lambda tdt, yds: "%06d" % tdt.microseconds,
+ "%H": lambda tdt, yds: "%02d" % (tdt.seconds / 60 / 60),
+ "%m": lambda tdt, yds: "%02d" % tdt.months,
+ "%M": lambda tdt, yds: "%02d" % ((tdt.seconds / 60) % 60),
+ "%S": lambda tdt, yds: "%02d" % (tdt.seconds % 60),
+ "%W": lambda tdt, yds: "%02d" % (abs(tdt.days / 7)),
+ "%Y": lambda tdt, yds: (((yds != 4) and "+") or "")
+ + (("%%0%dd" % yds) % tdt.years),
+ "%C": lambda tdt, yds: (((yds != 4) and "+") or "")
+ + (("%%0%dd" % (yds - 2)) % (tdt.years / 100)),
+ "%%": lambda tdt, yds: "%",
+}
def _strfduration(tdt, format, yeardigits=4):
- '''
+ """
this is the work method for timedelta and Duration instances.
see strftime for more details.
- '''
+ """
+
def repl(match):
- '''
+ """
lookup format command and return corresponding replacement.
- '''
+ """
if match.group(0) in STRF_D_MAP:
return STRF_D_MAP[match.group(0)](tdt, yeardigits)
- elif match.group(0) == '%P':
+ elif match.group(0) == "%P":
ret = []
if isinstance(tdt, Duration):
if tdt.years:
- ret.append('%sY' % abs(tdt.years))
+ ret.append("%sY" % abs(tdt.years))
if tdt.months:
- ret.append('%sM' % abs(tdt.months))
- usecs = abs((tdt.days * 24 * 60 * 60 + tdt.seconds) * 1000000 +
- tdt.microseconds)
+ ret.append("%sM" % abs(tdt.months))
+ usecs = abs(
+ (tdt.days * 24 * 60 * 60 + tdt.seconds) * 1000000 + tdt.microseconds
+ )
seconds, usecs = divmod(usecs, 1000000)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
days, hours = divmod(hours, 24)
if days:
- ret.append('%sD' % days)
+ ret.append("%sD" % days)
if hours or minutes or seconds or usecs:
- ret.append('T')
+ ret.append("T")
if hours:
- ret.append('%sH' % hours)
+ ret.append("%sH" % hours)
if minutes:
- ret.append('%sM' % minutes)
+ ret.append("%sM" % minutes)
if seconds or usecs:
if usecs:
- ret.append(("%d.%06d" % (seconds, usecs)).rstrip('0'))
+ ret.append(("%d.%06d" % (seconds, usecs)).rstrip("0"))
else:
ret.append("%d" % seconds)
- ret.append('S')
+ ret.append("S")
# at least one component has to be there.
- return ret and ''.join(ret) or '0D'
- elif match.group(0) == '%p':
- return str(abs(tdt.days // 7)) + 'W'
+ return ret and "".join(ret) or "0D"
+ elif match.group(0) == "%p":
+ return str(abs(tdt.days // 7)) + "W"
return match.group(0)
- return re.sub('%d|%f|%H|%m|%M|%S|%W|%Y|%C|%%|%P|%p', repl,
- format)
+
+ return re.sub("%d|%f|%H|%m|%M|%S|%W|%Y|%C|%%|%P|%p", repl, format)
def _strfdt(tdt, format, yeardigits=4):
- '''
+ """
this is the work method for time and date instances.
see strftime for more details.
- '''
+ """
+
def repl(match):
- '''
+ """
lookup format command and return corresponding replacement.
- '''
+ """
if match.group(0) in STRF_DT_MAP:
return STRF_DT_MAP[match.group(0)](tdt, yeardigits)
return match.group(0)
- return re.sub('%d|%f|%H|%j|%m|%M|%S|%w|%W|%Y|%C|%z|%Z|%h|%%', repl,
- format)
+
+ return re.sub("%d|%f|%H|%j|%m|%M|%S|%w|%W|%Y|%C|%z|%Z|%h|%%", repl, format)
def strftime(tdt, format, yeardigits=4):
- '''Directive Meaning Notes
+ """Directive Meaning Notes
%d Day of the month as a decimal number [01,31].
%f Microsecond as a decimal number [0,999999], zero-padded
on the left (1)
@@ -208,7 +182,7 @@ def strftime(tdt, format, yeardigits=4):
%p ISO8601 duration format in weeks.
%% A literal '%' character.
- '''
+ """
if isinstance(tdt, (timedelta, Duration)):
return _strfduration(tdt, format, yeardigits)
return _strfdt(tdt, format, yeardigits)
diff --git a/src/isodate/isotime.py b/src/isodate/isotime.py
index 113d34b..619e4aa 100644
--- a/src/isodate/isotime.py
+++ b/src/isodate/isotime.py
@@ -1,38 +1,12 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This modules provides a method to parse an ISO 8601:2004 time string to a
Python datetime.time instance.
It supports all basic and extended formats including time zone specifications
as described in the ISO standard.
-'''
+"""
import re
-from decimal import Decimal
+from decimal import Decimal, ROUND_FLOOR
from datetime import time
from isodate.isostrf import strftime, TIME_EXT_COMPLETE, TZ_EXT
@@ -44,12 +18,12 @@ TIME_REGEX_CACHE = []
def build_time_regexps():
- '''
+ """
Build regular expressions to parse ISO time string.
The regular expressions are compiled and stored in TIME_REGEX_CACHE
for later reuse.
- '''
+ """
if not TIME_REGEX_CACHE:
# ISO 8601 time representations allow decimal fractions on least
# significant time component. Command and Full Stop are both valid
@@ -67,40 +41,42 @@ def build_time_regexps():
# +-hhmm
# +-hh =>
# isotzinfo.TZ_REGEX
+ def add_re(regex_text):
+ TIME_REGEX_CACHE.append(re.compile(r"\A" + regex_text + TZ_REGEX + r"\Z"))
+
# 1. complete time:
# hh:mm:ss.ss ... extended format
- TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2}):"
- r"(?P<minute>[0-9]{2}):"
- r"(?P<second>[0-9]{2}"
- r"([,.][0-9]+)?)" + TZ_REGEX))
+ add_re(
+ r"T?(?P<hour>[0-9]{2}):"
+ r"(?P<minute>[0-9]{2}):"
+ r"(?P<second>[0-9]{2}"
+ r"([,.][0-9]+)?)"
+ )
# hhmmss.ss ... basic format
- TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2})"
- r"(?P<minute>[0-9]{2})"
- r"(?P<second>[0-9]{2}"
- r"([,.][0-9]+)?)" + TZ_REGEX))
+ add_re(
+ r"T?(?P<hour>[0-9]{2})"
+ r"(?P<minute>[0-9]{2})"
+ r"(?P<second>[0-9]{2}"
+ r"([,.][0-9]+)?)"
+ )
# 2. reduced accuracy:
# hh:mm.mm ... extended format
- TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2}):"
- r"(?P<minute>[0-9]{2}"
- r"([,.][0-9]+)?)" + TZ_REGEX))
+ add_re(r"T?(?P<hour>[0-9]{2}):" r"(?P<minute>[0-9]{2}" r"([,.][0-9]+)?)")
# hhmm.mm ... basic format
- TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2})"
- r"(?P<minute>[0-9]{2}"
- r"([,.][0-9]+)?)" + TZ_REGEX))
+ add_re(r"T?(?P<hour>[0-9]{2})" r"(?P<minute>[0-9]{2}" r"([,.][0-9]+)?)")
# hh.hh ... basic format
- TIME_REGEX_CACHE.append(re.compile(r"T?(?P<hour>[0-9]{2}"
- r"([,.][0-9]+)?)" + TZ_REGEX))
+ add_re(r"T?(?P<hour>[0-9]{2}" r"([,.][0-9]+)?)")
return TIME_REGEX_CACHE
def parse_time(timestring):
- '''
+ """
Parses ISO 8601 times into datetime.time objects.
Following ISO 8601 formats are supported:
(as decimal separator a ',' or a '.' is allowed)
hhmmss.ssTZD basic complete time
- hh:mm:ss.ssTZD extended compelte time
+ hh:mm:ss.ssTZD extended complete time
hhmm.mmTZD basic reduced accuracy time
hh:mm.mmTZD extended reduced accuracy time
hh.hhTZD basic reduced accuracy time
@@ -110,7 +86,7 @@ def parse_time(timestring):
+-hhmm basic hours and minutes
+-hh:mm extended hours and minutes
+-hh hours
- '''
+ """
isotimes = build_time_regexps()
for pattern in isotimes:
match = pattern.match(timestring)
@@ -118,41 +94,61 @@ def parse_time(timestring):
groups = match.groupdict()
for key, value in groups.items():
if value is not None:
- groups[key] = value.replace(',', '.')
- tzinfo = build_tzinfo(groups['tzname'], groups['tzsign'],
- int(groups['tzhour'] or 0),
- int(groups['tzmin'] or 0))
- if 'second' in groups:
- # round to microseconds if fractional seconds are more precise
- second = Decimal(groups['second']).quantize(Decimal('.000001'))
+ groups[key] = value.replace(",", ".")
+ tzinfo = build_tzinfo(
+ groups["tzname"],
+ groups["tzsign"],
+ int(groups["tzhour"] or 0),
+ int(groups["tzmin"] or 0),
+ )
+ if "second" in groups:
+ second = Decimal(groups["second"]).quantize(
+ Decimal(".000001"), rounding=ROUND_FLOOR
+ )
microsecond = (second - int(second)) * int(1e6)
# int(...) ... no rounding
# to_integral() ... rounding
- return time(int(groups['hour']), int(groups['minute']),
- int(second), int(microsecond.to_integral()),
- tzinfo)
- if 'minute' in groups:
- minute = Decimal(groups['minute'])
- second = (minute - int(minute)) * 60
+ return time(
+ int(groups["hour"]),
+ int(groups["minute"]),
+ int(second),
+ int(microsecond.to_integral()),
+ tzinfo,
+ )
+ if "minute" in groups:
+ minute = Decimal(groups["minute"])
+ second = Decimal((minute - int(minute)) * 60).quantize(
+ Decimal(".000001"), rounding=ROUND_FLOOR
+ )
microsecond = (second - int(second)) * int(1e6)
- return time(int(groups['hour']), int(minute), int(second),
- int(microsecond.to_integral()), tzinfo)
+ return time(
+ int(groups["hour"]),
+ int(minute),
+ int(second),
+ int(microsecond.to_integral()),
+ tzinfo,
+ )
else:
microsecond, second, minute = 0, 0, 0
- hour = Decimal(groups['hour'])
+ hour = Decimal(groups["hour"])
minute = (hour - int(hour)) * 60
second = (minute - int(minute)) * 60
microsecond = (second - int(second)) * int(1e6)
- return time(int(hour), int(minute), int(second),
- int(microsecond.to_integral()), tzinfo)
- raise ISO8601Error('Unrecognised ISO 8601 time format: %r' % timestring)
+ return time(
+ int(hour),
+ int(minute),
+ int(second),
+ int(microsecond.to_integral()),
+ tzinfo,
+ )
+ raise ISO8601Error("Unrecognised ISO 8601 time format: %r" % timestring)
def time_isoformat(ttime, format=TIME_EXT_COMPLETE + TZ_EXT):
- '''
+ """
Format time strings.
This method is just a wrapper around isodate.isostrf.strftime and uses
Time-Extended-Complete with extended time zone as default format.
- '''
+ """
return strftime(ttime, format)
diff --git a/src/isodate/isotzinfo.py b/src/isodate/isotzinfo.py
index a805ebe..ec6dd17 100644
--- a/src/isodate/isotzinfo.py
+++ b/src/isodate/isotzinfo.py
@@ -1,65 +1,40 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
This module provides an ISO 8601:2004 time zone info parser.
It offers a function to parse the time zone offset as specified by ISO 8601.
-'''
+"""
import re
from isodate.isoerror import ISO8601Error
from isodate.tzinfo import UTC, FixedOffset, ZERO
-TZ_REGEX = r"(?P<tzname>(Z|(?P<tzsign>[+-])"\
- r"(?P<tzhour>[0-9]{2})(:?(?P<tzmin>[0-9]{2}))?)?)"
+TZ_REGEX = (
+ r"(?P<tzname>(Z|(?P<tzsign>[+-])" r"(?P<tzhour>[0-9]{2})(:?(?P<tzmin>[0-9]{2}))?)?)"
+)
TZ_RE = re.compile(TZ_REGEX)
-def build_tzinfo(tzname, tzsign='+', tzhour=0, tzmin=0):
- '''
+def build_tzinfo(tzname, tzsign="+", tzhour=0, tzmin=0):
+ """
create a tzinfo instance according to given parameters.
tzname:
'Z' ... return UTC
'' | None ... return None
other ... return FixedOffset
- '''
- if tzname is None or tzname == '':
+ """
+ if tzname is None or tzname == "":
return None
- if tzname == 'Z':
+ if tzname == "Z":
return UTC
- tzsign = ((tzsign == '-') and -1) or 1
+ tzsign = ((tzsign == "-") and -1) or 1
return FixedOffset(tzsign * tzhour, tzsign * tzmin, tzname)
def parse_tzinfo(tzstring):
- '''
- Parses ISO 8601 time zone designators to tzinfo objecs.
+ """
+ Parses ISO 8601 time zone designators to tzinfo objects.
A time zone designator can be in the following format:
no designator indicates local time zone
@@ -67,18 +42,21 @@ def parse_tzinfo(tzstring):
+-hhmm basic hours and minutes
+-hh:mm extended hours and minutes
+-hh hours
- '''
+ """
match = TZ_RE.match(tzstring)
if match:
groups = match.groupdict()
- return build_tzinfo(groups['tzname'], groups['tzsign'],
- int(groups['tzhour'] or 0),
- int(groups['tzmin'] or 0))
- raise ISO8601Error('%s not a valid time zone info' % tzstring)
+ return build_tzinfo(
+ groups["tzname"],
+ groups["tzsign"],
+ int(groups["tzhour"] or 0),
+ int(groups["tzmin"] or 0),
+ )
+ raise ISO8601Error("%s not a valid time zone info" % tzstring)
-def tz_isoformat(dt, format='%Z'):
- '''
+def tz_isoformat(dt, format="%Z"):
+ """
return time zone offset ISO 8601 formatted.
The various ISO formats can be chosen with the format parameter.
@@ -89,24 +67,24 @@ def tz_isoformat(dt, format='%Z'):
%h ... +-HH
%z ... +-HHMM
%Z ... +-HH:MM
- '''
+ """
tzinfo = dt.tzinfo
if (tzinfo is None) or (tzinfo.utcoffset(dt) is None):
- return ''
+ return ""
if tzinfo.utcoffset(dt) == ZERO and tzinfo.dst(dt) == ZERO:
- return 'Z'
+ return "Z"
tdelta = tzinfo.utcoffset(dt)
seconds = tdelta.days * 24 * 60 * 60 + tdelta.seconds
- sign = ((seconds < 0) and '-') or '+'
+ sign = ((seconds < 0) and "-") or "+"
seconds = abs(seconds)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
if hours > 99:
- raise OverflowError('can not handle differences > 99 hours')
- if format == '%Z':
- return '%s%02d:%02d' % (sign, hours, minutes)
- elif format == '%z':
- return '%s%02d%02d' % (sign, hours, minutes)
- elif format == '%h':
- return '%s%02d' % (sign, hours)
+ raise OverflowError("can not handle differences > 99 hours")
+ if format == "%Z":
+ return "%s%02d:%02d" % (sign, hours, minutes)
+ elif format == "%z":
+ return "%s%02d%02d" % (sign, hours, minutes)
+ elif format == "%h":
+ return "%s%02d" % (sign, hours)
raise ValueError('unknown format string "%s"' % format)
diff --git a/src/isodate/tests/__init__.py b/src/isodate/tests/__init__.py
index b1d46bd..ea0f433 100644
--- a/src/isodate/tests/__init__.py
+++ b/src/isodate/tests/__init__.py
@@ -1,51 +1,36 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Collect all test suites into one TestSuite instance.
-'''
+"""
import unittest
-from isodate.tests import (test_date, test_time, test_datetime, test_duration,
- test_strf, test_pickle)
+import warnings
+from isodate.tests import (
+ test_date,
+ test_time,
+ test_datetime,
+ test_duration,
+ test_strf,
+ test_pickle,
+)
def test_suite():
- '''
+ """
Return a new TestSuite instance consisting of all available TestSuites.
- '''
- return unittest.TestSuite([
- test_date.test_suite(),
- test_time.test_suite(),
- test_datetime.test_suite(),
- test_duration.test_suite(),
- test_strf.test_suite(),
- test_pickle.test_suite(),
- ])
+ """
+ warnings.filterwarnings("error", module=r"isodate(\..)*")
+ return unittest.TestSuite(
+ [
+ test_date.test_suite(),
+ test_time.test_suite(),
+ test_datetime.test_suite(),
+ test_duration.test_suite(),
+ test_strf.test_suite(),
+ test_pickle.test_suite(),
+ ]
+ )
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tests/test_date.py b/src/isodate/tests/test_date.py
index 6ee89cb..cee3903 100644
--- a/src/isodate/tests/test_date.py
+++ b/src/isodate/tests/test_date.py
@@ -1,32 +1,6 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Test cases for the isodate module.
-'''
+"""
import unittest
from datetime import date
from isodate import parse_date, ISO8601Error, date_isoformat
@@ -41,85 +15,89 @@ from isodate import DATE_EXT_WEEK, DATE_EXT_WEEK_COMPLETE
# result from the parse_date method. A result of None means an ISO8601Error
# is expected. The test cases are grouped into dates with 4 digit years
# and 6 digit years.
-TEST_CASES = {4: [('19', date(1901, 1, 1), DATE_CENTURY),
- ('1985', date(1985, 1, 1), DATE_YEAR),
- ('1985-04', date(1985, 4, 1), DATE_EXT_MONTH),
- ('198504', date(1985, 4, 1), DATE_BAS_MONTH),
- ('1985-04-12', date(1985, 4, 12), DATE_EXT_COMPLETE),
- ('19850412', date(1985, 4, 12), DATE_BAS_COMPLETE),
- ('1985102', date(1985, 4, 12), DATE_BAS_ORD_COMPLETE),
- ('1985-102', date(1985, 4, 12), DATE_EXT_ORD_COMPLETE),
- ('1985W155', date(1985, 4, 12), DATE_BAS_WEEK_COMPLETE),
- ('1985-W15-5', date(1985, 4, 12), DATE_EXT_WEEK_COMPLETE),
- ('1985W15', date(1985, 4, 8), DATE_BAS_WEEK),
- ('1985-W15', date(1985, 4, 8), DATE_EXT_WEEK),
- ('1989-W15', date(1989, 4, 10), DATE_EXT_WEEK),
- ('1989-W15-5', date(1989, 4, 14), DATE_EXT_WEEK_COMPLETE),
- ('1-W1-1', None, DATE_BAS_WEEK_COMPLETE)],
- 6: [('+0019', date(1901, 1, 1), DATE_CENTURY),
- ('+001985', date(1985, 1, 1), DATE_YEAR),
- ('+001985-04', date(1985, 4, 1), DATE_EXT_MONTH),
- ('+001985-04-12', date(1985, 4, 12), DATE_EXT_COMPLETE),
- ('+0019850412', date(1985, 4, 12), DATE_BAS_COMPLETE),
- ('+001985102', date(1985, 4, 12), DATE_BAS_ORD_COMPLETE),
- ('+001985-102', date(1985, 4, 12), DATE_EXT_ORD_COMPLETE),
- ('+001985W155', date(1985, 4, 12), DATE_BAS_WEEK_COMPLETE),
- ('+001985-W15-5', date(1985, 4, 12), DATE_EXT_WEEK_COMPLETE),
- ('+001985W15', date(1985, 4, 8), DATE_BAS_WEEK),
- ('+001985-W15', date(1985, 4, 8), DATE_EXT_WEEK)]}
+TEST_CASES = {
+ 4: [
+ ("19", date(1901, 1, 1), DATE_CENTURY),
+ ("1985", date(1985, 1, 1), DATE_YEAR),
+ ("1985-04", date(1985, 4, 1), DATE_EXT_MONTH),
+ ("198504", date(1985, 4, 1), DATE_BAS_MONTH),
+ ("1985-04-12", date(1985, 4, 12), DATE_EXT_COMPLETE),
+ ("19850412", date(1985, 4, 12), DATE_BAS_COMPLETE),
+ ("1985102", date(1985, 4, 12), DATE_BAS_ORD_COMPLETE),
+ ("1985-102", date(1985, 4, 12), DATE_EXT_ORD_COMPLETE),
+ ("1985W155", date(1985, 4, 12), DATE_BAS_WEEK_COMPLETE),
+ ("1985-W15-5", date(1985, 4, 12), DATE_EXT_WEEK_COMPLETE),
+ ("1985W15", date(1985, 4, 8), DATE_BAS_WEEK),
+ ("1985-W15", date(1985, 4, 8), DATE_EXT_WEEK),
+ ("1989-W15", date(1989, 4, 10), DATE_EXT_WEEK),
+ ("1989-W15-5", date(1989, 4, 14), DATE_EXT_WEEK_COMPLETE),
+ ("1-W1-1", None, DATE_BAS_WEEK_COMPLETE),
+ ],
+ 6: [
+ ("+0019", date(1901, 1, 1), DATE_CENTURY),
+ ("+001985", date(1985, 1, 1), DATE_YEAR),
+ ("+001985-04", date(1985, 4, 1), DATE_EXT_MONTH),
+ ("+001985-04-12", date(1985, 4, 12), DATE_EXT_COMPLETE),
+ ("+0019850412", date(1985, 4, 12), DATE_BAS_COMPLETE),
+ ("+001985102", date(1985, 4, 12), DATE_BAS_ORD_COMPLETE),
+ ("+001985-102", date(1985, 4, 12), DATE_EXT_ORD_COMPLETE),
+ ("+001985W155", date(1985, 4, 12), DATE_BAS_WEEK_COMPLETE),
+ ("+001985-W15-5", date(1985, 4, 12), DATE_EXT_WEEK_COMPLETE),
+ ("+001985W15", date(1985, 4, 8), DATE_BAS_WEEK),
+ ("+001985-W15", date(1985, 4, 8), DATE_EXT_WEEK),
+ ],
+}
def create_testcase(yeardigits, datestring, expectation, format):
- '''
+ """
Create a TestCase class for a specific test.
This allows having a separate TestCase for each test tuple from the
TEST_CASES list, so that a failed test won't stop other tests.
- '''
+ """
class TestDate(unittest.TestCase):
- '''
+ """
A test case template to parse an ISO date string into a date
object.
- '''
+ """
def test_parse(self):
- '''
+ """
Parse an ISO date string and compare it to the expected value.
- '''
+ """
if expectation is None:
- self.assertRaises(ISO8601Error, parse_date, datestring,
- yeardigits)
+ self.assertRaises(ISO8601Error, parse_date, datestring, yeardigits)
else:
result = parse_date(datestring, yeardigits)
self.assertEqual(result, expectation)
def test_format(self):
- '''
+ """
Take date object and create ISO string from it.
This is the reverse test to test_parse.
- '''
+ """
if expectation is None:
- self.assertRaises(AttributeError,
- date_isoformat, expectation, format,
- yeardigits)
+ self.assertRaises(
+ AttributeError, date_isoformat, expectation, format, yeardigits
+ )
else:
- self.assertEqual(date_isoformat(expectation, format,
- yeardigits),
- datestring)
+ self.assertEqual(
+ date_isoformat(expectation, format, yeardigits), datestring
+ )
return unittest.TestLoader().loadTestsFromTestCase(TestDate)
def test_suite():
- '''
+ """
Construct a TestSuite instance for all test cases.
- '''
+ """
suite = unittest.TestSuite()
for yeardigits, tests in TEST_CASES.items():
for datestring, expectation, format in tests:
- suite.addTest(create_testcase(yeardigits, datestring,
- expectation, format))
+ suite.addTest(create_testcase(yeardigits, datestring, expectation, format))
return suite
@@ -128,5 +106,5 @@ def load_tests(loader, tests, pattern):
return test_suite()
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tests/test_datetime.py b/src/isodate/tests/test_datetime.py
index 85c822f..19752d3 100644
--- a/src/isodate/tests/test_datetime.py
+++ b/src/isodate/tests/test_datetime.py
@@ -1,32 +1,6 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Test cases for the isodatetime module.
-'''
+"""
import unittest
from datetime import datetime
@@ -41,62 +15,113 @@ from isodate import DATE_BAS_WEEK_COMPLETE, DATE_EXT_WEEK_COMPLETE
# the following list contains tuples of ISO datetime strings and the expected
# result from the parse_datetime method. A result of None means an ISO8601Error
# is expected.
-TEST_CASES = [('19850412T1015', datetime(1985, 4, 12, 10, 15),
- DATE_BAS_COMPLETE + 'T' + TIME_BAS_MINUTE,
- '19850412T1015'),
- ('1985-04-12T10:15', datetime(1985, 4, 12, 10, 15),
- DATE_EXT_COMPLETE + 'T' + TIME_EXT_MINUTE,
- '1985-04-12T10:15'),
- ('1985102T1015Z', datetime(1985, 4, 12, 10, 15, tzinfo=UTC),
- DATE_BAS_ORD_COMPLETE + 'T' + TIME_BAS_MINUTE + TZ_BAS,
- '1985102T1015Z'),
- ('1985-102T10:15Z', datetime(1985, 4, 12, 10, 15, tzinfo=UTC),
- DATE_EXT_ORD_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_EXT,
- '1985-102T10:15Z'),
- ('1985W155T1015+0400', datetime(1985, 4, 12, 10, 15,
- tzinfo=FixedOffset(4, 0,
- '+0400')),
- DATE_BAS_WEEK_COMPLETE + 'T' + TIME_BAS_MINUTE + TZ_BAS,
- '1985W155T1015+0400'),
- ('1985-W15-5T10:15+04', datetime(1985, 4, 12, 10, 15,
- tzinfo=FixedOffset(4, 0,
- '+0400'),),
- DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_HOUR,
- '1985-W15-5T10:15+04'),
- ('1985-W15-5T10:15-0430',
- datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(-4, -30,
- '-0430'),),
- DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_BAS,
- '1985-W15-5T10:15-0430'),
- ('1985-W15-5T10:15+04:45',
- datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(4, 45,
- '+04:45'),),
- DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_EXT,
- '1985-W15-5T10:15+04:45'),
- ('20110410T101225.123000Z',
- datetime(2011, 4, 10, 10, 12, 25, 123000, tzinfo=UTC),
- DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE + ".%f" + TZ_BAS,
- '20110410T101225.123000Z'),
- ('2012-10-12T08:29:46.069178Z',
- datetime(2012, 10, 12, 8, 29, 46, 69178, tzinfo=UTC),
- DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS,
- '2012-10-12T08:29:46.069178Z'),
- ('2012-10-12T08:29:46.691780Z',
- datetime(2012, 10, 12, 8, 29, 46, 691780, tzinfo=UTC),
- DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS,
- '2012-10-12T08:29:46.691780Z'),
- ('2012-10-30T08:55:22.1234567Z',
- datetime(2012, 10, 30, 8, 55, 22, 123457, tzinfo=UTC),
- DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS,
- '2012-10-30T08:55:22.123457Z'),
- ('2012-10-30T08:55:22.1234561Z',
- datetime(2012, 10, 30, 8, 55, 22, 123456, tzinfo=UTC),
- DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS,
- '2012-10-30T08:55:22.123456Z'),
- ('2014-08-18 14:55:22.123456Z', None,
- DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS,
- '2014-08-18T14:55:22.123456Z'),
- ]
+TEST_CASES = [
+ (
+ "19850412T1015",
+ datetime(1985, 4, 12, 10, 15),
+ DATE_BAS_COMPLETE + "T" + TIME_BAS_MINUTE,
+ "19850412T1015",
+ ),
+ (
+ "1985-04-12T10:15",
+ datetime(1985, 4, 12, 10, 15),
+ DATE_EXT_COMPLETE + "T" + TIME_EXT_MINUTE,
+ "1985-04-12T10:15",
+ ),
+ (
+ "1985102T1015Z",
+ datetime(1985, 4, 12, 10, 15, tzinfo=UTC),
+ DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_MINUTE + TZ_BAS,
+ "1985102T1015Z",
+ ),
+ (
+ "1985-102T10:15Z",
+ datetime(1985, 4, 12, 10, 15, tzinfo=UTC),
+ DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_MINUTE + TZ_EXT,
+ "1985-102T10:15Z",
+ ),
+ (
+ "1985W155T1015+0400",
+ datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(4, 0, "+0400")),
+ DATE_BAS_WEEK_COMPLETE + "T" + TIME_BAS_MINUTE + TZ_BAS,
+ "1985W155T1015+0400",
+ ),
+ (
+ "1985-W15-5T10:15+04",
+ datetime(
+ 1985,
+ 4,
+ 12,
+ 10,
+ 15,
+ tzinfo=FixedOffset(4, 0, "+0400"),
+ ),
+ DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_MINUTE + TZ_HOUR,
+ "1985-W15-5T10:15+04",
+ ),
+ (
+ "1985-W15-5T10:15-0430",
+ datetime(
+ 1985,
+ 4,
+ 12,
+ 10,
+ 15,
+ tzinfo=FixedOffset(-4, -30, "-0430"),
+ ),
+ DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_MINUTE + TZ_BAS,
+ "1985-W15-5T10:15-0430",
+ ),
+ (
+ "1985-W15-5T10:15+04:45",
+ datetime(
+ 1985,
+ 4,
+ 12,
+ 10,
+ 15,
+ tzinfo=FixedOffset(4, 45, "+04:45"),
+ ),
+ DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_MINUTE + TZ_EXT,
+ "1985-W15-5T10:15+04:45",
+ ),
+ (
+ "20110410T101225.123000Z",
+ datetime(2011, 4, 10, 10, 12, 25, 123000, tzinfo=UTC),
+ DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE + ".%f" + TZ_BAS,
+ "20110410T101225.123000Z",
+ ),
+ (
+ "2012-10-12T08:29:46.069178Z",
+ datetime(2012, 10, 12, 8, 29, 46, 69178, tzinfo=UTC),
+ DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + ".%f" + TZ_BAS,
+ "2012-10-12T08:29:46.069178Z",
+ ),
+ (
+ "2012-10-12T08:29:46.691780Z",
+ datetime(2012, 10, 12, 8, 29, 46, 691780, tzinfo=UTC),
+ DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + ".%f" + TZ_BAS,
+ "2012-10-12T08:29:46.691780Z",
+ ),
+ (
+ "2012-10-30T08:55:22.1234567Z",
+ datetime(2012, 10, 30, 8, 55, 22, 123456, tzinfo=UTC),
+ DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + ".%f" + TZ_BAS,
+ "2012-10-30T08:55:22.123456Z",
+ ),
+ (
+ "2012-10-30T08:55:22.1234561Z",
+ datetime(2012, 10, 30, 8, 55, 22, 123456, tzinfo=UTC),
+ DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + ".%f" + TZ_BAS,
+ "2012-10-30T08:55:22.123456Z",
+ ),
+ (
+ "2014-08-18 14:55:22.123456Z",
+ None,
+ DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + ".%f" + TZ_BAS,
+ "2014-08-18T14:55:22.123456Z",
+ ),
+]
def create_testcase(datetimestring, expectation, format, output):
@@ -108,43 +133,42 @@ def create_testcase(datetimestring, expectation, format, output):
"""
class TestDateTime(unittest.TestCase):
- '''
+ """
A test case template to parse an ISO datetime string into a
datetime object.
- '''
+ """
def test_parse(self):
- '''
+ """
Parse an ISO datetime string and compare it to the expected value.
- '''
+ """
if expectation is None:
self.assertRaises(ISO8601Error, parse_datetime, datetimestring)
else:
self.assertEqual(parse_datetime(datetimestring), expectation)
def test_format(self):
- '''
+ """
Take datetime object and create ISO string from it.
This is the reverse test to test_parse.
- '''
+ """
if expectation is None:
- self.assertRaises(AttributeError,
- datetime_isoformat, expectation, format)
+ self.assertRaises(
+ AttributeError, datetime_isoformat, expectation, format
+ )
else:
- self.assertEqual(datetime_isoformat(expectation, format),
- output)
+ self.assertEqual(datetime_isoformat(expectation, format), output)
return unittest.TestLoader().loadTestsFromTestCase(TestDateTime)
def test_suite():
- '''
+ """
Construct a TestSuite instance for all test cases.
- '''
+ """
suite = unittest.TestSuite()
for datetimestring, expectation, format, output in TEST_CASES:
- suite.addTest(create_testcase(datetimestring, expectation,
- format, output))
+ suite.addTest(create_testcase(datetimestring, expectation, format, output))
return suite
@@ -153,5 +177,5 @@ def load_tests(loader, tests, pattern):
return test_suite()
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tests/test_duration.py b/src/isodate/tests/test_duration.py
index b0e0332..20c3f2d 100644
--- a/src/isodate/tests/test_duration.py
+++ b/src/isodate/tests/test_duration.py
@@ -1,32 +1,6 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Test cases for the isoduration module.
-'''
+"""
import unittest
import operator
from datetime import timedelta, date, datetime
@@ -37,47 +11,40 @@ from isodate import D_DEFAULT, D_WEEK, D_ALT_EXT, duration_isoformat
# the following list contains tuples of ISO duration strings and the expected
# result from the parse_duration method. A result of None means an ISO8601Error
# is expected.
-PARSE_TEST_CASES = {'P18Y9M4DT11H9M8S': (Duration(4, 8, 0, 0, 9, 11, 0, 9, 18),
- D_DEFAULT, None),
- 'P2W': (timedelta(weeks=2), D_WEEK, None),
- 'P3Y6M4DT12H30M5S': (Duration(4, 5, 0, 0, 30, 12, 0, 6, 3),
- D_DEFAULT, None),
- 'P23DT23H': (timedelta(hours=23, days=23),
- D_DEFAULT, None),
- 'P4Y': (Duration(years=4), D_DEFAULT, None),
- 'P1M': (Duration(months=1), D_DEFAULT, None),
- 'PT1M': (timedelta(minutes=1), D_DEFAULT, None),
- 'P0.5Y': (Duration(years=0.5), D_DEFAULT, None),
- 'PT36H': (timedelta(hours=36), D_DEFAULT, 'P1DT12H'),
- 'P1DT12H': (timedelta(days=1, hours=12), D_DEFAULT, None),
- '+P11D': (timedelta(days=11), D_DEFAULT, 'P11D'),
- '-P2W': (timedelta(weeks=-2), D_WEEK, None),
- '-P2.2W': (timedelta(weeks=-2.2), D_DEFAULT,
- '-P15DT9H36M'),
- 'P1DT2H3M4S': (timedelta(days=1, hours=2, minutes=3,
- seconds=4), D_DEFAULT, None),
- 'P1DT2H3M': (timedelta(days=1, hours=2, minutes=3),
- D_DEFAULT, None),
- 'P1DT2H': (timedelta(days=1, hours=2), D_DEFAULT, None),
- 'PT2H': (timedelta(hours=2), D_DEFAULT, None),
- 'PT2.3H': (timedelta(hours=2.3), D_DEFAULT, 'PT2H18M'),
- 'PT2H3M4S': (timedelta(hours=2, minutes=3, seconds=4),
- D_DEFAULT, None),
- 'PT3M4S': (timedelta(minutes=3, seconds=4), D_DEFAULT,
- None),
- 'PT22S': (timedelta(seconds=22), D_DEFAULT, None),
- 'PT22.22S': (timedelta(seconds=22.22), 'PT%S.%fS',
- 'PT22.220000S'),
- '-P2Y': (Duration(years=-2), D_DEFAULT, None),
- '-P3Y6M4DT12H30M5S': (Duration(-4, -5, 0, 0, -30, -12, 0,
- -6, -3), D_DEFAULT, None),
- '-P1DT2H3M4S': (timedelta(days=-1, hours=-2, minutes=-3,
- seconds=-4), D_DEFAULT, None),
- # alternative format
- 'P0018-09-04T11:09:08': (Duration(4, 8, 0, 0, 9, 11, 0, 9,
- 18), D_ALT_EXT, None),
- # 'PT000022.22': timedelta(seconds=22.22),
- }
+PARSE_TEST_CASES = {
+ "P18Y9M4DT11H9M8S": (Duration(4, 8, 0, 0, 9, 11, 0, 9, 18), D_DEFAULT, None),
+ "P2W": (timedelta(weeks=2), D_WEEK, None),
+ "P3Y6M4DT12H30M5S": (Duration(4, 5, 0, 0, 30, 12, 0, 6, 3), D_DEFAULT, None),
+ "P23DT23H": (timedelta(hours=23, days=23), D_DEFAULT, None),
+ "P4Y": (Duration(years=4), D_DEFAULT, None),
+ "P1M": (Duration(months=1), D_DEFAULT, None),
+ "PT1M": (timedelta(minutes=1), D_DEFAULT, None),
+ "P0.5Y": (Duration(years=0.5), D_DEFAULT, None),
+ "PT36H": (timedelta(hours=36), D_DEFAULT, "P1DT12H"),
+ "P1DT12H": (timedelta(days=1, hours=12), D_DEFAULT, None),
+ "+P11D": (timedelta(days=11), D_DEFAULT, "P11D"),
+ "-P2W": (timedelta(weeks=-2), D_WEEK, None),
+ "-P2.2W": (timedelta(weeks=-2.2), D_DEFAULT, "-P15DT9H36M"),
+ "P1DT2H3M4S": (timedelta(days=1, hours=2, minutes=3, seconds=4), D_DEFAULT, None),
+ "P1DT2H3M": (timedelta(days=1, hours=2, minutes=3), D_DEFAULT, None),
+ "P1DT2H": (timedelta(days=1, hours=2), D_DEFAULT, None),
+ "PT2H": (timedelta(hours=2), D_DEFAULT, None),
+ "PT2.3H": (timedelta(hours=2.3), D_DEFAULT, "PT2H18M"),
+ "PT2H3M4S": (timedelta(hours=2, minutes=3, seconds=4), D_DEFAULT, None),
+ "PT3M4S": (timedelta(minutes=3, seconds=4), D_DEFAULT, None),
+ "PT22S": (timedelta(seconds=22), D_DEFAULT, None),
+ "PT22.22S": (timedelta(seconds=22.22), "PT%S.%fS", "PT22.220000S"),
+ "-P2Y": (Duration(years=-2), D_DEFAULT, None),
+ "-P3Y6M4DT12H30M5S": (Duration(-4, -5, 0, 0, -30, -12, 0, -6, -3), D_DEFAULT, None),
+ "-P1DT2H3M4S": (
+ timedelta(days=-1, hours=-2, minutes=-3, seconds=-4),
+ D_DEFAULT,
+ None,
+ ),
+ # alternative format
+ "P0018-09-04T11:09:08": (Duration(4, 8, 0, 0, 9, 11, 0, 9, 18), D_ALT_EXT, None),
+ # 'PT000022.22': timedelta(seconds=22.22),
+}
# d1 d2 '+', '-', '>'
# A list of test cases to test addition and subtraction between datetime and
@@ -85,188 +52,174 @@ PARSE_TEST_CASES = {'P18Y9M4DT11H9M8S': (Duration(4, 8, 0, 0, 9, 11, 0, 9, 18),
# each tuple contains 2 duration strings, and a result string for addition and
# one for subtraction. The last value says, if the first duration is greater
# than the second.
-MATH_TEST_CASES = (('P5Y7M1DT9H45M16.72S', 'PT27M24.68S',
- 'P5Y7M1DT10H12M41.4S', 'P5Y7M1DT9H17M52.04S', None),
- ('PT28M12.73S', 'PT56M29.92S',
- 'PT1H24M42.65S', '-PT28M17.19S', False),
- ('P3Y7M23DT5H25M0.33S', 'PT1H1.95S',
- 'P3Y7M23DT6H25M2.28S', 'P3Y7M23DT4H24M58.38S', None),
- ('PT1H1.95S', 'P3Y7M23DT5H25M0.33S',
- 'P3Y7M23DT6H25M2.28S', '-P3Y7M23DT4H24M58.38S', None),
- ('P1332DT55M0.33S', 'PT1H1.95S',
- 'P1332DT1H55M2.28S', 'P1331DT23H54M58.38S', True),
- ('PT1H1.95S', 'P1332DT55M0.33S',
- 'P1332DT1H55M2.28S', '-P1331DT23H54M58.38S', False))
+MATH_TEST_CASES = (
+ (
+ "P5Y7M1DT9H45M16.72S",
+ "PT27M24.68S",
+ "P5Y7M1DT10H12M41.4S",
+ "P5Y7M1DT9H17M52.04S",
+ None,
+ ),
+ ("PT28M12.73S", "PT56M29.92S", "PT1H24M42.65S", "-PT28M17.19S", False),
+ (
+ "P3Y7M23DT5H25M0.33S",
+ "PT1H1.95S",
+ "P3Y7M23DT6H25M2.28S",
+ "P3Y7M23DT4H24M58.38S",
+ None,
+ ),
+ (
+ "PT1H1.95S",
+ "P3Y7M23DT5H25M0.33S",
+ "P3Y7M23DT6H25M2.28S",
+ "-P3Y7M23DT4H24M58.38S",
+ None,
+ ),
+ ("P1332DT55M0.33S", "PT1H1.95S", "P1332DT1H55M2.28S", "P1331DT23H54M58.38S", True),
+ (
+ "PT1H1.95S",
+ "P1332DT55M0.33S",
+ "P1332DT1H55M2.28S",
+ "-P1331DT23H54M58.38S",
+ False,
+ ),
+)
# A list of test cases to test addition and subtraction of date/datetime
# and Duration objects. They are tested against the results of an
# equal long timedelta duration.
-DATE_TEST_CASES = ((date(2008, 2, 29),
- timedelta(days=10, hours=12, minutes=20),
- Duration(days=10, hours=12, minutes=20)),
- (date(2008, 1, 31),
- timedelta(days=10, hours=12, minutes=20),
- Duration(days=10, hours=12, minutes=20)),
- (datetime(2008, 2, 29),
- timedelta(days=10, hours=12, minutes=20),
- Duration(days=10, hours=12, minutes=20)),
- (datetime(2008, 1, 31),
- timedelta(days=10, hours=12, minutes=20),
- Duration(days=10, hours=12, minutes=20)),
- (datetime(2008, 4, 21),
- timedelta(days=10, hours=12, minutes=20),
- Duration(days=10, hours=12, minutes=20)),
- (datetime(2008, 5, 5),
- timedelta(days=10, hours=12, minutes=20),
- Duration(days=10, hours=12, minutes=20)),
- (datetime(2000, 1, 1),
- timedelta(hours=-33),
- Duration(hours=-33)),
- (datetime(2008, 5, 5),
- Duration(years=1, months=1, days=10, hours=12,
- minutes=20),
- Duration(months=13, days=10, hours=12, minutes=20)),
- (datetime(2000, 3, 30),
- Duration(years=1, months=1, days=10, hours=12,
- minutes=20),
- Duration(months=13, days=10, hours=12, minutes=20)),
- )
-
-# A list of test cases of additon of date/datetime and Duration. The results
+DATE_TEST_CASES = (
+ (
+ date(2008, 2, 29),
+ timedelta(days=10, hours=12, minutes=20),
+ Duration(days=10, hours=12, minutes=20),
+ ),
+ (
+ date(2008, 1, 31),
+ timedelta(days=10, hours=12, minutes=20),
+ Duration(days=10, hours=12, minutes=20),
+ ),
+ (
+ datetime(2008, 2, 29),
+ timedelta(days=10, hours=12, minutes=20),
+ Duration(days=10, hours=12, minutes=20),
+ ),
+ (
+ datetime(2008, 1, 31),
+ timedelta(days=10, hours=12, minutes=20),
+ Duration(days=10, hours=12, minutes=20),
+ ),
+ (
+ datetime(2008, 4, 21),
+ timedelta(days=10, hours=12, minutes=20),
+ Duration(days=10, hours=12, minutes=20),
+ ),
+ (
+ datetime(2008, 5, 5),
+ timedelta(days=10, hours=12, minutes=20),
+ Duration(days=10, hours=12, minutes=20),
+ ),
+ (datetime(2000, 1, 1), timedelta(hours=-33), Duration(hours=-33)),
+ (
+ datetime(2008, 5, 5),
+ Duration(years=1, months=1, days=10, hours=12, minutes=20),
+ Duration(months=13, days=10, hours=12, minutes=20),
+ ),
+ (
+ datetime(2000, 3, 30),
+ Duration(years=1, months=1, days=10, hours=12, minutes=20),
+ Duration(months=13, days=10, hours=12, minutes=20),
+ ),
+)
+
+# A list of test cases of addition of date/datetime and Duration. The results
# are compared against a given expected result.
DATE_CALC_TEST_CASES = (
- (date(2000, 2, 1),
- Duration(years=1, months=1),
- date(2001, 3, 1)),
- (date(2000, 2, 29),
- Duration(years=1, months=1),
- date(2001, 3, 29)),
- (date(2000, 2, 29),
- Duration(years=1),
- date(2001, 2, 28)),
- (date(1996, 2, 29),
- Duration(years=4),
- date(2000, 2, 29)),
- (date(2096, 2, 29),
- Duration(years=4),
- date(2100, 2, 28)),
- (date(2000, 2, 1),
- Duration(years=-1, months=-1),
- date(1999, 1, 1)),
- (date(2000, 2, 29),
- Duration(years=-1, months=-1),
- date(1999, 1, 29)),
- (date(2000, 2, 1),
- Duration(years=1, months=1, days=1),
- date(2001, 3, 2)),
- (date(2000, 2, 29),
- Duration(years=1, months=1, days=1),
- date(2001, 3, 30)),
- (date(2000, 2, 29),
- Duration(years=1, days=1),
- date(2001, 3, 1)),
- (date(1996, 2, 29),
- Duration(years=4, days=1),
- date(2000, 3, 1)),
- (date(2096, 2, 29),
- Duration(years=4, days=1),
- date(2100, 3, 1)),
- (date(2000, 2, 1),
- Duration(years=-1, months=-1, days=-1),
- date(1998, 12, 31)),
- (date(2000, 2, 29),
- Duration(years=-1, months=-1, days=-1),
- date(1999, 1, 28)),
- (date(2001, 4, 1),
- Duration(years=-1, months=-1, days=-1),
- date(2000, 2, 29)),
- (date(2000, 4, 1),
- Duration(years=-1, months=-1, days=-1),
- date(1999, 2, 28)),
- (Duration(years=1, months=2),
- Duration(years=0, months=0, days=1),
- Duration(years=1, months=2, days=1)),
- (Duration(years=-1, months=-1, days=-1),
- date(2000, 4, 1),
- date(1999, 2, 28)),
- (Duration(years=1, months=1, weeks=5),
- date(2000, 1, 30),
- date(2001, 4, 4)),
- (parse_duration("P1Y1M5W"),
- date(2000, 1, 30),
- date(2001, 4, 4)),
- (parse_duration("P0.5Y"),
- date(2000, 1, 30),
- None),
- (Duration(years=1, months=1, hours=3),
- datetime(2000, 1, 30, 12, 15, 00),
- datetime(2001, 2, 28, 15, 15, 00)),
- (parse_duration("P1Y1MT3H"),
- datetime(2000, 1, 30, 12, 15, 00),
- datetime(2001, 2, 28, 15, 15, 00)),
- (Duration(years=1, months=2),
- timedelta(days=1),
- Duration(years=1, months=2, days=1)),
- (timedelta(days=1),
- Duration(years=1, months=2),
- Duration(years=1, months=2, days=1)),
- (datetime(2008, 1, 1, 0, 2),
- Duration(months=1),
- datetime(2008, 2, 1, 0, 2)),
- (datetime.strptime("200802", "%Y%M"),
- parse_duration("P1M"),
- datetime(2008, 2, 1, 0, 2)),
- (datetime(2008, 2, 1),
- Duration(months=1),
- datetime(2008, 3, 1)),
- (datetime.strptime("200802", "%Y%m"),
- parse_duration("P1M"),
- datetime(2008, 3, 1)),
+ (date(2000, 2, 1), Duration(years=1, months=1), date(2001, 3, 1)),
+ (date(2000, 2, 29), Duration(years=1, months=1), date(2001, 3, 29)),
+ (date(2000, 2, 29), Duration(years=1), date(2001, 2, 28)),
+ (date(1996, 2, 29), Duration(years=4), date(2000, 2, 29)),
+ (date(2096, 2, 29), Duration(years=4), date(2100, 2, 28)),
+ (date(2000, 2, 1), Duration(years=-1, months=-1), date(1999, 1, 1)),
+ (date(2000, 2, 29), Duration(years=-1, months=-1), date(1999, 1, 29)),
+ (date(2000, 2, 1), Duration(years=1, months=1, days=1), date(2001, 3, 2)),
+ (date(2000, 2, 29), Duration(years=1, months=1, days=1), date(2001, 3, 30)),
+ (date(2000, 2, 29), Duration(years=1, days=1), date(2001, 3, 1)),
+ (date(1996, 2, 29), Duration(years=4, days=1), date(2000, 3, 1)),
+ (date(2096, 2, 29), Duration(years=4, days=1), date(2100, 3, 1)),
+ (date(2000, 2, 1), Duration(years=-1, months=-1, days=-1), date(1998, 12, 31)),
+ (date(2000, 2, 29), Duration(years=-1, months=-1, days=-1), date(1999, 1, 28)),
+ (date(2001, 4, 1), Duration(years=-1, months=-1, days=-1), date(2000, 2, 29)),
+ (date(2000, 4, 1), Duration(years=-1, months=-1, days=-1), date(1999, 2, 28)),
+ (
+ Duration(years=1, months=2),
+ Duration(years=0, months=0, days=1),
+ Duration(years=1, months=2, days=1),
+ ),
+ (Duration(years=-1, months=-1, days=-1), date(2000, 4, 1), date(1999, 2, 28)),
+ (Duration(years=1, months=1, weeks=5), date(2000, 1, 30), date(2001, 4, 4)),
+ (parse_duration("P1Y1M5W"), date(2000, 1, 30), date(2001, 4, 4)),
+ (parse_duration("P0.5Y"), date(2000, 1, 30), None),
+ (
+ Duration(years=1, months=1, hours=3),
+ datetime(2000, 1, 30, 12, 15, 00),
+ datetime(2001, 2, 28, 15, 15, 00),
+ ),
+ (
+ parse_duration("P1Y1MT3H"),
+ datetime(2000, 1, 30, 12, 15, 00),
+ datetime(2001, 2, 28, 15, 15, 00),
+ ),
+ (
+ Duration(years=1, months=2),
+ timedelta(days=1),
+ Duration(years=1, months=2, days=1),
+ ),
+ (
+ timedelta(days=1),
+ Duration(years=1, months=2),
+ Duration(years=1, months=2, days=1),
+ ),
+ (datetime(2008, 1, 1, 0, 2), Duration(months=1), datetime(2008, 2, 1, 0, 2)),
+ (
+ datetime.strptime("200802", "%Y%M"),
+ parse_duration("P1M"),
+ datetime(2008, 2, 1, 0, 2),
+ ),
+ (datetime(2008, 2, 1), Duration(months=1), datetime(2008, 3, 1)),
+ (datetime.strptime("200802", "%Y%m"), parse_duration("P1M"), datetime(2008, 3, 1)),
# (date(2000, 1, 1),
# Duration(years=1.5),
# date(2001, 6, 1)),
# (date(2000, 1, 1),
# Duration(years=1, months=1.5),
# date(2001, 2, 14)),
- )
+)
# A list of test cases of multiplications of durations
# are compared against a given expected result.
DATE_MUL_TEST_CASES = (
- (Duration(years=1, months=1),
- 3,
- Duration(years=3, months=3)),
- (Duration(years=1, months=1),
- -3,
- Duration(years=-3, months=-3)),
- (3,
- Duration(years=1, months=1),
- Duration(years=3, months=3)),
- (-3,
- Duration(years=1, months=1),
- Duration(years=-3, months=-3)),
- (5,
- Duration(years=2, minutes=40),
- Duration(years=10, hours=3, minutes=20)),
- (-5,
- Duration(years=2, minutes=40),
- Duration(years=-10, hours=-3, minutes=-20)),
- (7,
- Duration(years=1, months=2, weeks=40),
- Duration(years=8, months=2, weeks=280)))
+ (Duration(years=1, months=1), 3, Duration(years=3, months=3)),
+ (Duration(years=1, months=1), -3, Duration(years=-3, months=-3)),
+ (3, Duration(years=1, months=1), Duration(years=3, months=3)),
+ (-3, Duration(years=1, months=1), Duration(years=-3, months=-3)),
+ (5, Duration(years=2, minutes=40), Duration(years=10, hours=3, minutes=20)),
+ (-5, Duration(years=2, minutes=40), Duration(years=-10, hours=-3, minutes=-20)),
+ (7, Duration(years=1, months=2, weeks=40), Duration(years=8, months=2, weeks=280)),
+)
class DurationTest(unittest.TestCase):
- '''
+ """
This class tests various other aspects of the isoduration module,
which are not covered with the test cases listed above.
- '''
+ """
def test_associative(self):
- '''
+ """
Adding 2 durations to a date is not associative.
- '''
+ """
days1 = Duration(days=1)
months1 = Duration(months=1)
start = date(2000, 3, 30)
@@ -275,53 +228,67 @@ class DurationTest(unittest.TestCase):
self.assertNotEqual(res1, res2)
def test_typeerror(self):
- '''
+ """
Test if TypError is raised with certain parameters.
- '''
+ """
self.assertRaises(TypeError, parse_duration, date(2000, 1, 1))
- self.assertRaises(TypeError, operator.sub, Duration(years=1),
- date(2000, 1, 1))
- self.assertRaises(TypeError, operator.sub, 'raise exc',
- Duration(years=1))
- self.assertRaises(TypeError, operator.add,
- Duration(years=1, months=1, weeks=5),
- 'raise exception')
- self.assertRaises(TypeError, operator.add, 'raise exception',
- Duration(years=1, months=1, weeks=5))
- self.assertRaises(TypeError, operator.mul,
- Duration(years=1, months=1, weeks=5),
- 'raise exception')
- self.assertRaises(TypeError, operator.mul, 'raise exception',
- Duration(years=1, months=1, weeks=5))
- self.assertRaises(TypeError, operator.mul,
- Duration(years=1, months=1, weeks=5),
- 3.14)
- self.assertRaises(TypeError, operator.mul, 3.14,
- Duration(years=1, months=1, weeks=5))
+ self.assertRaises(TypeError, operator.sub, Duration(years=1), date(2000, 1, 1))
+ self.assertRaises(TypeError, operator.sub, "raise exc", Duration(years=1))
+ self.assertRaises(
+ TypeError,
+ operator.add,
+ Duration(years=1, months=1, weeks=5),
+ "raise exception",
+ )
+ self.assertRaises(
+ TypeError,
+ operator.add,
+ "raise exception",
+ Duration(years=1, months=1, weeks=5),
+ )
+ self.assertRaises(
+ TypeError,
+ operator.mul,
+ Duration(years=1, months=1, weeks=5),
+ "raise exception",
+ )
+ self.assertRaises(
+ TypeError,
+ operator.mul,
+ "raise exception",
+ Duration(years=1, months=1, weeks=5),
+ )
+ self.assertRaises(
+ TypeError, operator.mul, Duration(years=1, months=1, weeks=5), 3.14
+ )
+ self.assertRaises(
+ TypeError, operator.mul, 3.14, Duration(years=1, months=1, weeks=5)
+ )
def test_parseerror(self):
- '''
- Test for unparseable duration string.
- '''
- self.assertRaises(ISO8601Error, parse_duration, 'T10:10:10')
+ """
+ Test for unparsable duration string.
+ """
+ self.assertRaises(ISO8601Error, parse_duration, "T10:10:10")
def test_repr(self):
- '''
+ """
Test __repr__ and __str__ for Duration objects.
- '''
+ """
dur = Duration(10, 10, years=10, months=10)
- self.assertEqual('10 years, 10 months, 10 days, 0:00:10', str(dur))
- self.assertEqual('isodate.duration.Duration(10, 10, 0,'
- ' years=10, months=10)', repr(dur))
+ self.assertEqual("10 years, 10 months, 10 days, 0:00:10", str(dur))
+ self.assertEqual(
+ "isodate.duration.Duration(10, 10, 0," " years=10, months=10)", repr(dur)
+ )
dur = Duration(months=0)
- self.assertEqual('0:00:00', str(dur))
+ self.assertEqual("0:00:00", str(dur))
dur = Duration(months=1)
- self.assertEqual('1 month, 0:00:00', str(dur))
+ self.assertEqual("1 month, 0:00:00", str(dur))
def test_hash(self):
- '''
+ """
Test __hash__ for Duration objects.
- '''
+ """
dur1 = Duration(10, 10, years=10, months=10)
dur2 = Duration(9, 9, years=9, months=9)
dur3 = Duration(10, 10, years=10, months=10)
@@ -336,12 +303,11 @@ class DurationTest(unittest.TestCase):
self.assertEqual(len(durSet), 2)
def test_neg(self):
- '''
+ """
Test __neg__ for Duration objects.
- '''
+ """
self.assertEqual(-Duration(0), Duration(0))
- self.assertEqual(-Duration(years=1, months=1),
- Duration(years=-1, months=-1))
+ self.assertEqual(-Duration(years=1, months=1), Duration(years=-1, months=-1))
self.assertEqual(-Duration(years=1, months=1), Duration(months=-13))
self.assertNotEqual(-Duration(years=1), timedelta(days=-365))
self.assertNotEqual(-timedelta(days=365), Duration(years=-1))
@@ -350,41 +316,34 @@ class DurationTest(unittest.TestCase):
# self.assertNotEqual(-timedelta(days=10), -Duration(days=10))
def test_format(self):
- '''
+ """
Test various other strftime combinations.
- '''
- self.assertEqual(duration_isoformat(Duration(0)), 'P0D')
- self.assertEqual(duration_isoformat(-Duration(0)), 'P0D')
- self.assertEqual(duration_isoformat(Duration(seconds=10)), 'PT10S')
- self.assertEqual(duration_isoformat(Duration(years=-1, months=-1)),
- '-P1Y1M')
- self.assertEqual(duration_isoformat(-Duration(years=1, months=1)),
- '-P1Y1M')
- self.assertEqual(duration_isoformat(-Duration(years=-1, months=-1)),
- 'P1Y1M')
- self.assertEqual(duration_isoformat(-Duration(years=-1, months=-1)),
- 'P1Y1M')
- dur = Duration(years=3, months=7, days=23, hours=5, minutes=25,
- milliseconds=330)
- self.assertEqual(duration_isoformat(dur), 'P3Y7M23DT5H25M0.33S')
- self.assertEqual(duration_isoformat(-dur), '-P3Y7M23DT5H25M0.33S')
+ """
+ self.assertEqual(duration_isoformat(Duration(0)), "P0D")
+ self.assertEqual(duration_isoformat(-Duration(0)), "P0D")
+ self.assertEqual(duration_isoformat(Duration(seconds=10)), "PT10S")
+ self.assertEqual(duration_isoformat(Duration(years=-1, months=-1)), "-P1Y1M")
+ self.assertEqual(duration_isoformat(-Duration(years=1, months=1)), "-P1Y1M")
+ self.assertEqual(duration_isoformat(-Duration(years=-1, months=-1)), "P1Y1M")
+ self.assertEqual(duration_isoformat(-Duration(years=-1, months=-1)), "P1Y1M")
+ dur = Duration(
+ years=3, months=7, days=23, hours=5, minutes=25, milliseconds=330
+ )
+ self.assertEqual(duration_isoformat(dur), "P3Y7M23DT5H25M0.33S")
+ self.assertEqual(duration_isoformat(-dur), "-P3Y7M23DT5H25M0.33S")
def test_equal(self):
- '''
+ """
Test __eq__ and __ne__ methods.
- '''
- self.assertEqual(Duration(years=1, months=1),
- Duration(years=1, months=1))
+ """
+ self.assertEqual(Duration(years=1, months=1), Duration(years=1, months=1))
self.assertEqual(Duration(years=1, months=1), Duration(months=13))
- self.assertNotEqual(Duration(years=1, months=2),
- Duration(years=1, months=1))
+ self.assertNotEqual(Duration(years=1, months=2), Duration(years=1, months=1))
self.assertNotEqual(Duration(years=1, months=1), Duration(months=14))
self.assertNotEqual(Duration(years=1), timedelta(days=365))
- self.assertFalse(Duration(years=1, months=1) !=
- Duration(years=1, months=1))
+ self.assertFalse(Duration(years=1, months=1) != Duration(years=1, months=1))
self.assertFalse(Duration(years=1, months=1) != Duration(months=13))
- self.assertTrue(Duration(years=1, months=2) !=
- Duration(years=1, months=1))
+ self.assertTrue(Duration(years=1, months=2) != Duration(years=1, months=1))
self.assertTrue(Duration(years=1, months=1) != Duration(months=14))
self.assertTrue(Duration(years=1) != timedelta(days=365))
self.assertEqual(Duration(days=1), timedelta(days=1))
@@ -393,15 +352,13 @@ class DurationTest(unittest.TestCase):
# self.assertNotEqual(timedelta(days=1), Duration(days=1))
def test_totimedelta(self):
- '''
+ """
Test conversion form Duration to timedelta.
- '''
+ """
dur = Duration(years=1, months=2, days=10)
- self.assertEqual(dur.totimedelta(datetime(1998, 2, 25)),
- timedelta(434))
+ self.assertEqual(dur.totimedelta(datetime(1998, 2, 25)), timedelta(434))
# leap year has one day more in february
- self.assertEqual(dur.totimedelta(datetime(2000, 2, 25)),
- timedelta(435))
+ self.assertEqual(dur.totimedelta(datetime(2000, 2, 25)), timedelta(435))
dur = Duration(months=2)
# march is longer than february, but april is shorter than
# march (cause only one day difference compared to 2)
@@ -419,31 +376,31 @@ def create_parsetestcase(durationstring, expectation, format, altstr):
"""
class TestParseDuration(unittest.TestCase):
- '''
+ """
A test case template to parse an ISO duration string into a
timedelta or Duration object.
- '''
+ """
def test_parse(self):
- '''
+ """
Parse an ISO duration string and compare it to the expected value.
- '''
+ """
result = parse_duration(durationstring)
self.assertEqual(result, expectation)
def test_format(self):
- '''
+ """
Take duration/timedelta object and create ISO string from it.
This is the reverse test to test_parse.
- '''
+ """
if altstr:
- self.assertEqual(duration_isoformat(expectation, format),
- altstr)
+ self.assertEqual(duration_isoformat(expectation, format), altstr)
else:
# if durationstring == '-P2W':
# import pdb; pdb.set_trace()
- self.assertEqual(duration_isoformat(expectation, format),
- durationstring)
+ self.assertEqual(
+ duration_isoformat(expectation, format), durationstring
+ )
return unittest.TestLoader().loadTestsFromTestCase(TestParseDuration)
@@ -462,34 +419,36 @@ def create_mathtestcase(dur1, dur2, resadd, ressub, resge):
ressub = parse_duration(ressub)
class TestMathDuration(unittest.TestCase):
- '''
+ """
A test case template test addition, subtraction and >
operators for Duration objects.
- '''
+ """
def test_add(self):
- '''
+ """
Test operator + (__add__, __radd__)
- '''
+ """
self.assertEqual(dur1 + dur2, resadd)
def test_sub(self):
- '''
+ """
Test operator - (__sub__, __rsub__)
- '''
+ """
self.assertEqual(dur1 - dur2, ressub)
def test_ge(self):
- '''
+ """
Test operator > and <
- '''
+ """
+
def dogetest():
- ''' Test greater than.'''
+ """Test greater than."""
return dur1 > dur2
def doletest():
- ''' Test less than.'''
+ """Test less than."""
return dur1 < dur2
+
if resge is None:
self.assertRaises(TypeError, dogetest)
self.assertRaises(TypeError, doletest)
@@ -509,21 +468,21 @@ def create_datetestcase(start, tdelta, duration):
"""
class TestDateCalc(unittest.TestCase):
- '''
+ """
A test case template test addition, subtraction
operators for Duration objects.
- '''
+ """
def test_add(self):
- '''
+ """
Test operator +.
- '''
+ """
self.assertEqual(start + tdelta, start + duration)
def test_sub(self):
- '''
+ """
Test operator -.
- '''
+ """
self.assertEqual(start - tdelta, start - duration)
return unittest.TestLoader().loadTestsFromTestCase(TestDateCalc)
@@ -538,14 +497,14 @@ def create_datecalctestcase(start, duration, expectation):
"""
class TestDateCalc(unittest.TestCase):
- '''
+ """
A test case template test addition operators for Duration objects.
- '''
+ """
def test_calc(self):
- '''
+ """
Test operator +.
- '''
+ """
if expectation is None:
self.assertRaises(ValueError, operator.add, start, duration)
else:
@@ -563,28 +522,26 @@ def create_datemultestcase(operand1, operand2, expectation):
"""
class TestDateMul(unittest.TestCase):
- '''
+ """
A test case template test addition operators for Duration objects.
- '''
+ """
def test_mul(self):
- '''
+ """
Test operator *.
- '''
+ """
self.assertEqual(operand1 * operand2, expectation)
return unittest.TestLoader().loadTestsFromTestCase(TestDateMul)
def test_suite():
- '''
+ """
Return a test suite containing all test defined above.
- '''
+ """
suite = unittest.TestSuite()
- for durationstring, (expectation, format,
- altstr) in PARSE_TEST_CASES.items():
- suite.addTest(create_parsetestcase(durationstring, expectation,
- format, altstr))
+ for durationstring, (expectation, format, altstr) in PARSE_TEST_CASES.items():
+ suite.addTest(create_parsetestcase(durationstring, expectation, format, altstr))
for testdata in MATH_TEST_CASES:
suite.addTest(create_mathtestcase(*testdata))
for testdata in DATE_TEST_CASES:
@@ -602,5 +559,5 @@ def load_tests(loader, tests, pattern):
return test_suite()
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tests/test_pickle.py b/src/isodate/tests/test_pickle.py
index 7fc6213..01b56e6 100644
--- a/src/isodate/tests/test_pickle.py
+++ b/src/isodate/tests/test_pickle.py
@@ -1,31 +1,31 @@
import unittest
-from six.moves import cPickle as pickle
+import pickle
import isodate
class TestPickle(unittest.TestCase):
- '''
+ """
A test case template to parse an ISO datetime string into a
datetime object.
- '''
+ """
def test_pickle_datetime(self):
- '''
+ """
Parse an ISO datetime string and compare it to the expected value.
- '''
- dti = isodate.parse_datetime('2012-10-26T09:33+00:00')
+ """
+ dti = isodate.parse_datetime("2012-10-26T09:33+00:00")
for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
pikl = pickle.dumps(dti, proto)
- self.assertEqual(dti, pickle.loads(pikl),
- "pickle proto %d failed" % proto)
+ self.assertEqual(dti, pickle.loads(pikl), "pickle proto %d failed" % proto)
def test_pickle_duration(self):
- '''
+ """
Pickle / unpickle duration objects.
- '''
+ """
from isodate.duration import Duration
+
dur = Duration()
failed = []
for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
@@ -35,20 +35,19 @@ class TestPickle(unittest.TestCase):
raise Exception("not equal")
except Exception as e:
failed.append("pickle proto %d failed (%s)" % (proto, repr(e)))
- self.assertEqual(len(failed), 0, "pickle protos failed: %s" %
- str(failed))
+ self.assertEqual(len(failed), 0, "pickle protos failed: %s" % str(failed))
def test_pickle_utc(self):
- '''
+ """
isodate.UTC objects remain the same after pickling.
- '''
+ """
self.assertTrue(isodate.UTC is pickle.loads(pickle.dumps(isodate.UTC)))
def test_suite():
- '''
+ """
Construct a TestSuite instance for all test cases.
- '''
+ """
suite = unittest.TestSuite()
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestPickle))
return suite
@@ -59,5 +58,5 @@ def load_tests(loader, tests, pattern):
return test_suite()
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tests/test_strf.py b/src/isodate/tests/test_strf.py
index 1aa76c7..2e2b246 100644
--- a/src/isodate/tests/test_strf.py
+++ b/src/isodate/tests/test_strf.py
@@ -1,32 +1,6 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Test cases for the isodate module.
-'''
+"""
import unittest
import time
from datetime import datetime, timedelta
@@ -36,19 +10,30 @@ from isodate import DT_EXT_COMPLETE
from isodate import tzinfo
-TEST_CASES = ((datetime(2012, 12, 25, 13, 30, 0, 0, LOCAL), DT_EXT_COMPLETE,
- "2012-12-25T13:30:00+10:00"),
- # DST ON
- (datetime(1999, 12, 25, 13, 30, 0, 0, LOCAL), DT_EXT_COMPLETE,
- "1999-12-25T13:30:00+11:00"),
- # microseconds
- (datetime(2012, 10, 12, 8, 29, 46, 69178),
- "%Y-%m-%dT%H:%M:%S.%f",
- "2012-10-12T08:29:46.069178"),
- (datetime(2012, 10, 12, 8, 29, 46, 691780),
- "%Y-%m-%dT%H:%M:%S.%f",
- "2012-10-12T08:29:46.691780"),
- )
+TEST_CASES = (
+ (
+ datetime(2012, 12, 25, 13, 30, 0, 0, LOCAL),
+ DT_EXT_COMPLETE,
+ "2012-12-25T13:30:00+10:00",
+ ),
+ # DST ON
+ (
+ datetime(1999, 12, 25, 13, 30, 0, 0, LOCAL),
+ DT_EXT_COMPLETE,
+ "1999-12-25T13:30:00+11:00",
+ ),
+ # microseconds
+ (
+ datetime(2012, 10, 12, 8, 29, 46, 69178),
+ "%Y-%m-%dT%H:%M:%S.%f",
+ "2012-10-12T08:29:46.069178",
+ ),
+ (
+ datetime(2012, 10, 12, 8, 29, 46, 691780),
+ "%Y-%m-%dT%H:%M:%S.%f",
+ "2012-10-12T08:29:46.691780",
+ ),
+)
def create_testcase(dt, format, expectation):
@@ -60,9 +45,9 @@ def create_testcase(dt, format, expectation):
"""
class TestDate(unittest.TestCase):
- '''
+ """
A test case template to test ISO date formatting.
- '''
+ """
# local time zone mock function
def localtime_mock(self, secs):
@@ -70,24 +55,32 @@ def create_testcase(dt, format, expectation):
mock time.localtime so that it always returns a time_struct with
tm_idst=1
"""
- tt = self.ORIG['localtime'](secs)
- # befor 2000 everything is dst, after 2000 no dst.
+ tt = self.ORIG["localtime"](secs)
+ # before 2000 everything is dst, after 2000 no dst.
if tt.tm_year < 2000:
dst = 1
else:
dst = 0
- tt = (tt.tm_year, tt.tm_mon, tt.tm_mday,
- tt.tm_hour, tt.tm_min, tt.tm_sec,
- tt.tm_wday, tt.tm_yday, dst)
+ tt = (
+ tt.tm_year,
+ tt.tm_mon,
+ tt.tm_mday,
+ tt.tm_hour,
+ tt.tm_min,
+ tt.tm_sec,
+ tt.tm_wday,
+ tt.tm_yday,
+ dst,
+ )
return time.struct_time(tt)
def setUp(self):
self.ORIG = {}
- self.ORIG['STDOFFSET'] = tzinfo.STDOFFSET
- self.ORIG['DSTOFFSET'] = tzinfo.DSTOFFSET
- self.ORIG['DSTDIFF'] = tzinfo.DSTDIFF
- self.ORIG['localtime'] = time.localtime
- # ovveride all saved values with fixtures.
+ self.ORIG["STDOFFSET"] = tzinfo.STDOFFSET
+ self.ORIG["DSTOFFSET"] = tzinfo.DSTOFFSET
+ self.ORIG["DSTDIFF"] = tzinfo.DSTDIFF
+ self.ORIG["localtime"] = time.localtime
+ # override all saved values with fixtures.
# calculate LOCAL TZ offset, so that this test runs in
# every time zone
tzinfo.STDOFFSET = timedelta(seconds=36000) # assume LOC = +10:00
@@ -97,30 +90,28 @@ def create_testcase(dt, format, expectation):
def tearDown(self):
# restore test fixtures
- tzinfo.STDOFFSET = self.ORIG['STDOFFSET']
- tzinfo.DSTOFFSET = self.ORIG['DSTOFFSET']
- tzinfo.DSTDIFF = self.ORIG['DSTDIFF']
- time.localtime = self.ORIG['localtime']
+ tzinfo.STDOFFSET = self.ORIG["STDOFFSET"]
+ tzinfo.DSTOFFSET = self.ORIG["DSTOFFSET"]
+ tzinfo.DSTDIFF = self.ORIG["DSTDIFF"]
+ time.localtime = self.ORIG["localtime"]
def test_format(self):
- '''
+ """
Take date object and create ISO string from it.
This is the reverse test to test_parse.
- '''
+ """
if expectation is None:
- self.assertRaises(AttributeError,
- strftime(dt, format))
+ self.assertRaises(AttributeError, strftime(dt, format))
else:
- self.assertEqual(strftime(dt, format),
- expectation)
+ self.assertEqual(strftime(dt, format), expectation)
return unittest.TestLoader().loadTestsFromTestCase(TestDate)
def test_suite():
- '''
+ """
Construct a TestSuite instance for all test cases.
- '''
+ """
suite = unittest.TestSuite()
for dt, format, expectation in TEST_CASES:
suite.addTest(create_testcase(dt, format, expectation))
@@ -132,5 +123,5 @@ def load_tests(loader, tests, pattern):
return test_suite()
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tests/test_time.py b/src/isodate/tests/test_time.py
index fe96dab..c843bc9 100644
--- a/src/isodate/tests/test_time.py
+++ b/src/isodate/tests/test_time.py
@@ -1,32 +1,6 @@
-##############################################################################
-# Copyright 2009, Gerhard Weis
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the authors nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT
-##############################################################################
-'''
+"""
Test cases for the isotime module.
-'''
+"""
import unittest
from datetime import time
@@ -39,57 +13,83 @@ from isodate import TZ_BAS, TZ_EXT, TZ_HOUR
# the following list contains tuples of ISO time strings and the expected
# result from the parse_time method. A result of None means an ISO8601Error
# is expected.
-TEST_CASES = [('232050', time(23, 20, 50), TIME_BAS_COMPLETE + TZ_BAS),
- ('23:20:50', time(23, 20, 50), TIME_EXT_COMPLETE + TZ_EXT),
- ('2320', time(23, 20), TIME_BAS_MINUTE),
- ('23:20', time(23, 20), TIME_EXT_MINUTE),
- ('23', time(23), TIME_HOUR),
- ('232050,5', time(23, 20, 50, 500000), None),
- ('23:20:50.5', time(23, 20, 50, 500000), None),
- # test precision
- ('15:33:42.123456', time(15, 33, 42, 123456), None),
- ('15:33:42.1234564', time(15, 33, 42, 123456), None),
- ('15:33:42.1234557', time(15, 33, 42, 123456), None),
- ('2320,8', time(23, 20, 48), None),
- ('23:20,8', time(23, 20, 48), None),
- ('23,3', time(23, 18), None),
- ('232030Z', time(23, 20, 30, tzinfo=UTC),
- TIME_BAS_COMPLETE + TZ_BAS),
- ('2320Z', time(23, 20, tzinfo=UTC), TIME_BAS_MINUTE + TZ_BAS),
- ('23Z', time(23, tzinfo=UTC), TIME_HOUR + TZ_BAS),
- ('23:20:30Z', time(23, 20, 30, tzinfo=UTC),
- TIME_EXT_COMPLETE + TZ_EXT),
- ('23:20Z', time(23, 20, tzinfo=UTC), TIME_EXT_MINUTE + TZ_EXT),
- ('152746+0100', time(15, 27, 46,
- tzinfo=FixedOffset(1, 0, '+0100')), TIME_BAS_COMPLETE + TZ_BAS),
- ('152746-0500', time(15, 27, 46,
- tzinfo=FixedOffset(-5, 0, '-0500')),
- TIME_BAS_COMPLETE + TZ_BAS),
- ('152746+01', time(15, 27, 46,
- tzinfo=FixedOffset(1, 0, '+01:00')),
- TIME_BAS_COMPLETE + TZ_HOUR),
- ('152746-05', time(15, 27, 46,
- tzinfo=FixedOffset(-5, -0, '-05:00')),
- TIME_BAS_COMPLETE + TZ_HOUR),
- ('15:27:46+01:00', time(15, 27, 46,
- tzinfo=FixedOffset(1, 0, '+01:00')),
- TIME_EXT_COMPLETE + TZ_EXT),
- ('15:27:46-05:00', time(15, 27, 46,
- tzinfo=FixedOffset(-5, -0, '-05:00')),
- TIME_EXT_COMPLETE + TZ_EXT),
- ('15:27:46+01', time(15, 27, 46,
- tzinfo=FixedOffset(1, 0, '+01:00')),
- TIME_EXT_COMPLETE + TZ_HOUR),
- ('15:27:46-05', time(15, 27, 46,
- tzinfo=FixedOffset(-5, -0, '-05:00')),
- TIME_EXT_COMPLETE + TZ_HOUR),
- ('15:27:46-05:30', time(15, 27, 46,
- tzinfo=FixedOffset(-5, -30, '-05:30')),
- TIME_EXT_COMPLETE + TZ_EXT),
- ('15:27:46-0545', time(15, 27, 46,
- tzinfo=FixedOffset(-5, -45, '-0545')),
- TIME_EXT_COMPLETE + TZ_BAS),
- ('1:17:30', None, TIME_EXT_COMPLETE)]
+TEST_CASES = [
+ ("232050", time(23, 20, 50), TIME_BAS_COMPLETE + TZ_BAS),
+ ("23:20:50", time(23, 20, 50), TIME_EXT_COMPLETE + TZ_EXT),
+ ("2320", time(23, 20), TIME_BAS_MINUTE),
+ ("23:20", time(23, 20), TIME_EXT_MINUTE),
+ ("23", time(23), TIME_HOUR),
+ ("232050,5", time(23, 20, 50, 500000), None),
+ ("23:20:50.5", time(23, 20, 50, 500000), None),
+ # test precision
+ ("15:33:42.123456", time(15, 33, 42, 123456), None),
+ ("15:33:42.1234564", time(15, 33, 42, 123456), None),
+ ("15:33:42.1234557", time(15, 33, 42, 123455), None),
+ (
+ "10:59:59.9999999Z",
+ time(10, 59, 59, 999999, tzinfo=UTC),
+ None,
+ ), # TIME_EXT_COMPLETE + TZ_EXT),
+ ("2320,8", time(23, 20, 48), None),
+ ("23:20,8", time(23, 20, 48), None),
+ ("23,3", time(23, 18), None),
+ ("232030Z", time(23, 20, 30, tzinfo=UTC), TIME_BAS_COMPLETE + TZ_BAS),
+ ("2320Z", time(23, 20, tzinfo=UTC), TIME_BAS_MINUTE + TZ_BAS),
+ ("23Z", time(23, tzinfo=UTC), TIME_HOUR + TZ_BAS),
+ ("23:20:30Z", time(23, 20, 30, tzinfo=UTC), TIME_EXT_COMPLETE + TZ_EXT),
+ ("23:20Z", time(23, 20, tzinfo=UTC), TIME_EXT_MINUTE + TZ_EXT),
+ (
+ "152746+0100",
+ time(15, 27, 46, tzinfo=FixedOffset(1, 0, "+0100")),
+ TIME_BAS_COMPLETE + TZ_BAS,
+ ),
+ (
+ "152746-0500",
+ time(15, 27, 46, tzinfo=FixedOffset(-5, 0, "-0500")),
+ TIME_BAS_COMPLETE + TZ_BAS,
+ ),
+ (
+ "152746+01",
+ time(15, 27, 46, tzinfo=FixedOffset(1, 0, "+01:00")),
+ TIME_BAS_COMPLETE + TZ_HOUR,
+ ),
+ (
+ "152746-05",
+ time(15, 27, 46, tzinfo=FixedOffset(-5, -0, "-05:00")),
+ TIME_BAS_COMPLETE + TZ_HOUR,
+ ),
+ (
+ "15:27:46+01:00",
+ time(15, 27, 46, tzinfo=FixedOffset(1, 0, "+01:00")),
+ TIME_EXT_COMPLETE + TZ_EXT,
+ ),
+ (
+ "15:27:46-05:00",
+ time(15, 27, 46, tzinfo=FixedOffset(-5, -0, "-05:00")),
+ TIME_EXT_COMPLETE + TZ_EXT,
+ ),
+ (
+ "15:27:46+01",
+ time(15, 27, 46, tzinfo=FixedOffset(1, 0, "+01:00")),
+ TIME_EXT_COMPLETE + TZ_HOUR,
+ ),
+ (
+ "15:27:46-05",
+ time(15, 27, 46, tzinfo=FixedOffset(-5, -0, "-05:00")),
+ TIME_EXT_COMPLETE + TZ_HOUR,
+ ),
+ (
+ "15:27:46-05:30",
+ time(15, 27, 46, tzinfo=FixedOffset(-5, -30, "-05:30")),
+ TIME_EXT_COMPLETE + TZ_EXT,
+ ),
+ (
+ "15:27:46-0545",
+ time(15, 27, 46, tzinfo=FixedOffset(-5, -45, "-0545")),
+ TIME_EXT_COMPLETE + TZ_BAS,
+ ),
+ ("1:17:30", None, TIME_EXT_COMPLETE),
+]
def create_testcase(timestring, expectation, format):
@@ -101,15 +101,15 @@ def create_testcase(timestring, expectation, format):
"""
class TestTime(unittest.TestCase):
- '''
+ """
A test case template to parse an ISO time string into a time
object.
- '''
+ """
def test_parse(self):
- '''
+ """
Parse an ISO time string and compare it to the expected value.
- '''
+ """
if expectation is None:
self.assertRaises(ISO8601Error, parse_time, timestring)
else:
@@ -117,24 +117,22 @@ def create_testcase(timestring, expectation, format):
self.assertEqual(result, expectation)
def test_format(self):
- '''
+ """
Take time object and create ISO string from it.
This is the reverse test to test_parse.
- '''
+ """
if expectation is None:
- self.assertRaises(AttributeError,
- time_isoformat, expectation, format)
+ self.assertRaises(AttributeError, time_isoformat, expectation, format)
elif format is not None:
- self.assertEqual(time_isoformat(expectation, format),
- timestring)
+ self.assertEqual(time_isoformat(expectation, format), timestring)
return unittest.TestLoader().loadTestsFromTestCase(TestTime)
def test_suite():
- '''
+ """
Construct a TestSuite instance for all test cases.
- '''
+ """
suite = unittest.TestSuite()
for timestring, expectation, format in TEST_CASES:
suite.addTest(create_testcase(timestring, expectation, format))
@@ -146,5 +144,5 @@ def load_tests(loader, tests, pattern):
return test_suite()
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/src/isodate/tzinfo.py b/src/isodate/tzinfo.py
index 5d3a42d..102b6e3 100644
--- a/src/isodate/tzinfo.py
+++ b/src/isodate/tzinfo.py
@@ -1,8 +1,8 @@
-'''
+"""
This module provides some datetime.tzinfo implementations.
All those classes are taken from the Python documentation.
-'''
+"""
from datetime import timedelta, tzinfo
import time
@@ -11,35 +11,35 @@ ZERO = timedelta(0)
class Utc(tzinfo):
- '''UTC
+ """UTC
Universal time coordinated time zone.
- '''
+ """
def utcoffset(self, dt):
- '''
+ """
Return offset from UTC in minutes east of UTC, which is ZERO for UTC.
- '''
+ """
return ZERO
def tzname(self, dt):
- '''
+ """
Return the time zone name corresponding to the datetime object dt,
as a string.
- '''
+ """
return "UTC"
def dst(self, dt):
- '''
+ """
Return the daylight saving time (DST) adjustment, in minutes east
of UTC.
- '''
+ """
return ZERO
def __reduce__(self):
- '''
+ """
When unpickling a Utc object, return the default instance below, UTC.
- '''
+ """
return _Utc, ()
@@ -48,53 +48,53 @@ UTC = Utc()
def _Utc():
- '''
+ """
Helper function for unpickling a Utc object.
- '''
+ """
return UTC
class FixedOffset(tzinfo):
- '''
+ """
A class building tzinfo objects for fixed-offset time zones.
Note that FixedOffset(0, 0, "UTC") or FixedOffset() is a different way to
build a UTC tzinfo object.
- '''
+ """
def __init__(self, offset_hours=0, offset_minutes=0, name="UTC"):
- '''
+ """
Initialise an instance with time offset and name.
The time offset should be positive for time zones east of UTC
and negate for time zones west of UTC.
- '''
+ """
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
self.__name = name
def utcoffset(self, dt):
- '''
+ """
Return offset from UTC in minutes of UTC.
- '''
+ """
return self.__offset
def tzname(self, dt):
- '''
+ """
Return the time zone name corresponding to the datetime object dt, as a
string.
- '''
+ """
return self.__name
def dst(self, dt):
- '''
+ """
Return the daylight saving time (DST) adjustment, in minutes east of
UTC.
- '''
+ """
return ZERO
def __repr__(self):
- '''
+ """
Return nicely formatted repr string.
- '''
+ """
return "<FixedOffset %r>" % self.__name
@@ -117,37 +117,45 @@ class LocalTimezone(tzinfo):
"""
def utcoffset(self, dt):
- '''
+ """
Return offset from UTC in minutes of UTC.
- '''
+ """
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
- '''
+ """
Return daylight saving offset.
- '''
+ """
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
- '''
+ """
Return the time zone name corresponding to the datetime object dt, as a
string.
- '''
+ """
return time.tzname[self._isdst(dt)]
def _isdst(self, dt):
- '''
+ """
Returns true if DST is active for given datetime object dt.
- '''
- tt = (dt.year, dt.month, dt.day,
- dt.hour, dt.minute, dt.second,
- dt.weekday(), 0, -1)
+ """
+ tt = (
+ dt.year,
+ dt.month,
+ dt.day,
+ dt.hour,
+ dt.minute,
+ dt.second,
+ dt.weekday(),
+ 0,
+ -1,
+ )
stamp = time.mktime(tt)
tt = time.localtime(stamp)
return tt.tm_isdst > 0
diff --git a/tox.ini b/tox.ini
index 16616ab..322d454 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,20 +1,21 @@
[tox]
-envlist = py27,py34,py35,py36,pypy,pypy3,flake,cover
+envlist =
+ lint
+ py{37, 38, 39, 310, py3}
[testenv]
deps =
+setenv =
+ PYTHONWARNINGS = default
commands =
{envpython} setup.py test
-
-[testenv:flake]
-basepython = python3.5
-commands=
- pip install --quiet flake8
- {envpython} setup.py clean --all flake8
-
-[testenv:cover]
-basepython = python3.5
-commands =
pip install --quiet coverage
{envpython} setup.py clean --all
coverage run setup.py test
+ coverage xml
+
+[testenv:lint]
+deps = pre-commit
+commands = pre-commit run --all-files
+skip_install = true
+passenv = PRE_COMMIT_COLOR