Repository: sdispater/pendulum
Pull request #176 (2018-01-31 19:45:59)

fix mismatch import in the documentation


Patch:
diff --git a/.gitignore b/.gitignore
index 402ca83..f400f9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,4 @@ benchmark.py
 results.json
 profile.html
 /wheelhouse
+pyproject.lock
diff --git a/.travis.yml b/.travis.yml
index 850d370..d2b9b67 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,10 +38,16 @@ install:
         virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION"
         source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
       fi
-  - pip install -r tests-requirements.txt
-  - python setup.py develop
+  - virtualenv --python="$HOME/virtualenv/python3.6/bin/python" "$HOME/virtualenvs/venv-poetry"
+  - $HOME/virtualenvs/venv-poetry/bin/pip install poetry --pre
+  - $HOME/virtualenvs/venv-poetry/bin/poetry install -v
+  - $HOME/virtualenvs/venv-poetry/bin/poetry build -v
+  - |
+      if [ "$PENDULUM_EXTENSIONS" == "1" ]; then
+        find dist/ -iname pendulum*.whl -exec unzip -o {} 'pendulum/*' -d . \;
+      fi
 
-script: py.test --cov=pendulum --cov-config=.coveragerc tests/
+script: pytest --cov=pendulum --cov-config=.coveragerc tests/
 
 after_success:
   - codecov
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 250485c..8f19529 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,36 @@
 # Change Log
 
-## [Unreleased]
+## [1.4.4] - 2018-03-21
+
+### Fixed
+
+- Fixed extension building script.
+
+
+## [1.4.3] - 2018-03-20
+
+### Fixed
+
+- Fixed an error when adding intervals to a Pendulum instance across DST transition.
+- Fixed an error when subtracting two pendulum instances in the same timezone.
+
+
+## [1.4.2] - 2018-02-22
+
+### Fixed
+
+- Fixed an offset error when subtracting datetimes in the same timezone.
+- Fixed wrong value returned by `tzname()` for the UTC timezone.
+- Fixed `deepcopy()` raising an error when using `UTC`.
+
+
+## [1.4.1] - 2018-02-05
 
 ### Fixed
 
 - Fixed an error when comparing a Period to a timedelta in PyPy.
 - Fixed an offset error for datetimes between the before last and last transition.
+- Fixed unpickling with undefined / empty timezone name. (Thanks to [Delgan](https://github.com/Delgan))
 
 
 ## [1.4.0] - 2018-01-22
@@ -448,7 +473,11 @@ Initial release
 
 
 
-[Unreleased]: https://github.com/sdispater/pendulum/compare/1.4.0...1.x
+[Unreleased]: https://github.com/sdispater/pendulum/compare/1.4.4...1.x
+[1.4.4]: https://github.com/sdispater/pendulum/releases/tag/1.4.4
+[1.4.3]: https://github.com/sdispater/pendulum/releases/tag/1.4.3
+[1.4.2]: https://github.com/sdispater/pendulum/releases/tag/1.4.2
+[1.4.1]: https://github.com/sdispater/pendulum/releases/tag/1.4.1
 [1.4.0]: https://github.com/sdispater/pendulum/releases/tag/1.4.0
 [1.3.2]: https://github.com/sdispater/pendulum/releases/tag/1.3.2
 [1.3.1]: https://github.com/sdispater/pendulum/releases/tag/1.3.1
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 4e978a5..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,2 +0,0 @@
-include README.rst LICENSE
-exclude test*
diff --git a/Makefile b/Makefile
index 35b9c94..136f3d1 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ setup: setup-python
 test:
 	@py.test --cov=pendulum --cov-config .coveragerc tests/ -sq
 
-release: tar wheels_x64 cp_wheels_x64 wheels_i686 cp_wheels_i686 wheel
+release: wheels_x64 cp_wheels_x64 wheels_i686 cp_wheels_i686 wheel
 
 publish:
 	@python -m twine upload dist/pendulum-$(PENDULUM_RELEASE)*
@@ -32,7 +32,7 @@ tar:
 	python setup.py sdist --formats=gztar
 
 wheel:
-	@pip wheel --no-index --no-deps --wheel-dir dist dist/pendulum-$(PENDULUM_RELEASE).tar.gz
+	@poetry build -v
 
 wheels_x64: clean_wheels build_wheels_x64
 
@@ -40,11 +40,13 @@ wheels_i686: clean_wheels build_wheels_i686
 
 build_wheels_x64:
 	rm -rf wheelhouse/
+	mkdir wheelhouse
 	docker pull quay.io/pypa/manylinux1_x86_64
 	docker run --rm -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/build-wheels.sh
 
 build_wheels_i686:
 	rm -rf wheelhouse/
+	mkdir wheelhouse
 	docker pull quay.io/pypa/manylinux1_i686
 	docker run --rm -v `pwd`:/io quay.io/pypa/manylinux1_i686 /io/build-wheels.sh
 
@@ -52,10 +54,10 @@ clean_wheels:
 	rm -rf wheelhouse/
 
 cp_wheels_x64:
-	cp wheelhouse/*manylinux1_x86_64.whl dist/
+	mv wheelhouse/*manylinux1_x86_64.whl dist/
 
 cp_wheels_i686:
-	cp wheelhouse/*manylinux1_i686.whl dist/
+	mv wheelhouse/*manylinux1_i686.whl dist/
 
 upload_wheels_x64:
 	@for f in wheelhouse/*manylinux1_x86_64.whl ; do \
diff --git a/appveyor.yml b/appveyor.yml
index a2bf497..6ea1597 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,6 +8,8 @@ environment:
     - PYTHON: "C:/Python34-x64"
     - PYTHON: "C:/Python35"
     - PYTHON: "C:/Python35-x64"
+    - PYTHON: "C:/Python36"
+    - PYTHON: "C:/Python36-x64"
 
 install:
   - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
@@ -16,12 +18,22 @@ install:
   # about it being out of date.
   - "pip install --disable-pip-version-check --user --upgrade pip"
 
+  # Create poetry virtualenv
+  - pip install virtualenv
+  - virtualenv --python="C:/Python36-x64\\python.exe" "venv-poetry"
+  - venv-poetry\\Scripts\\pip.exe install poetry --pre
+
+  # Create current python virtualenv
+  - python -m virtualenv venv
+  - venv\Scripts\activate.bat
+  - "SET VIRTUAL_ENV=venv"  # Seems that activate.bat does not set VIRTUAL_ENV
+
   # Install dependencies
-  - "%CMD_IN_ENV% pip install -r tests-requirements.txt"
-  - "%CMD_IN_ENV% pip install codecov"
+  - pip install codecov
+  - venv-poetry\\Scripts\\poetry.exe install -v
 
 test_script:
-  - "py.test --cov=pendulum --cov-config=.coveragerc tests/"
+  - "pytest --cov=pendulum --cov-config=.coveragerc tests/"
 
 after_test:
   - "codecov"
diff --git a/build-wheels.sh b/build-wheels.sh
index cfc8394..18ff4b8 100755
--- a/build-wheels.sh
+++ b/build-wheels.sh
@@ -1,25 +1,38 @@
 #!/bin/bash
 PYTHON_VERSIONS="cp27-cp27m cp35-cp35m cp36-cp36m"
 
+POETRY_PYTHON="cp36-cp36m"
+POETRY_VENV="/opt/python/poetry"
+echo "Create Poetry's virtualenv"
+/opt/python/${POETRY_PYTHON}/bin/pip install virtualenv
+/opt/python/${POETRY_PYTHON}/bin/virtualenv --python /opt/python/${POETRY_PYTHON}/bin/python ${POETRY_VENV}
+${POETRY_VENV}/bin/pip install poetry --pre
+
+RELEASE=$(sed -n "s/VERSION = '\(.*\)'/\1/p" /io/pendulum/version.py)
+
 echo "Compile wheels"
 for PYTHON in ${PYTHON_VERSIONS}; do
     cd /io
-    /opt/python/${PYTHON}/bin/pip install -r wheels-requirements.txt
-    /opt/python/${PYTHON}/bin/pip install -r tests-requirements.txt
-    /opt/python/${PYTHON}/bin/python setup.py sdist --dist-dir wheelhouse --formats=gztar
-    /opt/python/${PYTHON}/bin/pip wheel --no-index --no-deps --wheel-dir wheelhouse wheelhouse/*.tar.gz
+    /opt/python/${POETRY_PYTHON}/bin/virtualenv --python /opt/python/${PYTHON}/bin/python /opt/python/venv-${PYTHON}
+    . /opt/python/venv-${PYTHON}/bin/activate
+    ${POETRY_VENV}/bin/poetry install -v
+    ${POETRY_VENV}/bin/poetry build -v
+    mv dist/*-${RELEASE}-*-linux_*.whl wheelhouse/
+    deactivate
     cd -
 done
 
 echo "Bundle external shared libraries into the wheels"
-for whl in /io/wheelhouse/*.whl; do
-    auditwheel repair $whl -w /io/wheelhouse/
+for whl in /io/wheelhouse/pendulum-${RELEASE}-*-linux_*.whl; do
+    auditwheel repair "$whl" -w /io/wheelhouse/
 done
 
 echo "Install packages and test"
 for PYTHON in ${PYTHON_VERSIONS}; do
-    /opt/python/${PYTHON}/bin/pip install pendulum --no-index -f /io/wheelhouse
+    . /opt/python/venv-${PYTHON}/bin/activate
+    pip install pendulum==${RELEASE} --no-index --find-links /io/wheelhouse
     find ./io/tests | grep -E "(__pycache__|\.pyc$)" | xargs rm -rf
-    /opt/python/${PYTHON}/bin/py.test /io/tests
+    pytest /io/tests
     find ./io/tests | grep -E "(__pycache__|\.pyc$)" | xargs rm -rf
+    deactivate
 done
diff --git a/build.py b/build.py
new file mode 100644
index 0000000..852a7ac
--- /dev/null
+++ b/build.py
@@ -0,0 +1,60 @@
+import os
+import sys
+
+
+from distutils.core import Extension
+
+from distutils.errors import (CCompilerError, DistutilsExecError,
+                              DistutilsPlatformError)
+from distutils.command.build_ext import build_ext
+
+
+# C Extensions
+with_extensions = os.getenv('PENDULUM_EXTENSIONS', None)
+
+if with_extensions == '1' or with_extensions is None:
+    with_extensions = True
+
+if with_extensions == '0' or hasattr(sys, 'pypy_version_info'):
+    with_extensions = False
+
+extensions = []
+if with_extensions:
+    extensions = [
+        Extension('pendulum._extensions._helpers',
+                  ['pendulum/_extensions/_helpers.c']),
+    ]
+
+
+class BuildFailed(Exception):
+
+    pass
+
+
+class ExtBuilder(build_ext):
+    # This class allows C extension building to fail.
+
+    def run(self):
+        try:
+            build_ext.run(self)
+        except (DistutilsPlatformError, FileNotFoundError):
+           pass
+
+    def build_extension(self, ext):
+        try:
+            build_ext.build_extension(self, ext)
+        except (CCompilerError, DistutilsExecError,
+                DistutilsPlatformError, ValueError):
+            pass
+
+
+def build(setup_kwargs):
+    """
+    This function is mandatory in order to build the extensions.
+    """
+    setup_kwargs.update({
+        'ext_modules': extensions,
+        'cmdclass': {
+            'build_ext': ExtBuilder
+        }
+    })
diff --git a/docs/_docs/comparison.rst b/docs/_docs/comparison.rst
index 10369bb..a6f1a7e 100644
--- a/docs/_docs/comparison.rst
+++ b/docs/_docs/comparison.rst
@@ -102,7 +102,7 @@ the ``now()`` is created in the same timezone as the instance.
 
     import pendulum
 
-    dt = Pendulum.now()
+    dt = pendulum.now()
 
     dt.is_weekday()
     dt.is_weekend()
@@ -112,7 +112,7 @@ the ``now()`` is created in the same timezone as the instance.
     dt.is_future()
     dt.is_past()
     dt.is_leap_year()
-    dt.is_same_day(Pendulum.now())
+    dt.is_same_day(pendulum.now())
 
     born = pendulum.create(1987, 4, 23)
     not_birthday = pendulum.create(2014, 9, 26)
diff --git a/docs/_docs/difference.rst b/docs/_docs/difference.rst
index 53c8570..13bdb7d 100644
--- a/docs/_docs/difference.rst
+++ b/docs/_docs/difference.rst
@@ -86,7 +86,7 @@ You may also pass ``True`` as a 2nd parameter to remove the modifiers `ago`, `fr
     pendulum.now().subtract(days=1).diff_for_humans()
     '5 days ago'
 
-    pendulum.now().diff_for_humans(Pendulum.now().subtract(years=1))
+    pendulum.now().diff_for_humans(pendulum.now().subtract(years=1))
     '1 year after'
 
     dt = pendulum.create(2011, 8, 1)
diff --git a/docs/conf.py b/docs/conf.py
index 487cd4e..c6f1bc2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -60,7 +60,7 @@ author = 'Sébastien Eustace'
 # The short X.Y version.
 version = '1.4'
 # The full version, including alpha/beta/rc tags.
-release = '1.4.0'
+release = '1.4.2'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/pendulum/helpers.py b/pendulum/helpers.py
index 23b7023..094dea7 100644
--- a/pendulum/helpers.py
+++ b/pendulum/helpers.py
@@ -159,9 +159,7 @@ def precise_diff(d1, d2):
         d1, d2 = d2, d1
         sign = -1
 
-    y_diff = d2.year - d1.year
-    m_diff = d2.month - d1.month
-    d_diff = d2.day - d1.day
+    d_diff = 0
     hour_diff = 0
     min_diff = 0
     sec_diff = 0
@@ -170,6 +168,7 @@ def precise_diff(d1, d2):
         _day_number(d2.year, d2.month, d2.day)
         - _day_number(d1.year, d1.month, d1.day)
     )
+
     in_same_tz = False
     tz1 = None
     tz2 = None
@@ -194,7 +193,10 @@ def precise_diff(d1, d2):
     if hasattr(d2, 'hour'):
         # If we are not in the same timezone
         # we need to adjust
-        if not in_same_tz:
+        #
+        # We also need to adjust if we do not
+        # have variable-length units
+        if not in_same_tz or total_days == 0:
             offset1 = d1.utcoffset()
             offset2 = d2.utcoffset()
 
@@ -225,6 +227,14 @@ def precise_diff(d1, d2):
             hour_diff += 24
             d_diff -= 1
 
+    if d1 > d2:
+        d1, d2 = d2, d1
+        sign = -1
+
+    y_diff = d2.year - d1.year
+    m_diff = d2.month - d1.month
+    d_diff += d2.day - d1.day
+
     if d_diff < 0:
         year = d2.year
         month = d2.month
diff --git a/pendulum/pendulum.py b/pendulum/pendulum.py
index 6cca7f6..38f37ee 100644
--- a/pendulum/pendulum.py
+++ b/pendulum/pendulum.py
@@ -1233,8 +1233,7 @@ class Pendulum(Date, datetime.datetime):
                 seconds=delta.remaining_seconds, microseconds=delta.microseconds
             )
 
-        return self.add(days=delta.days, seconds=delta.seconds,
-                        microseconds=delta.microseconds)
+        return self.add(seconds=delta.total_seconds())
 
     def subtract_timedelta(self, delta):
         """
diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py
index 416b352..fad44e5 100644
--- a/pendulum/tz/timezone.py
+++ b/pendulum/tz/timezone.py
@@ -518,7 +518,7 @@ class FixedTimezone(Timezone):
 class _UTC(FixedTimezone):
 
     def __init__(self):
-        super(_UTC, self).__init__(0, 'UTC', TransitionType(0, False, 'GMT'))
+        super(_UTC, self).__init__(0, 'UTC', TransitionType(0, False, 'UTC'))
 
         UTC._tz = self
 
diff --git a/pendulum/tz/timezone_info.py b/pendulum/tz/timezone_info.py
index 8dc1f4a..a4ab197 100644
--- a/pendulum/tz/timezone_info.py
+++ b/pendulum/tz/timezone_info.py
@@ -134,4 +134,8 @@ class _UTC(TimezoneInfo):
     def fromutc(self, dt):
         return dt.replace(tzinfo=self)
 
+    def __getinitargs__(self):
+        return ()
+
+
 UTC = _UTC()
diff --git a/pendulum/version.py b/pendulum/version.py
index c43f3e2..360b89f 100644
--- a/pendulum/version.py
+++ b/pendulum/version.py
@@ -1,3 +1,3 @@
 # -*- coding: utf-8 -*-
 
-VERSION = '1.4.0'
+VERSION = '1.4.4'
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..c8fe959
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,31 @@
+[tool.poetry]
+name = "pendulum"
+version = "1.4.4"
+description = "Python datetimes made easy."
+
+license = "MIT"
+
+authors = [
+    "Sébastien Eustace <sebastien@eustace.io>"
+]
+
+readme = 'README.rst'
+
+homepage = "https://pendulum.eustace.io"
+repository = "https://github.com/sdispater/pendulum"
+
+keywords = ['datetime', 'date', 'time', 'timezone']
+
+build = "build.py"
+
+
+[tool.poetry.dependencies]
+python = "~2.7 || ^3.4"
+python-dateutil = "^2.6"
+tzlocal = "^1.5"
+pytzdata = ">=2018.3"
+
+[tool.poetry.dev-dependencies]
+pytest = "^3.4"
+pytest-cov = "^2.4"
+pytz = ">=2018.3"
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 390579a..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-tzlocal
-python-dateutil
-pytzdata>=2017.2.2
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
deleted file mode 100644
index bd780d6..0000000
--- a/setup.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import sys
-from setuptools import setup, find_packages, Extension
-from distutils.errors import (CCompilerError, DistutilsExecError,
-                              DistutilsPlatformError)
-from distutils.command.build_ext import build_ext
-
-
-try:
-    FileNotFoundError
-except NameError:
-    FileNotFoundError = IOError  # py2k
-
-
-def get_version():
-    basedir = os.path.dirname(__file__)
-    with open(os.path.join(basedir, 'pendulum/version.py')) as f:
-        variables = {}
-        exec(f.read(), variables)
-
-        version = variables.get('VERSION')
-        if version:
-            return version
-
-    raise RuntimeError('No version info found.')
-
-
-__version__ = get_version()
-
-# C Extensions
-with_extensions = os.getenv('PENDULUM_EXTENSIONS', None)
-
-if with_extensions == '1' or with_extensions is None:
-    with_extensions = True
-
-if with_extensions == '0' or hasattr(sys, 'pypy_version_info'):
-    with_extensions = False
-
-extensions = []
-if with_extensions:
-    extensions = [
-        Extension('pendulum._extensions._helpers',
-                  ['pendulum/_extensions/_helpers.c']),
-    ]
-
-class BuildFailed(Exception):
-
-    pass
-
-
-class ve_build_ext(build_ext):
-    # This class allows C extension building to fail.
-
-    def run(self):
-        try:
-            build_ext.run(self)
-        except (DistutilsPlatformError, FileNotFoundError):
-            raise BuildFailed()
-
-    def build_extension(self, ext):
-        try:
-            build_ext.build_extension(self, ext)
-        except (CCompilerError, DistutilsExecError,
-                DistutilsPlatformError, ValueError):
-            raise BuildFailed()
-
-packages = ['pendulum']
-for pkg in find_packages('pendulum'):
-    packages.append('pendulum.' + pkg)
-
-kwargs = dict(
-    name='pendulum',
-    license='MIT',
-    version=__version__,
-    description='Python datetimes made easy.',
-    long_description=open('README.rst').read(),
-    author='Sébastien Eustace',
-    author_email='sebastien@eustace.io',
-    url='https://github.com/sdispater/pendulum',
-    download_url='https://github.com/sdispater/pendulum/archive/%s.tar.gz' % __version__,
-    packages=packages,
-    install_requires=[
-        'tzlocal',
-        'python-dateutil',
-        'pytzdata',
-    ],
-    include_package_data=True,
-    tests_require=['pytest'],
-    test_suite='nose.collector',
-    classifiers=[
-        'Intended Audience :: Developers',
-        'Operating System :: OS Independent',
-        'Programming Language :: Python',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.2',
-        'Programming Language :: Python :: 3.3',
-        'Programming Language :: Python :: 3.4',
-        'Programming Language :: Python :: 3.5',
-        'Programming Language :: Python :: 3.6',
-        'Programming Language :: Python :: Implementation :: CPython',
-        'Programming Language :: Python :: Implementation :: PyPy'
-    ],
-)
-
-if extensions:
-    kwargs['ext_modules'] = extensions
-    kwargs['cmdclass'] = dict(build_ext=ve_build_ext)
-
-
-try:
-    setup(**kwargs)
-except BuildFailed:
-    print("************************************************************")
-    print("Cannot compile C accelerator module, use pure python version")
-    print("************************************************************")
-    del kwargs['ext_modules']
-    del kwargs['cmdclass']
-    setup(**kwargs)
diff --git a/wheels-requirements.txt b/wheels-requirements.txt
deleted file mode 100644
index e079f8a..0000000
--- a/wheels-requirements.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-pytest


Test patch:
diff --git a/tests-requirements.txt b/tests-requirements.txt
deleted file mode 100644
index edfd951..0000000
--- a/tests-requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
--r requirements.txt
-pytest
-pytest-cov
-coverage<4
-pytz
diff --git a/tests/pendulum_tests/test_add.py b/tests/pendulum_tests/test_add.py
index e11fd03..8b75d40 100644
--- a/tests/pendulum_tests/test_add.py
+++ b/tests/pendulum_tests/test_add.py
@@ -236,3 +236,17 @@ class AddTest(AbstractTestCase):
         self.assertEqual('Europe/Paris', dt.timezone_name)
         self.assertEqual(7200, dt.offset)
         self.assertTrue(dt.is_dst)
+
+    def test_period_over_midnight_tz(self):
+        start = pendulum.create(2018, 2, 25, tz='Europe/Paris')
+        end = start.add(hours=1)
+        period = end - start
+        new_end = start + period
+
+        assert new_end == end
+
+    def test_add_interval(self):
+        dt = pendulum.create(2017, 3, 11, 10, 45, tz='America/Los_Angeles')
+        new = dt + pendulum.interval(hours=24)
+
+        self.assertPendulum(new, 2017, 3, 12, 11, 45)
diff --git a/tests/pendulum_tests/test_behavior.py b/tests/pendulum_tests/test_behavior.py
index 76d90ed..acfbd65 100644
--- a/tests/pendulum_tests/test_behavior.py
+++ b/tests/pendulum_tests/test_behavior.py
@@ -125,6 +125,11 @@ class BehaviorTest(AbstractTestCase):
 
         self.assertEqual(dt._datetime, deepcopy(dt._datetime))
 
+    def test_deepcopy_datetime_utcnow(self):
+        dt = pendulum.utcnow()
+
+        self.assertEqual(dt._datetime, deepcopy(dt._datetime))
+
     def test_pickle_timezone(self):
         dt1 = pendulum.timezone('Europe/Amsterdam')
         s = pickle.dumps(dt1)
diff --git a/tests/period_tests/test_add_subtract.py b/tests/period_tests/test_add_subtract.py
index d645401..3b177a1 100644
--- a/tests/period_tests/test_add_subtract.py
+++ b/tests/period_tests/test_add_subtract.py
@@ -12,6 +12,15 @@ def test_dst_add():
     assert new_end == end
 
 
+def test_dst_add_non_variable_units():
+    start = pendulum.create(2013, 3, 31, 1, 30, tz='Europe/Paris')
+    end = start.add(hours=1)
+    period = end - start
+    new_end = start + period
+
+    assert new_end == end
+
+
 def test_dst_subtract():
     start = pendulum.create(2017, 3, 7, tz='America/Toronto')
     end = start.add(days=6)
diff --git a/tests/tz_tests/test_timezone.py b/tests/tz_tests/test_timezone.py
index 2b09231..2b389aa 100644
--- a/tests/tz_tests/test_timezone.py
+++ b/tests/tz_tests/test_timezone.py
@@ -379,3 +379,9 @@ class TimezoneTest(AbstractTestCase):
 
         self.assertEqual(tz2.utcoffset(dt).total_seconds(), 18000)
         self.assertIsNone(tz2.dst(dt))
+
+    def test_utc_tzname(self):
+        tz = Timezone.load('UTC')
+        dt = datetime(2016, 11, 26)
+
+        assert tz.tzname(dt) == 'UTC'