diff --git a/lib/README.md b/lib/README.md
index 13558c12e2c748bb4fddac8fce943833caace1e9..cf54967cd17098e25c2728a7572ca1ecdfbfe287 100644
--- a/lib/README.md
+++ b/lib/README.md
@@ -66,5 +66,6 @@ xmltodict==0.11.0
 
 ```
 pip install --target=lib -U --global-option="--no-user-cfg" --no-binary :all --no-compile --no-cache-dir babelfish tzlocal Unidecode validators xmltodict tornado==4.5 mako python-twitter python-dateutil PyGithub markdown2 html5lib bencode pysocks requests requests-oauthlib tzlocal
-    pip install --target=lib -U --global-option="--no-user-cfg" --no-binary :all --no-compile --no-cache-dir git+https://github.com/Diaoul/subliminal.git@4ad5d31a6c52c7fa0061ad0da16254580d31dc2a#egg=subliminal
+#pip install --target=lib -U --global-option="--no-user-cfg" --no-binary :all --no-compile --no-cache-dir git+https://github.com/Diaoul/subliminal.git@4ad5d31a6c52c7fa0061ad0da16254580d31dc2a#egg=subliminal
+pip install --target=lib -U --global-option="--no-user-cfg" --no-binary :all --no-compile --no-cache-dir git+https://github.com/SickRage/subliminal.git@bc92dbd#egg=subliminal
 ```
diff --git a/lib/certifi/__init__.py b/lib/certifi/__init__.py
index 556193cefbf1f8b9b65cab70f394e28481af1fea..0c4963ef6050d85f50925ff2d0108ca50a1f232d 100644
--- a/lib/certifi/__init__.py
+++ b/lib/certifi/__init__.py
@@ -1,3 +1,3 @@
 from .core import where, old_where
 
-__version__ = "2018.01.18"
+__version__ = "2018.04.16"
diff --git a/lib/certifi/cacert.pem b/lib/certifi/cacert.pem
index 101ac98fa42d9eb4b27836e122cc81d495e33c21..2713f541c44982d22ed24d8d2bf10a0f40f2e0a6 100644
--- a/lib/certifi/cacert.pem
+++ b/lib/certifi/cacert.pem
@@ -3483,39 +3483,6 @@ AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
 5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
 -----END CERTIFICATE-----
 
-# Issuer: CN=T\xdcRKTRUST Elektronik Sertifika Hizmet Sa\u011flay\u0131c\u0131s\u0131 H5 O=T\xdcRKTRUST Bilgi \u0130leti\u015fim ve Bili\u015fim G\xfcvenli\u011fi Hizmetleri A.\u015e.
-# Subject: CN=T\xdcRKTRUST Elektronik Sertifika Hizmet Sa\u011flay\u0131c\u0131s\u0131 H5 O=T\xdcRKTRUST Bilgi \u0130leti\u015fim ve Bili\u015fim G\xfcvenli\u011fi Hizmetleri A.\u015e.
-# Label: "T\xdcRKTRUST Elektronik Sertifika Hizmet Sa\u011flay\u0131c\u0131s\u0131 H5"
-# Serial: 156233699172481
-# MD5 Fingerprint: da:70:8e:f0:22:df:93:26:f6:5f:9f:d3:15:06:52:4e
-# SHA1 Fingerprint: c4:18:f6:4d:46:d1:df:00:3d:27:30:13:72:43:a9:12:11:c6:75:fb
-# SHA256 Fingerprint: 49:35:1b:90:34:44:c1:85:cc:dc:5c:69:3d:24:d8:55:5c:b2:08:d6:a8:14:13:07:69:9f:4a:f0:63:19:9d:78
------BEGIN CERTIFICATE-----
-MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE
-BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn
-aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg
-QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg
-SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0
-MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD
-VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8
-dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB
-IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom
-/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR
-Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3
-4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z
-5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0
-hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID
-AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/
-BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX
-SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l
-VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq
-URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf
-peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF
-Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW
-+qtB4Uu2NQvAmxU=
------END CERTIFICATE-----
-
 # Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
 # Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
 # Label: "Certinomis - Root CA"
diff --git a/lib/dateutil/_version.py b/lib/dateutil/_version.py
index 3aa93e975693f5071e1da099d432e41deafc6348..de463291c4108ed2c631fe1a1a2eefa330b6cbe7 100644
--- a/lib/dateutil/_version.py
+++ b/lib/dateutil/_version.py
@@ -1,4 +1,4 @@
 # coding: utf-8
 # file generated by setuptools_scm
 # don't change, don't track in version control
-version = u'2.7.0'
+version = u'2.7.2'
diff --git a/lib/dateutil/parser/_parser.py b/lib/dateutil/parser/_parser.py
index 1b5aabf1b6cdd297577fe4f7a1950502e6eb6fe5..0eac592f94a473ec3de9c5c44b0d154abb9a624d 100644
--- a/lib/dateutil/parser/_parser.py
+++ b/lib/dateutil/parser/_parser.py
@@ -836,7 +836,11 @@ class parser(object):
     def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy):
         # Token is a number
         value_repr = tokens[idx]
-        value = Decimal(value_repr)
+        try:
+            value = self._to_decimal(value_repr)
+        except Exception as e:
+            six.raise_from(ValueError('Unknown numeric token'), e)
+
         len_li = len(value_repr)
 
         len_l = len(tokens)
@@ -895,7 +899,7 @@ class parser(object):
         elif idx + 2 < len_l and tokens[idx + 1] == ':':
             # HH:MM[:SS[.ss]]
             res.hour = int(value)
-            value = Decimal(tokens[idx + 2])  # TODO: try/except for this?
+            value = self._to_decimal(tokens[idx + 2])  # TODO: try/except for this?
             (res.minute, res.second) = self._parse_min_sec(value)
 
             if idx + 4 < len_l and tokens[idx + 3] == ':':
@@ -996,7 +1000,7 @@ class parser(object):
 
     def _assign_hms(self, res, value_repr, hms):
         # See GH issue #427, fixing float rounding
-        value = Decimal(value_repr)
+        value = self._to_decimal(value_repr)
 
         if hms == 0:
             # Hour
@@ -1196,6 +1200,13 @@ class parser(object):
 
         return dt
 
+    def _to_decimal(self, val):
+        try:
+            return Decimal(val)
+        except Exception as e:
+            msg = "Could not convert %s to decimal" % val
+            six.raise_from(ValueError(msg), e)
+
 
 DEFAULTPARSER = parser()
 
diff --git a/lib/dateutil/rrule.py b/lib/dateutil/rrule.py
index 6d6adb10a60b9dd8328e3f2367b25d7909786812..ef4607a346927868d58a96e8162cd993451679e6 100644
--- a/lib/dateutil/rrule.py
+++ b/lib/dateutil/rrule.py
@@ -21,6 +21,7 @@ from six.moves import _thread, range
 import heapq
 
 from ._common import weekday as weekdaybase
+from .tz import tzutc, tzlocal
 
 # For warning about deprecation of until and count
 from warnings import warn
@@ -447,6 +448,20 @@ class rrule(rrulebase):
             until = datetime.datetime.fromordinal(until.toordinal())
         self._until = until
 
+        if self._dtstart and self._until:
+            if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None):
+                # According to RFC5545 Section 3.3.10:
+                # https://tools.ietf.org/html/rfc5545#section-3.3.10
+                #
+                # > If the "DTSTART" property is specified as a date with UTC
+                # > time or a date with local time and time zone reference,
+                # > then the UNTIL rule part MUST be specified as a date with
+                # > UTC time.
+                raise ValueError(
+                    'RRULE UNTIL values must be specified in UTC when DTSTART '
+                    'is timezone-aware'
+                )
+
         if count is not None and until:
             warn("Using both 'count' and 'until' is inconsistent with RFC 5545"
                  " and has been deprecated in dateutil. Future versions will "
diff --git a/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz
index c1722f643090bf004e5521dee56ad5204079033d..5b76ea17dca6d2b68d0802ee7d395b099fedee97 100644
Binary files a/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz and b/lib/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ
diff --git a/lib/pbr/builddoc.py b/lib/pbr/builddoc.py
index c1a2e6f475304a997a1303bc7a1ad34c080397bb..167d13e04a5be8ef9a66c2abcb4f0fc4189b9352 100644
--- a/lib/pbr/builddoc.py
+++ b/lib/pbr/builddoc.py
@@ -25,7 +25,19 @@ except ImportError:
     import io as cStringIO
 
 try:
-    from sphinx import apidoc
+    import sphinx
+    # NOTE(dhellmann): Newer versions of Sphinx have moved the apidoc
+    # module into sphinx.ext and the API is slightly different (the
+    # function expects sys.argv[1:] instead of sys.argv[:]. So, figure
+    # out where we can import it from and set a flag so we can invoke
+    # it properly. See this change in sphinx for details:
+    # https://github.com/sphinx-doc/sphinx/commit/87630c8ae8bff8c0e23187676e6343d8903003a6
+    try:
+        from sphinx.ext import apidoc
+        apidoc_use_padding = False
+    except ImportError:
+        from sphinx import apidoc
+        apidoc_use_padding = True
     from sphinx import application
     from sphinx import setup_command
 except Exception as e:
@@ -42,6 +54,7 @@ except Exception as e:
     raise ImportError(str(e))
 from pbr import git
 from pbr import options
+from pbr import version
 
 
 _rst_template = """%(heading)s
@@ -117,7 +130,9 @@ class LocalBuildDoc(setup_command.BuildDoc):
 
     def _sphinx_tree(self):
             source_dir = self._get_source_dir()
-            cmd = ['apidoc', '.', '-H', 'Modules', '-o', source_dir]
+            cmd = ['-H', 'Modules', '-o', source_dir, '.']
+            if apidoc_use_padding:
+                cmd.insert(0, 'apidoc')
             apidoc.main(cmd + self.autodoc_tree_excludes)
 
     def _sphinx_run(self):
@@ -186,15 +201,34 @@ class LocalBuildDoc(setup_command.BuildDoc):
                         "autodoc_exclude_modules",
                         [None, ""])[1].split()))
 
-        # TODO(stephenfin): Deprecate this functionality once we depend on
-        # Sphinx 1.6, which includes a similar feature, in g-r
-        # https://github.com/sphinx-doc/sphinx/pull/3476
         self.finalize_options()
-        if hasattr(self, "builder_target_dirs"):
-            # Sphinx >= 1.6.1
+
+        is_multibuilder_sphinx = version.SemanticVersion.from_pip_string(
+            sphinx.__version__) >= version.SemanticVersion(1, 6)
+
+        # TODO(stephenfin): Remove support for Sphinx < 1.6 in 4.0
+        if not is_multibuilder_sphinx:
+            log.warn('[pbr] Support for Sphinx < 1.6 will be dropped in '
+                     'pbr 4.0. Upgrade to Sphinx 1.6+')
+
+        # TODO(stephenfin): Remove this at the next MAJOR version bump
+        if self.builders != ['html']:
+            log.warn("[pbr] Sphinx 1.6 added native support for "
+                     "specifying multiple builders in the "
+                     "'[sphinx_build] builder' configuration option, "
+                     "found in 'setup.cfg'. As a result, the "
+                     "'[sphinx_build] builders' option has been "
+                     "deprecated and will be removed in pbr 4.0. Migrate "
+                     "to the 'builder' configuration option.")
+            if is_multibuilder_sphinx:
+                self.builder = self.builders
+
+        if is_multibuilder_sphinx:
+            # Sphinx >= 1.6
             return setup_command.BuildDoc.run(self)
+
         # Sphinx < 1.6
-        for builder in self.builders:
+        for builder in self.builder:
             self.builder = builder
             self.finalize_options()
             self._sphinx_run()
@@ -208,6 +242,8 @@ class LocalBuildDoc(setup_command.BuildDoc):
         self.autodoc_tree_excludes = ['setup.py']
 
     def finalize_options(self):
+        from pbr import util
+
         # Not a new style class, super keyword does not work.
         setup_command.BuildDoc.finalize_options(self)
 
@@ -228,8 +264,8 @@ class LocalBuildDoc(setup_command.BuildDoc):
         opt = 'autodoc_tree_excludes'
         option_dict = self.distribution.get_option_dict('pbr')
         if opt in option_dict:
-            self.autodoc_tree_excludes = option_dict[opt][1]
-            self.ensure_string_list(opt)
+            self.autodoc_tree_excludes = util.split_multiline(
+                option_dict[opt][1])
 
         # handle Sphinx < 1.5.0
         if not hasattr(self, 'warning_is_error'):
diff --git a/lib/pbr/core.py b/lib/pbr/core.py
index 71d1e5696009e2de568423e5d92e0cce2b60ad34..a93253baddf4796a1d3be3c80ef0f0979cf95d54 100644
--- a/lib/pbr/core.py
+++ b/lib/pbr/core.py
@@ -50,22 +50,9 @@ import os
 import sys
 import warnings
 
-from setuptools import dist
-
 from pbr import util
 
 
-_saved_core_distribution = core.Distribution
-
-
-def _monkeypatch_distribution():
-    core.Distribution = dist._get_unpatched(core.Distribution)
-
-
-def _restore_distribution_monkeypatch():
-    core.Distribution = _saved_core_distribution
-
-
 if sys.version_info[0] == 3:
     string_type = str
     integer_types = (int,)
@@ -94,63 +81,54 @@ def pbr(dist, attr, value):
     not work well with distributions that do use a `Distribution` subclass.
     """
 
+    if not value:
+        return
+    if isinstance(value, string_type):
+        path = os.path.abspath(value)
+    else:
+        path = os.path.abspath('setup.cfg')
+    if not os.path.exists(path):
+        raise errors.DistutilsFileError(
+            'The setup.cfg file %s does not exist.' % path)
+
+    # Converts the setup.cfg file to setup() arguments
     try:
-        _monkeypatch_distribution()
-        if not value:
-            return
-        if isinstance(value, string_type):
-            path = os.path.abspath(value)
-        else:
-            path = os.path.abspath('setup.cfg')
-        if not os.path.exists(path):
-            raise errors.DistutilsFileError(
-                'The setup.cfg file %s does not exist.' % path)
-
-        # Converts the setup.cfg file to setup() arguments
-        try:
-            attrs = util.cfg_to_args(path, dist.script_args)
-        except Exception:
-            e = sys.exc_info()[1]
-            # NB: This will output to the console if no explicit logging has
-            # been setup - but thats fine, this is a fatal distutils error, so
-            # being pretty isn't the #1 goal.. being diagnosable is.
-            logging.exception('Error parsing')
-            raise errors.DistutilsSetupError(
-                'Error parsing %s: %s: %s' % (path, e.__class__.__name__, e))
-
-        # Repeat some of the Distribution initialization code with the newly
-        # provided attrs
-        if attrs:
-            # Skips 'options' and 'licence' support which are rarely used; may
-            # add back in later if demanded
-            for key, val in attrs.items():
-                if hasattr(dist.metadata, 'set_' + key):
-                    getattr(dist.metadata, 'set_' + key)(val)
-                elif hasattr(dist.metadata, key):
-                    setattr(dist.metadata, key, val)
-                elif hasattr(dist, key):
-                    setattr(dist, key, val)
-                else:
-                    msg = 'Unknown distribution option: %s' % repr(key)
-                    warnings.warn(msg)
-
-        # Re-finalize the underlying Distribution
-        core.Distribution.finalize_options(dist)
-
-        # This bit comes out of distribute/setuptools
-        if isinstance(dist.metadata.version, integer_types + (float,)):
-            # Some people apparently take "version number" too literally :)
-            dist.metadata.version = str(dist.metadata.version)
-
-        # This bit of hackery is necessary so that the Distribution will ignore
-        # normally unsupport command options (namely pre-hooks and post-hooks).
-        # dist.command_options is normally a dict mapping command names to
-        # dicts of their options.  Now it will be a defaultdict that returns
-        # IgnoreDicts for the each command's options so we can pass through the
-        # unsupported options
-        ignore = ['pre_hook.*', 'post_hook.*']
-        dist.command_options = util.DefaultGetDict(
-            lambda: util.IgnoreDict(ignore)
-        )
-    finally:
-        _restore_distribution_monkeypatch()
+        attrs = util.cfg_to_args(path, dist.script_args)
+    except Exception:
+        e = sys.exc_info()[1]
+        # NB: This will output to the console if no explicit logging has
+        # been setup - but thats fine, this is a fatal distutils error, so
+        # being pretty isn't the #1 goal.. being diagnosable is.
+        logging.exception('Error parsing')
+        raise errors.DistutilsSetupError(
+            'Error parsing %s: %s: %s' % (path, e.__class__.__name__, e))
+
+    # Repeat some of the Distribution initialization code with the newly
+    # provided attrs
+    if attrs:
+        # Skips 'options' and 'licence' support which are rarely used; may
+        # add back in later if demanded
+        for key, val in attrs.items():
+            if hasattr(dist.metadata, 'set_' + key):
+                getattr(dist.metadata, 'set_' + key)(val)
+            elif hasattr(dist.metadata, key):
+                setattr(dist.metadata, key, val)
+            elif hasattr(dist, key):
+                setattr(dist, key, val)
+            else:
+                msg = 'Unknown distribution option: %s' % repr(key)
+                warnings.warn(msg)
+
+    # Re-finalize the underlying Distribution
+    try:
+        super(dist.__class__, dist).finalize_options()
+    except TypeError:
+        # If dist is not declared as a new-style class (with object as
+        # a subclass) then super() will not work on it. This is the case
+        # for Python 2. In that case, fall back to doing this the ugly way
+        dist.__class__.__bases__[-1].finalize_options(dist)
+
+    # This bit comes out of distribute/setuptools
+    if isinstance(dist.metadata.version, integer_types + (float,)):
+        # Some people apparently take "version number" too literally :)
+        dist.metadata.version = str(dist.metadata.version)
diff --git a/lib/pbr/packaging.py b/lib/pbr/packaging.py
index c8eedbdb16df54916f9e871cbb89e5d55ad0fe69..1ff28da9b15cf2dc0c3191d9baef5f61c12d3e60 100644
--- a/lib/pbr/packaging.py
+++ b/lib/pbr/packaging.py
@@ -27,6 +27,7 @@ import email.errors
 import os
 import re
 import sys
+import warnings
 
 import pkg_resources
 import setuptools
@@ -45,6 +46,8 @@ from pbr import testr_command
 from pbr import version
 
 REQUIREMENTS_FILES = ('requirements.txt', 'tools/pip-requires')
+PY_REQUIREMENTS_FILES = [x % sys.version_info[0] for x in (
+    'requirements-py%d.txt', 'tools/pip-requires-py%d')]
 TEST_REQUIREMENTS_FILES = ('test-requirements.txt', 'tools/test-requires')
 
 
@@ -56,9 +59,8 @@ def get_requirements_files():
     # - REQUIREMENTS_FILES with -py2 or -py3 in the name
     #   (e.g. requirements-py3.txt)
     # - REQUIREMENTS_FILES
-    return (list(map(('-py' + str(sys.version_info[0])).join,
-                     map(os.path.splitext, REQUIREMENTS_FILES)))
-            + list(REQUIREMENTS_FILES))
+
+    return PY_REQUIREMENTS_FILES + list(REQUIREMENTS_FILES)
 
 
 def append_text_list(config, key, text_list):
@@ -77,9 +79,20 @@ def _any_existing(file_list):
 
 # Get requirements from the first file that exists
 def get_reqs_from_files(requirements_files):
-    for requirements_file in _any_existing(requirements_files):
+    existing = _any_existing(requirements_files)
+
+    deprecated = [f for f in existing if f in PY_REQUIREMENTS_FILES]
+    if deprecated:
+        warnings.warn('Support for \'-pyN\'-suffixed requirements files is '
+                      'deprecated in pbr 4.0 and will be removed in 5.0. '
+                      'Use environment markers instead. Conflicting files: '
+                      '%r' % deprecated,
+                      DeprecationWarning)
+
+    for requirements_file in existing:
         with open(requirements_file, 'r') as fil:
             return fil.read().split('\n')
+
     return []
 
 
@@ -259,8 +272,14 @@ try:
         """Fallback test runner if testr is a no-go."""
 
         command_name = 'test'
+        description = 'DEPRECATED: Run unit tests using nose'
 
         def run(self):
+            warnings.warn('nose integration in pbr is deprecated. Please use '
+                          'the native nose setuptools configuration or call '
+                          'nose directly',
+                          DeprecationWarning)
+
             # Can't use super - base class old-style class
             commands.nosetests.run(self)
 
@@ -390,8 +409,6 @@ class LocalDevelop(develop.develop):
     command_name = 'develop'
 
     def install_wrapper_scripts(self, dist):
-        if sys.platform == 'win32':
-            return develop.develop.install_wrapper_scripts(self, dist)
         if not self.exclude_scripts:
             for args in override_get_script_args(dist):
                 self.write_script(*args)
@@ -445,13 +462,7 @@ class LocalInstallScripts(install_scripts.install_scripts):
             # entry-points listed for this package.
             return
 
-        if os.name != 'nt':
-            get_script_args = override_get_script_args
-        else:
-            get_script_args = easy_install.get_script_args
-            executable = '"%s"' % executable
-
-        for args in get_script_args(dist, executable, is_wininst):
+        for args in override_get_script_args(dist, executable, is_wininst):
             self.write_script(*args)
 
 
@@ -532,11 +543,57 @@ class LocalSDist(sdist.sdist):
 
     command_name = 'sdist'
 
+    def checking_reno(self):
+        """Ensure reno is installed and configured.
+
+        We can't run reno-based commands if reno isn't installed/available, and
+        don't want to if the user isn't using it.
+        """
+        if hasattr(self, '_has_reno'):
+            return self._has_reno
+
+        try:
+            # versions of reno witout this module will not have the required
+            # feature, hence the import
+            from reno import setup_command  # noqa
+        except ImportError:
+            log.info('[pbr] reno was not found or is too old. Skipping '
+                     'release notes')
+            self._has_reno = False
+            return False
+
+        conf, output_file, cache_file = setup_command.load_config(
+            self.distribution)
+
+        if not os.path.exists(os.path.join(conf.reporoot, conf.notespath)):
+            log.info('[pbr] reno does not appear to be configured. Skipping '
+                     'release notes')
+            self._has_reno = False
+            return False
+
+        self._files = [output_file, cache_file]
+
+        log.info('[pbr] Generating release notes')
+        self._has_reno = True
+
+        return True
+
+    sub_commands = [('build_reno', checking_reno)] + sdist.sdist.sub_commands
+
     def run(self):
         _from_git(self.distribution)
         # sdist.sdist is an old style class, can't use super()
         sdist.sdist.run(self)
 
+    def make_distribution(self):
+        # This is included in make_distribution because setuptools doesn't use
+        # 'get_file_list'. As such, this is the only hook point that runs after
+        # the commands in 'sub_commands'
+        if self.checking_reno():
+            self.filelist.extend(self._files)
+            self.filelist.sort()
+        sdist.sdist.make_distribution(self)
+
 try:
     from pbr import builddoc
     _have_sphinx = True
@@ -565,10 +622,14 @@ def _get_increment_kwargs(git_dir, tag):
         version_spec = tag + "..HEAD"
     else:
         version_spec = "HEAD"
-    changelog = git._run_git_command(['log', version_spec], git_dir)
-    header_len = len('    sem-ver:')
+    # Get the raw body of the commit messages so that we don't have to
+    # parse out any formatting whitespace and to avoid user settings on
+    # git log output affecting out ability to have working sem ver headers.
+    changelog = git._run_git_command(['log', '--pretty=%B', version_spec],
+                                     git_dir)
+    header_len = len('sem-ver:')
     commands = [line[header_len:].strip() for line in changelog.split('\n')
-                if line.lower().startswith('    sem-ver:')]
+                if line.lower().startswith('sem-ver:')]
     symbols = set()
     for command in commands:
         symbols.update([symbol.strip() for symbol in command.split(',')])
diff --git a/lib/pbr/testr_command.py b/lib/pbr/testr_command.py
index cd179a27a65bd691bf763ffa536ed4244cd095b9..d143565fb66f664fafdebba377599fedd4884355 100644
--- a/lib/pbr/testr_command.py
+++ b/lib/pbr/testr_command.py
@@ -45,13 +45,14 @@ import distutils.errors
 import logging
 import os
 import sys
+import warnings
 
 logger = logging.getLogger(__name__)
 
 
 class TestrReal(cmd.Command):
 
-    description = "Run unit tests using testr"
+    description = "DEPRECATED: Run unit tests using testr"
 
     user_options = [
         ('coverage', None, "Replace PYTHON with coverage and merge coverage "
@@ -100,6 +101,11 @@ class TestrReal(cmd.Command):
     def run(self):
         """Set up testr repo, then run testr."""
         logger.debug("run called")
+
+        warnings.warn('testr integration in pbr is deprecated. Please use '
+                      'the \'testr\' setup command or call testr directly',
+                      DeprecationWarning)
+
         if not os.path.isdir(".testrepository"):
             self._run_testr("init")
 
diff --git a/lib/pbr/tests/test_hooks.py b/lib/pbr/tests/test_hooks.py
index 07597060ad3f9c482334e8f0f947bf3f23c9fd55..0fcf96cac2cad32cbbde1e21451e6c8fbfefebd4 100644
--- a/lib/pbr/tests/test_hooks.py
+++ b/lib/pbr/tests/test_hooks.py
@@ -39,9 +39,7 @@
 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 
 import os
-import textwrap
 
-from testtools import content
 from testtools import matchers
 
 from pbr.tests import base
@@ -56,10 +54,6 @@ class TestHooks(base.BaseTestCase):
             cfg.set('global', 'setup-hooks',
                     'pbr_testpackage._setup_hooks.test_hook_1\n'
                     'pbr_testpackage._setup_hooks.test_hook_2')
-            cfg.set('build_ext', 'pre-hook.test_pre_hook',
-                    'pbr_testpackage._setup_hooks.test_pre_hook')
-            cfg.set('build_ext', 'post-hook.test_post_hook',
-                    'pbr_testpackage._setup_hooks.test_post_hook')
 
     def test_global_setup_hooks(self):
         """Test setup_hooks.
@@ -72,28 +66,6 @@ class TestHooks(base.BaseTestCase):
         assert 'test_hook_1\ntest_hook_2' in stdout
         assert return_code == 0
 
-    def test_command_hooks(self):
-        """Test command hooks.
-
-        Simple test that the appropriate command hooks run at the
-        beginning/end of the appropriate command.
-        """
-
-        stdout, _, return_code = self.run_setup('egg_info')
-        assert 'build_ext pre-hook' not in stdout
-        assert 'build_ext post-hook' not in stdout
-        assert return_code == 0
-
-        stdout, stderr, return_code = self.run_setup('build_ext')
-        self.addDetailUniqueName('stderr', content.text_content(stderr))
-        assert textwrap.dedent("""
-            running build_ext
-            running pre_hook pbr_testpackage._setup_hooks.test_pre_hook for command build_ext
-            build_ext pre-hook
-        """) in stdout  # flake8: noqa
-        self.expectThat(stdout, matchers.EndsWith('build_ext post-hook'))
-        assert return_code == 0
-
     def test_custom_commands_known(self):
         stdout, _, return_code = self.run_setup('--help-commands')
         self.assertFalse(return_code)
diff --git a/lib/pbr/tests/test_packaging.py b/lib/pbr/tests/test_packaging.py
index 9efcbd7892e1284e675d676edb856e336b2ac324..5512d5d62aa995aec42f51fd340a21a0aa8ae0c2 100644
--- a/lib/pbr/tests/test_packaging.py
+++ b/lib/pbr/tests/test_packaging.py
@@ -43,6 +43,7 @@ import email.errors
 import imp
 import os
 import re
+import sys
 import sysconfig
 import tempfile
 import textwrap
@@ -51,6 +52,7 @@ import fixtures
 import mock
 import pkg_resources
 import six
+import testscenarios
 import testtools
 from testtools import matchers
 import virtualenv
@@ -486,9 +488,47 @@ class TestPresenceOfGit(base.BaseTestCase):
             self.assertEqual(False, git._git_is_installed())
 
 
-class TestIndexInRequirements(base.BaseTestCase):
+class ParseRequirementsTest(base.BaseTestCase):
 
-    def test_index_in_requirement(self):
+    def test_empty_requirements(self):
+        actual = packaging.parse_requirements([])
+        self.assertEqual([], actual)
+
+    def test_default_requirements(self):
+        """Ensure default files used if no files provided."""
+        tempdir = tempfile.mkdtemp()
+        requirements = os.path.join(tempdir, 'requirements.txt')
+        with open(requirements, 'w') as f:
+            f.write('pbr')
+        # the defaults are relative to where pbr is called from so we need to
+        # override them. This is OK, however, as we want to validate that
+        # defaults are used - not what those defaults are
+        with mock.patch.object(packaging, 'REQUIREMENTS_FILES', (
+                requirements,)):
+            result = packaging.parse_requirements()
+        self.assertEqual(['pbr'], result)
+
+    def test_override_with_env(self):
+        """Ensure environment variable used if no files provided."""
+        _, tmp_file = tempfile.mkstemp(prefix='openstack', suffix='.setup')
+        with open(tmp_file, 'w') as fh:
+            fh.write("foo\nbar")
+        self.useFixture(
+            fixtures.EnvironmentVariable('PBR_REQUIREMENTS_FILES', tmp_file))
+        self.assertEqual(['foo', 'bar'],
+                         packaging.parse_requirements())
+
+    def test_override_with_env_multiple_files(self):
+        _, tmp_file = tempfile.mkstemp(prefix='openstack', suffix='.setup')
+        with open(tmp_file, 'w') as fh:
+            fh.write("foo\nbar")
+        self.useFixture(
+            fixtures.EnvironmentVariable('PBR_REQUIREMENTS_FILES',
+                                         "no-such-file," + tmp_file))
+        self.assertEqual(['foo', 'bar'],
+                         packaging.parse_requirements())
+
+    def test_index_present(self):
         tempdir = tempfile.mkdtemp()
         requirements = os.path.join(tempdir, 'requirements.txt')
         with open(requirements, 'w') as f:
@@ -498,10 +538,7 @@ class TestIndexInRequirements(base.BaseTestCase):
         result = packaging.parse_requirements([requirements])
         self.assertEqual([], result)
 
-
-class TestNestedRequirements(base.BaseTestCase):
-
-    def test_nested_requirement(self):
+    def test_nested_requirements(self):
         tempdir = tempfile.mkdtemp()
         requirements = os.path.join(tempdir, 'requirements.txt')
         nested = os.path.join(tempdir, 'nested.txt')
@@ -512,6 +549,94 @@ class TestNestedRequirements(base.BaseTestCase):
         result = packaging.parse_requirements([requirements])
         self.assertEqual(['pbr'], result)
 
+    @mock.patch('warnings.warn')
+    def test_python_version(self, mock_warn):
+        with open("requirements-py%d.txt" % sys.version_info[0],
+                  "w") as fh:
+            fh.write("# this is a comment\nfoobar\n# and another one\nfoobaz")
+        self.assertEqual(['foobar', 'foobaz'],
+                         packaging.parse_requirements())
+        mock_warn.assert_called_once_with(mock.ANY, DeprecationWarning)
+
+    @mock.patch('warnings.warn')
+    def test_python_version_multiple_options(self, mock_warn):
+        with open("requirements-py1.txt", "w") as fh:
+            fh.write("thisisatrap")
+        with open("requirements-py%d.txt" % sys.version_info[0],
+                  "w") as fh:
+            fh.write("# this is a comment\nfoobar\n# and another one\nfoobaz")
+        self.assertEqual(['foobar', 'foobaz'],
+                         packaging.parse_requirements())
+        # even though we have multiple offending files, this should only be
+        # called once
+        mock_warn.assert_called_once_with(mock.ANY, DeprecationWarning)
+
+
+class ParseRequirementsTestScenarios(base.BaseTestCase):
+
+    versioned_scenarios = [
+        ('non-versioned', {'versioned': False, 'expected': ['bar']}),
+        ('versioned', {'versioned': True, 'expected': ['bar>=1.2.3']})
+    ]
+
+    scenarios = [
+        ('normal', {'url': "foo\nbar", 'expected': ['foo', 'bar']}),
+        ('normal_with_comments', {
+            'url': "# this is a comment\nfoo\n# and another one\nbar",
+            'expected': ['foo', 'bar']}),
+        ('removes_index_lines', {'url': '-f foobar', 'expected': []}),
+    ]
+
+    scenarios = scenarios + testscenarios.multiply_scenarios([
+        ('ssh_egg_url', {'url': 'git+ssh://foo.com/zipball#egg=bar'}),
+        ('git_https_egg_url', {'url': 'git+https://foo.com/zipball#egg=bar'}),
+        ('http_egg_url', {'url': 'https://foo.com/zipball#egg=bar'}),
+    ], versioned_scenarios)
+
+    scenarios = scenarios + testscenarios.multiply_scenarios(
+        [
+            ('git_egg_url',
+                {'url': 'git://foo.com/zipball#egg=bar', 'name': 'bar'})
+        ], [
+            ('non-editable', {'editable': False}),
+            ('editable', {'editable': True}),
+        ],
+        versioned_scenarios)
+
+    def test_parse_requirements(self):
+        tmp_file = tempfile.NamedTemporaryFile()
+        req_string = self.url
+        if hasattr(self, 'editable') and self.editable:
+            req_string = ("-e %s" % req_string)
+        if hasattr(self, 'versioned') and self.versioned:
+            req_string = ("%s-1.2.3" % req_string)
+        with open(tmp_file.name, 'w') as fh:
+            fh.write(req_string)
+        self.assertEqual(self.expected,
+                         packaging.parse_requirements([tmp_file.name]))
+
+
+class ParseDependencyLinksTest(base.BaseTestCase):
+
+    def setUp(self):
+        super(ParseDependencyLinksTest, self).setUp()
+        _, self.tmp_file = tempfile.mkstemp(prefix="openstack",
+                                            suffix=".setup")
+
+    def test_parse_dependency_normal(self):
+        with open(self.tmp_file, "w") as fh:
+            fh.write("http://test.com\n")
+        self.assertEqual(
+            ["http://test.com"],
+            packaging.parse_dependency_links([self.tmp_file]))
+
+    def test_parse_dependency_with_git_egg_url(self):
+        with open(self.tmp_file, "w") as fh:
+            fh.write("-e git://foo.com/zipball#egg=bar")
+        self.assertEqual(
+            ["git://foo.com/zipball#egg=bar"],
+            packaging.parse_dependency_links([self.tmp_file]))
+
 
 class TestVersions(base.BaseTestCase):
 
@@ -759,7 +884,7 @@ class TestRequirementParsing(base.BaseTestCase):
         expected_requirements = {
             None: ['bar', 'requests-aws>=0.1.4'],
             ":(python_version=='2.6')": ['quux<1.0'],
-            ":(python_version=='2.7')": ['Routes>=1.12.3,!=2.0,!=2.1',
+            ":(python_version=='2.7')": ['Routes!=2.0,!=2.1,>=1.12.3',
                                          'requests-kerberos>=0.6'],
             'test': ['foo'],
             "test:(python_version=='2.7')": ['baz>3.2', 'bar>3.3']
@@ -778,7 +903,20 @@ class TestRequirementParsing(base.BaseTestCase):
             generated_requirements = dict(
                 pkg_resources.split_sections(requires))
 
-        self.assertEqual(expected_requirements, generated_requirements)
+        # NOTE(dhellmann): We have to spell out the comparison because
+        # the rendering for version specifiers in a range is not
+        # consistent across versions of setuptools.
+
+        for section, expected in expected_requirements.items():
+            exp_parsed = [
+                pkg_resources.Requirement.parse(s)
+                for s in expected
+            ]
+            gen_parsed = [
+                pkg_resources.Requirement.parse(s)
+                for s in generated_requirements[section]
+            ]
+            self.assertEqual(exp_parsed, gen_parsed)
 
 
 def get_soabi():
diff --git a/lib/pbr/tests/test_setup.py b/lib/pbr/tests/test_setup.py
index 0b9c81be9c26d4ef0c1fe47339c4b8ad43772f98..85d40ebf30dccf60d9b27ea4f8a6f7f480bcd178 100644
--- a/lib/pbr/tests/test_setup.py
+++ b/lib/pbr/tests/test_setup.py
@@ -17,9 +17,6 @@
 from __future__ import print_function
 
 import os
-import sys
-import tempfile
-import testscenarios
 
 try:
     import cStringIO as io
@@ -446,115 +443,3 @@ class APIAutoDocTest(base.BaseTestCase):
         self.assertTrue(
             os.path.exists(
                 "contributor/api/fake_package.fake_private_module.rst"))
-
-
-class ParseRequirementsTestScenarios(base.BaseTestCase):
-
-    versioned_scenarios = [
-        ('non-versioned', {'versioned': False, 'expected': ['bar']}),
-        ('versioned', {'versioned': True, 'expected': ['bar>=1.2.3']})
-    ]
-
-    scenarios = [
-        ('normal', {'url': "foo\nbar", 'expected': ['foo', 'bar']}),
-        ('normal_with_comments', {
-            'url': "# this is a comment\nfoo\n# and another one\nbar",
-            'expected': ['foo', 'bar']}),
-        ('removes_index_lines', {'url': '-f foobar', 'expected': []}),
-    ]
-
-    scenarios = scenarios + testscenarios.multiply_scenarios([
-        ('ssh_egg_url', {'url': 'git+ssh://foo.com/zipball#egg=bar'}),
-        ('git_https_egg_url', {'url': 'git+https://foo.com/zipball#egg=bar'}),
-        ('http_egg_url', {'url': 'https://foo.com/zipball#egg=bar'}),
-    ], versioned_scenarios)
-
-    scenarios = scenarios + testscenarios.multiply_scenarios(
-        [
-            ('git_egg_url',
-                {'url': 'git://foo.com/zipball#egg=bar', 'name': 'bar'})
-        ], [
-            ('non-editable', {'editable': False}),
-            ('editable', {'editable': True}),
-        ],
-        versioned_scenarios)
-
-    def test_parse_requirements(self):
-        tmp_file = tempfile.NamedTemporaryFile()
-        req_string = self.url
-        if hasattr(self, 'editable') and self.editable:
-            req_string = ("-e %s" % req_string)
-        if hasattr(self, 'versioned') and self.versioned:
-            req_string = ("%s-1.2.3" % req_string)
-        with open(tmp_file.name, 'w') as fh:
-            fh.write(req_string)
-        self.assertEqual(self.expected,
-                         packaging.parse_requirements([tmp_file.name]))
-
-
-class ParseRequirementsTest(base.BaseTestCase):
-
-    def setUp(self):
-        super(ParseRequirementsTest, self).setUp()
-        (fd, self.tmp_file) = tempfile.mkstemp(prefix='openstack',
-                                               suffix='.setup')
-
-    def test_parse_requirements_override_with_env(self):
-        with open(self.tmp_file, 'w') as fh:
-            fh.write("foo\nbar")
-        self.useFixture(
-            fixtures.EnvironmentVariable('PBR_REQUIREMENTS_FILES',
-                                         self.tmp_file))
-        self.assertEqual(['foo', 'bar'],
-                         packaging.parse_requirements())
-
-    def test_parse_requirements_override_with_env_multiple_files(self):
-        with open(self.tmp_file, 'w') as fh:
-            fh.write("foo\nbar")
-        self.useFixture(
-            fixtures.EnvironmentVariable('PBR_REQUIREMENTS_FILES',
-                                         "no-such-file," + self.tmp_file))
-        self.assertEqual(['foo', 'bar'],
-                         packaging.parse_requirements())
-
-    def test_get_requirement_from_file_empty(self):
-        actual = packaging.get_reqs_from_files([])
-        self.assertEqual([], actual)
-
-    def test_parse_requirements_python_version(self):
-        with open("requirements-py%d.txt" % sys.version_info[0],
-                  "w") as fh:
-            fh.write("# this is a comment\nfoobar\n# and another one\nfoobaz")
-        self.assertEqual(['foobar', 'foobaz'],
-                         packaging.parse_requirements())
-
-    def test_parse_requirements_right_python_version(self):
-        with open("requirements-py1.txt", "w") as fh:
-            fh.write("thisisatrap")
-        with open("requirements-py%d.txt" % sys.version_info[0],
-                  "w") as fh:
-            fh.write("# this is a comment\nfoobar\n# and another one\nfoobaz")
-        self.assertEqual(['foobar', 'foobaz'],
-                         packaging.parse_requirements())
-
-
-class ParseDependencyLinksTest(base.BaseTestCase):
-
-    def setUp(self):
-        super(ParseDependencyLinksTest, self).setUp()
-        (fd, self.tmp_file) = tempfile.mkstemp(prefix="openstack",
-                                               suffix=".setup")
-
-    def test_parse_dependency_normal(self):
-        with open(self.tmp_file, "w") as fh:
-            fh.write("http://test.com\n")
-        self.assertEqual(
-            ["http://test.com"],
-            packaging.parse_dependency_links([self.tmp_file]))
-
-    def test_parse_dependency_with_git_egg_url(self):
-        with open(self.tmp_file, "w") as fh:
-            fh.write("-e git://foo.com/zipball#egg=bar")
-        self.assertEqual(
-            ["git://foo.com/zipball#egg=bar"],
-            packaging.parse_dependency_links([self.tmp_file]))
diff --git a/lib/pbr/tests/test_util.py b/lib/pbr/tests/test_util.py
index 048b2b96815a1ab010e3c1d48c7c00e088e54d9e..370a7deee25e1adce773e99c55f167e9767f2d3f 100644
--- a/lib/pbr/tests/test_util.py
+++ b/lib/pbr/tests/test_util.py
@@ -36,8 +36,12 @@ class TestExtrasRequireParsingScenarios(base.BaseTestCase):
                     baz>=3.2
                     foo
                 """,
-            'expected_extra_requires': {'first': ['foo', 'bar==1.0'],
-                                        'second': ['baz>=3.2', 'foo']}
+            'expected_extra_requires': {
+                'first': ['foo', 'bar==1.0'],
+                'second': ['baz>=3.2', 'foo'],
+                'test': ['requests-mock'],
+                "test:(python_version=='2.6')": ['ordereddict'],
+            }
         }),
         ('with_markers', {
             'config_text': """
diff --git a/lib/pbr/tests/test_version.py b/lib/pbr/tests/test_version.py
index 14c8d173674447fc4e7b872f4f4a1b46cebbdef7..d861d572031141fd92aaf8844503d9b89ae84905 100644
--- a/lib/pbr/tests/test_version.py
+++ b/lib/pbr/tests/test_version.py
@@ -84,6 +84,19 @@ class TestSemanticVersion(base.BaseTestCase):
             lambda: from_pip_string('1.2.3.post5.dev6'),
             matchers.raises(ValueError))
 
+    def test_from_pip_string_v_version(self):
+        parsed = from_pip_string('v1.2.3')
+        expected = version.SemanticVersion(1, 2, 3)
+        self.expectThat(expected, matchers.Equals(parsed))
+
+        expected = version.SemanticVersion(1, 2, 3, 'a', 5, dev_count=6)
+        parsed = from_pip_string('V1.2.3.0a4.post6')
+        self.expectThat(expected, matchers.Equals(parsed))
+
+        self.expectThat(
+            lambda: from_pip_string('x1.2.3'),
+            matchers.raises(ValueError))
+
     def test_from_pip_string_legacy_nonzero_lead_in(self):
         # reported in bug 1361251
         expected = version.SemanticVersion(
diff --git a/lib/pbr/tests/testpackage/setup.cfg b/lib/pbr/tests/testpackage/setup.cfg
index 242c9c613bfb0c5686c216d6cad6dfc55243c10b..bf4c26a2758ac178ed115f9bec0e731c14d80acf 100644
--- a/lib/pbr/tests/testpackage/setup.cfg
+++ b/lib/pbr/tests/testpackage/setup.cfg
@@ -6,10 +6,15 @@ version = 0.1.dev
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
 home-page = http://pypi.python.org/pypi/pbr
+project_urls =
+    Bug Tracker = https://bugs.launchpad.net/pbr/
+    Documentation = https://docs.openstack.org/pbr/
+    Source Code = https://git.openstack.org/cgit/openstack-dev/pbr/
 summary = Test package for testing pbr
 description-file =
     README.txt
     CHANGES.txt
+description-content-type = text/plain; charset=UTF-8
 requires-python = >=2.5
 
 requires-dist =
@@ -51,7 +56,3 @@ optional = True
 #    pbr_testpackage._setup_hooks.test_hook_1
 #    pbr_testpackage._setup_hooks.test_hook_2
 commands = pbr_testpackage._setup_hooks.test_command
-
-[build_ext]
-#pre-hook.test_pre_hook = pbr_testpackage._setup_hooks.test_pre_hook
-#post-hook.test_post_hook = pbr_testpackage._setup_hooks.test_post_hook
diff --git a/lib/pbr/tests/testpackage/test-requirements.txt b/lib/pbr/tests/testpackage/test-requirements.txt
index f283aff52509d7ddc1dcfcc30b83db86d52e1883..8755eb4caea86fcdec52e163036caf9d8fac1616 100644
--- a/lib/pbr/tests/testpackage/test-requirements.txt
+++ b/lib/pbr/tests/testpackage/test-requirements.txt
@@ -1 +1,2 @@
 ordereddict;python_version=='2.6'
+requests-mock
diff --git a/lib/pbr/util.py b/lib/pbr/util.py
index a117785517b23ba1f4880780c12dc6628c41dbed..31a2a2622989937513e87b0ac997cb661fb6775c 100644
--- a/lib/pbr/util.py
+++ b/lib/pbr/util.py
@@ -101,9 +101,11 @@ D1_D2_SETUP_ARGS = {
     "maintainer": ("metadata",),
     "maintainer_email": ("metadata",),
     "url": ("metadata", "home_page"),
+    "project_urls": ("metadata",),
     "description": ("metadata", "summary"),
     "keywords": ("metadata",),
     "long_description": ("metadata", "description"),
+    "long_description_content_type": ("metadata", "description_content_type"),
     "download_url": ("metadata",),
     "classifiers": ("metadata", "classifier"),
     "platforms": ("metadata", "platform"),  # **
@@ -148,6 +150,9 @@ MULTI_FIELDS = ("classifiers",
                 "tests_require",
                 "cmdclass")
 
+# setup() arguments that can have mapping values in setup.cfg
+MAP_FIELDS = ("project_urls",)
+
 # setup() arguments that contain boolean values
 BOOL_FIELDS = ("use_2to3", "zip_safe", "include_package_data")
 
@@ -193,9 +198,9 @@ def cfg_to_args(path='setup.cfg', script_args=()):
     This method uses an existing setup.cfg to generate a dictionary of
     keywords that can be used by distutils.core.setup(kwargs**).
 
-    :param file:
+    :param path:
         The setup.cfg path.
-    :parm script_args:
+    :param script_args:
         List of commands setup.py was called with.
     :raises DistutilsFileError:
         When the setup.cfg file is not found.
@@ -209,7 +214,11 @@ def cfg_to_args(path='setup.cfg', script_args=()):
     if not os.path.exists(path):
         raise errors.DistutilsFileError("file '%s' does not exist" %
                                         os.path.abspath(path))
-    parser.read(path)
+    try:
+        parser.read(path, encoding='utf-8')
+    except TypeError:
+        # Python 2 doesn't accept the encoding kwarg
+        parser.read(path)
     config = {}
     for section in parser.sections():
         config[section] = dict()
@@ -264,8 +273,6 @@ def cfg_to_args(path='setup.cfg', script_args=()):
         if entry_points:
             kwargs['entry_points'] = entry_points
 
-        wrap_commands(kwargs)
-
         # Handle the [files]/extra_files option
         files_extra_files = has_get_option(config, 'files', 'extra_files')
         if files_extra_files:
@@ -323,6 +330,12 @@ def setup_cfg_to_setup_kwargs(config, script_args=()):
             in_cfg_value = split_csv(in_cfg_value)
         if arg in MULTI_FIELDS:
             in_cfg_value = split_multiline(in_cfg_value)
+        elif arg in MAP_FIELDS:
+            in_cfg_map = {}
+            for i in split_multiline(in_cfg_value):
+                k, v = i.split('=')
+                in_cfg_map[k.strip()] = v.strip()
+            in_cfg_value = in_cfg_map
         elif arg in BOOL_FIELDS:
             # Provide some flexibility here...
             if in_cfg_value.lower() in ('true', 't', '1', 'yes', 'y'):
@@ -402,6 +415,13 @@ def setup_cfg_to_setup_kwargs(config, script_args=()):
     if 'extras' in config:
         requirement_pattern = '(?P<package>[^:]*):?(?P<env_marker>[^#]*?)(?:\s*#.*)?$'
         extras = config['extras']
+        # Add contents of test-requirements, if any, into an extra named
+        # 'test' if one does not already exist.
+        if 'test' not in extras:
+            from pbr import packaging
+            extras['test'] = "\n".join(packaging.parse_requirements(
+                packaging.TEST_REQUIREMENTS_FILES)).replace(';', ':')
+
         for extra in extras:
             extra_requirements = []
             requirements = split_multiline(extras[extra])
@@ -552,103 +572,6 @@ def get_entry_points(config):
                 for option, value in config['entry_points'].items())
 
 
-def wrap_commands(kwargs):
-    dist = st_dist.Distribution()
-
-    # This should suffice to get the same config values and command classes
-    # that the actual Distribution will see (not counting cmdclass, which is
-    # handled below)
-    dist.parse_config_files()
-
-    # Setuptools doesn't patch get_command_list, and as such we do not get
-    # extra commands from entry_points.  As we need to be compatable we deal
-    # with this here.
-    for ep in pkg_resources.iter_entry_points('distutils.commands'):
-        if ep.name not in dist.cmdclass:
-            if hasattr(ep, 'resolve'):
-                cmdclass = ep.resolve()
-            else:
-                # Old setuptools does not have ep.resolve, and load with
-                # arguments is depricated in 11+.  Use resolve, 12+, if we
-                # can, otherwise fall back to load.
-                # Setuptools 11 will throw a deprication warning, as it
-                # uses _load instead of resolve.
-                cmdclass = ep.load(False)
-            dist.cmdclass[ep.name] = cmdclass
-
-    for cmd, _ in dist.get_command_list():
-        hooks = {}
-        for opt, val in dist.get_option_dict(cmd).items():
-            val = val[1]
-            if opt.startswith('pre_hook.') or opt.startswith('post_hook.'):
-                hook_type, alias = opt.split('.', 1)
-                hook_dict = hooks.setdefault(hook_type, {})
-                hook_dict[alias] = val
-        if not hooks:
-            continue
-
-        if 'cmdclass' in kwargs and cmd in kwargs['cmdclass']:
-            cmdclass = kwargs['cmdclass'][cmd]
-        else:
-            cmdclass = dist.get_command_class(cmd)
-
-        new_cmdclass = wrap_command(cmd, cmdclass, hooks)
-        kwargs.setdefault('cmdclass', {})[cmd] = new_cmdclass
-
-
-def wrap_command(cmd, cmdclass, hooks):
-    def run(self, cmdclass=cmdclass):
-        self.run_command_hooks('pre_hook')
-        cmdclass.run(self)
-        self.run_command_hooks('post_hook')
-
-    return type(cmd, (cmdclass, object),
-                {'run': run, 'run_command_hooks': run_command_hooks,
-                 'pre_hook': hooks.get('pre_hook'),
-                 'post_hook': hooks.get('post_hook')})
-
-
-def run_command_hooks(cmd_obj, hook_kind):
-    """Run hooks registered for that command and phase.
-
-    *cmd_obj* is a finalized command object; *hook_kind* is either
-    'pre_hook' or 'post_hook'.
-    """
-
-    if hook_kind not in ('pre_hook', 'post_hook'):
-        raise ValueError('invalid hook kind: %r' % hook_kind)
-
-    hooks = getattr(cmd_obj, hook_kind, None)
-
-    if hooks is None:
-        return
-
-    for hook in hooks.values():
-        if isinstance(hook, str):
-            try:
-                hook_obj = resolve_name(hook)
-            except ImportError:
-                err = sys.exc_info()[1] # For py3k
-                raise errors.DistutilsModuleError('cannot find hook %s: %s' %
-                                                  (hook,err))
-        else:
-            hook_obj = hook
-
-        if not hasattr(hook_obj, '__call__'):
-            raise errors.DistutilsOptionError('hook %r is not callable' % hook)
-
-        log.info('running %s %s for command %s',
-                 hook_kind, hook, cmd_obj.get_command_name())
-
-        try :
-            hook_obj(cmd_obj)
-        except:
-            e = sys.exc_info()[1]
-            log.error('hook %s raised exception: %s\n' % (hook, e))
-            log.error(traceback.format_exc())
-            sys.exit(1)
-
-
 def has_get_option(config, section, option):
     if section in config and option in config[section]:
         return config[section][option]
@@ -661,7 +584,7 @@ def split_multiline(value):
 
     value = [element for element in
              (line.strip() for line in value.split('\n'))
-             if element]
+             if element and not element.startswith('#')]
     return value
 
 
@@ -674,21 +597,6 @@ def split_csv(value):
     return value
 
 
-def monkeypatch_method(cls):
-    """A function decorator to monkey-patch a method of the same name on the
-    given class.
-    """
-
-    def wrapper(func):
-        orig = getattr(cls, func.__name__, None)
-        if orig and not hasattr(orig, '_orig'):  # Already patched
-            setattr(func, '_orig', orig)
-            setattr(cls, func.__name__, func)
-        return func
-
-    return wrapper
-
-
 # The following classes are used to hack Distribution.command_options a bit
 class DefaultGetDict(defaultdict):
     """Like defaultdict, but the get() method also sets and returns the default
@@ -699,20 +607,3 @@ class DefaultGetDict(defaultdict):
         if default is None:
             default = self.default_factory()
         return super(DefaultGetDict, self).setdefault(key, default)
-
-
-class IgnoreDict(dict):
-    """A dictionary that ignores any insertions in which the key is a string
-    matching any string in `ignore`.  The ignore list can also contain wildcard
-    patterns using '*'.
-    """
-
-    def __init__(self, ignore):
-        self.__ignore = re.compile(r'(%s)' % ('|'.join(
-                                   [pat.replace('*', '.*')
-                                    for pat in ignore])))
-
-    def __setitem__(self, key, val):
-        if self.__ignore.match(key):
-            return
-        super(IgnoreDict, self).__setitem__(key, val)
diff --git a/lib/pbr/version.py b/lib/pbr/version.py
index 474faf16a0db64d7fa3e8885d627956f62592506..5eb217afe89900fe32fcd48c78460e3962a05912 100644
--- a/lib/pbr/version.py
+++ b/lib/pbr/version.py
@@ -149,6 +149,7 @@ class SemanticVersion(object):
     @classmethod
     def _from_pip_string_unsafe(klass, version_string):
         # Versions need to start numerically, ignore if not
+        version_string = version_string.lstrip('vV')
         if not version_string[:1].isdigit():
             raise ValueError("Invalid version %r" % version_string)
         input_components = version_string.split('.')
diff --git a/lib/pytz/__init__.py b/lib/pytz/__init__.py
index 5f753b6648084db92e08872295a442a3030b0e64..178d3c46fc4a881804f49a8270d9e86475da0ce4 100644
--- a/lib/pytz/__init__.py
+++ b/lib/pytz/__init__.py
@@ -17,13 +17,13 @@ from pytz.exceptions import InvalidTimeError
 from pytz.exceptions import NonExistentTimeError
 from pytz.exceptions import UnknownTimeZoneError
 from pytz.lazy import LazyDict, LazyList, LazySet
-from pytz.tzinfo import unpickler
+from pytz.tzinfo import unpickler, BaseTzInfo
 from pytz.tzfile import build_tzinfo
 
 
 # The IANA (nee Olson) database is updated several times a year.
-OLSON_VERSION = '2018c'
-VERSION = '2018.3'  # Switching to pip compatible version numbering.
+OLSON_VERSION = '2018d'
+VERSION = '2018.4'  # pip compatible version number.
 __version__ = VERSION
 
 OLSEN_VERSION = OLSON_VERSION  # Old releases had this misspelling
@@ -37,10 +37,7 @@ __all__ = [
 ]
 
 
-try:
-    unicode
-
-except NameError:  # Python 3.x
+if sys.version_info[0] > 2:  # Python 3.x
 
     # Python 3.x doesn't have unicode(), making writing code
     # for Python 2.3 and Python 3.x a pain.
@@ -191,7 +188,7 @@ ZERO = datetime.timedelta(0)
 HOUR = datetime.timedelta(hours=1)
 
 
-class UTC(datetime.tzinfo):
+class UTC(BaseTzInfo):
     """UTC
 
     Optimized UTC implementation. It unpickles using the single module global
@@ -416,18 +413,18 @@ def FixedOffset(offset, _tzinfos={}):
         >>> one = FixedOffset(-330)
         >>> one
         pytz.FixedOffset(-330)
-        >>> one.utcoffset(datetime.datetime.now())
-        datetime.timedelta(-1, 66600)
-        >>> one.dst(datetime.datetime.now())
-        datetime.timedelta(0)
+        >>> str(one.utcoffset(datetime.datetime.now()))
+        '-1 day, 18:30:00'
+        >>> str(one.dst(datetime.datetime.now()))
+        '0:00:00'
 
         >>> two = FixedOffset(1380)
         >>> two
         pytz.FixedOffset(1380)
-        >>> two.utcoffset(datetime.datetime.now())
-        datetime.timedelta(0, 82800)
-        >>> two.dst(datetime.datetime.now())
-        datetime.timedelta(0)
+        >>> str(two.utcoffset(datetime.datetime.now()))
+        '23:00:00'
+        >>> str(two.dst(datetime.datetime.now()))
+        '0:00:00'
 
     The datetime.timedelta must be between the range of -1 and 1 day,
     non-inclusive.
diff --git a/lib/pytz/lazy.py b/lib/pytz/lazy.py
index fe91ff8dfd9b2167842494bc8496f224f9cb768a..39344fc1f8c77d5ec43539d0c8e655f4b5d7d6f6 100644
--- a/lib/pytz/lazy.py
+++ b/lib/pytz/lazy.py
@@ -1,8 +1,11 @@
 from threading import RLock
 try:
-    from UserDict import DictMixin
-except ImportError:
-    from collections import Mapping as DictMixin
+    from collections.abc import Mapping as DictMixin
+except ImportError:  # Python < 3.3
+    try:
+        from UserDict import DictMixin  # Python 2
+    except ImportError:  # Python 3.0-3.3
+        from collections import Mapping as DictMixin
 
 
 # With lazy loading, we might end up with multiple threads triggering
diff --git a/lib/pytz/tzinfo.py b/lib/pytz/tzinfo.py
index d78c2edc1d252dc506c914044afa51d9612e7e85..725978d53720202bf7b1a64f356f47c49d42fd92 100644
--- a/lib/pytz/tzinfo.py
+++ b/lib/pytz/tzinfo.py
@@ -403,11 +403,11 @@ class DstTzInfo(BaseTzInfo):
         >>> tz = timezone('America/St_Johns')
         >>> ambiguous = datetime(2009, 10, 31, 23, 30)
 
-        >>> tz.utcoffset(ambiguous, is_dst=False)
-        datetime.timedelta(-1, 73800)
+        >>> str(tz.utcoffset(ambiguous, is_dst=False))
+        '-1 day, 20:30:00'
 
-        >>> tz.utcoffset(ambiguous, is_dst=True)
-        datetime.timedelta(-1, 77400)
+        >>> str(tz.utcoffset(ambiguous, is_dst=True))
+        '-1 day, 21:30:00'
 
         >>> try:
         ...     tz.utcoffset(ambiguous)
@@ -435,19 +435,19 @@ class DstTzInfo(BaseTzInfo):
 
         >>> normal = datetime(2009, 9, 1)
 
-        >>> tz.dst(normal)
-        datetime.timedelta(0, 3600)
-        >>> tz.dst(normal, is_dst=False)
-        datetime.timedelta(0, 3600)
-        >>> tz.dst(normal, is_dst=True)
-        datetime.timedelta(0, 3600)
+        >>> str(tz.dst(normal))
+        '1:00:00'
+        >>> str(tz.dst(normal, is_dst=False))
+        '1:00:00'
+        >>> str(tz.dst(normal, is_dst=True))
+        '1:00:00'
 
         >>> ambiguous = datetime(2009, 10, 31, 23, 30)
 
-        >>> tz.dst(ambiguous, is_dst=False)
-        datetime.timedelta(0)
-        >>> tz.dst(ambiguous, is_dst=True)
-        datetime.timedelta(0, 3600)
+        >>> str(tz.dst(ambiguous, is_dst=False))
+        '0:00:00'
+        >>> str(tz.dst(ambiguous, is_dst=True))
+        '1:00:00'
         >>> try:
         ...     tz.dst(ambiguous)
         ... except AmbiguousTimeError:
diff --git a/lib/pytz/zoneinfo/Africa/Bissau b/lib/pytz/zoneinfo/Africa/Bissau
index 4e6fbe10367ca149b5e532e7c63489ae26bfd6cf..8e32be3e6e843379e69000273552ab8588c5ca34 100644
Binary files a/lib/pytz/zoneinfo/Africa/Bissau and b/lib/pytz/zoneinfo/Africa/Bissau differ
diff --git a/lib/pytz/zoneinfo/Africa/Sao_Tome b/lib/pytz/zoneinfo/Africa/Sao_Tome
index 3c0d33b0cbbc84b25dc00df433264a940fb01235..a4ece7ff2b7aba7f8e99fb8ee4ddbe1d439056cf 100644
Binary files a/lib/pytz/zoneinfo/Africa/Sao_Tome and b/lib/pytz/zoneinfo/Africa/Sao_Tome differ
diff --git a/lib/pytz/zoneinfo/America/Grand_Turk b/lib/pytz/zoneinfo/America/Grand_Turk
index a9740dae6876987c641a1f061b048288f7a577b0..4c8ca6f7fe8e432b6d0a2f511d7993546f3a5c68 100644
Binary files a/lib/pytz/zoneinfo/America/Grand_Turk and b/lib/pytz/zoneinfo/America/Grand_Turk differ
diff --git a/lib/pytz/zoneinfo/America/Jamaica b/lib/pytz/zoneinfo/America/Jamaica
index 006689bc895854db5deb33a29ff5d99c6c205fc6..7aedd262a57f096cda47a8542b3b66633561641d 100644
Binary files a/lib/pytz/zoneinfo/America/Jamaica and b/lib/pytz/zoneinfo/America/Jamaica differ
diff --git a/lib/pytz/zoneinfo/America/Montevideo b/lib/pytz/zoneinfo/America/Montevideo
index 9c6abeb93c2c25ade68add3f9626506739908ada..f524fd219e753111d18e26ea890a8ea16b5e0566 100644
Binary files a/lib/pytz/zoneinfo/America/Montevideo and b/lib/pytz/zoneinfo/America/Montevideo differ
diff --git a/lib/pytz/zoneinfo/Antarctica/Casey b/lib/pytz/zoneinfo/Antarctica/Casey
index 676f06da4a89848a21cfc9dbfd4ea7a40cc4c22a..d0bbacc8a9647d6d31b36a561c6b5922c71d505c 100644
Binary files a/lib/pytz/zoneinfo/Antarctica/Casey and b/lib/pytz/zoneinfo/Antarctica/Casey differ
diff --git a/lib/pytz/zoneinfo/Asia/Gaza b/lib/pytz/zoneinfo/Asia/Gaza
index 1818affb54a514d740b3636bd96f9f9dc9f25df0..60d0de00ad21c8a7260046dc49fdfa9e040838f1 100644
Binary files a/lib/pytz/zoneinfo/Asia/Gaza and b/lib/pytz/zoneinfo/Asia/Gaza differ
diff --git a/lib/pytz/zoneinfo/Asia/Hebron b/lib/pytz/zoneinfo/Asia/Hebron
index 286a9351372a529574f14f1299f9672123fa74b2..a2e1b364f9b50814468b5bf76290bfeec2bac37c 100644
Binary files a/lib/pytz/zoneinfo/Asia/Hebron and b/lib/pytz/zoneinfo/Asia/Hebron differ
diff --git a/lib/pytz/zoneinfo/Asia/Macao b/lib/pytz/zoneinfo/Asia/Macao
index 46c1bad540095a7bbcfb42ee62324242a8128162..2c20a32651d9da10d1d6f63495c3cf1fd55c7f16 100644
Binary files a/lib/pytz/zoneinfo/Asia/Macao and b/lib/pytz/zoneinfo/Asia/Macao differ
diff --git a/lib/pytz/zoneinfo/Asia/Macau b/lib/pytz/zoneinfo/Asia/Macau
index 46c1bad540095a7bbcfb42ee62324242a8128162..2c20a32651d9da10d1d6f63495c3cf1fd55c7f16 100644
Binary files a/lib/pytz/zoneinfo/Asia/Macau and b/lib/pytz/zoneinfo/Asia/Macau differ
diff --git a/lib/pytz/zoneinfo/Atlantic/Azores b/lib/pytz/zoneinfo/Atlantic/Azores
index 37218e127e4bc6dc6993c525023ae8b5c4d25105..1895e1b1e19120123e67367e55de3f2a5dac99cb 100644
Binary files a/lib/pytz/zoneinfo/Atlantic/Azores and b/lib/pytz/zoneinfo/Atlantic/Azores differ
diff --git a/lib/pytz/zoneinfo/Atlantic/Cape_Verde b/lib/pytz/zoneinfo/Atlantic/Cape_Verde
index 1c012da59793fc7ab7cddd643d6010d473c62cbb..6bda6db7604c316edf78226eed2f785ee5f8aecd 100644
Binary files a/lib/pytz/zoneinfo/Atlantic/Cape_Verde and b/lib/pytz/zoneinfo/Atlantic/Cape_Verde differ
diff --git a/lib/pytz/zoneinfo/Atlantic/Madeira b/lib/pytz/zoneinfo/Atlantic/Madeira
index 9c60071e4964de7b32d7d095b075d253e1fe5c3f..e25f8a599622cb45cd2afd7bf739e4d1ed5ed589 100644
Binary files a/lib/pytz/zoneinfo/Atlantic/Madeira and b/lib/pytz/zoneinfo/Atlantic/Madeira differ
diff --git a/lib/pytz/zoneinfo/Europe/Lisbon b/lib/pytz/zoneinfo/Europe/Lisbon
index b9aff3a51cae6888cbd0dce88d4f2d1ace1e88ae..a85653044e0094363ea0f30d017a20ccdce2227d 100644
Binary files a/lib/pytz/zoneinfo/Europe/Lisbon and b/lib/pytz/zoneinfo/Europe/Lisbon differ
diff --git a/lib/pytz/zoneinfo/Jamaica b/lib/pytz/zoneinfo/Jamaica
index 006689bc895854db5deb33a29ff5d99c6c205fc6..7aedd262a57f096cda47a8542b3b66633561641d 100644
Binary files a/lib/pytz/zoneinfo/Jamaica and b/lib/pytz/zoneinfo/Jamaica differ
diff --git a/lib/pytz/zoneinfo/Pacific/Enderbury b/lib/pytz/zoneinfo/Pacific/Enderbury
index b729b256a18079893515880d52c4f06e0acc175f..a3f30e5c7b9166d408a0851bfac437293c72b0cf 100644
Binary files a/lib/pytz/zoneinfo/Pacific/Enderbury and b/lib/pytz/zoneinfo/Pacific/Enderbury differ
diff --git a/lib/pytz/zoneinfo/Pacific/Kiritimati b/lib/pytz/zoneinfo/Pacific/Kiritimati
index 94384558c70125ad7d1ed0c47e5fef6d86cfa832..762275d3c1d28a472dda088be5b602336e38e84d 100644
Binary files a/lib/pytz/zoneinfo/Pacific/Kiritimati and b/lib/pytz/zoneinfo/Pacific/Kiritimati differ
diff --git a/lib/pytz/zoneinfo/Portugal b/lib/pytz/zoneinfo/Portugal
index b9aff3a51cae6888cbd0dce88d4f2d1ace1e88ae..a85653044e0094363ea0f30d017a20ccdce2227d 100644
Binary files a/lib/pytz/zoneinfo/Portugal and b/lib/pytz/zoneinfo/Portugal differ
diff --git a/lib/pytz/zoneinfo/tzdata.zi b/lib/pytz/zoneinfo/tzdata.zi
index 0b938fce9f0050e5d7ab7f816bb9507cd022813a..0841ab7e53d892ce2b5c2bfd81f31a4293958e1f 100644
--- a/lib/pytz/zoneinfo/tzdata.zi
+++ b/lib/pytz/zoneinfo/tzdata.zi
@@ -32,7 +32,7 @@ Z Africa/Algiers 0:12:12 - LMT 1891 Mar 15 0:1
 1 A CE%sT 1979 O 26
 0 A WE%sT 1981 May
 1 - CET
-Z Atlantic/Cape_Verde -1:34:4 - LMT 1907
+Z Atlantic/Cape_Verde -1:34:4 - LMT 1912 Ja 1 2u
 -2 - -02 1942 S
 -2 1 -01 1945 O 15
 -2 - -02 1975 N 25 2
@@ -86,11 +86,11 @@ R B 2014 o - Jul 31 24 1 S
 R B 2014 o - S lastTh 24 0 -
 Z Africa/Cairo 2:5:9 - LMT 1900 O
 2 B EE%sT
-R C 1920 1942 - S 1 0 0:20 GHST
-R C 1920 1942 - D 31 0 0 GMT
+R C 1920 1942 - S 1 0 0:20 -
+R C 1920 1942 - D 31 0 0 -
 Z Africa/Accra -0:0:52 - LMT 1918
 0 C GMT/+0020
-Z Africa/Bissau -1:2:20 - LMT 1912
+Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u
 -1 - -01 1975
 0 - GMT
 Z Africa/Nairobi 2:27:16 - LMT 1928 Jul
@@ -137,9 +137,9 @@ Z Africa/Tripoli 0:52:44 - LMT 1920
 2 - EET 2012 N 10 2
 1 D CE%sT 2013 O 25 2
 2 - EET
-R E 1982 o - O 10 0 1 S
+R E 1982 o - O 10 0 1 -
 R E 1983 o - Mar 21 0 0 -
-R E 2008 o - O lastSun 2 1 S
+R E 2008 o - O lastSun 2 1 -
 R E 2009 o - Mar lastSun 2 0 -
 Z Indian/Mauritius 3:50 - LMT 1907
 4 E +04/+05
@@ -238,7 +238,7 @@ Li Africa/Lagos Africa/Porto-Novo
 Z Indian/Reunion 3:41:52 - LMT 1911 Jun
 4 - +04
 Z Africa/Sao_Tome 0:26:56 - LMT 1884
--0:36:45 - LMT 1912
+-0:36:45 - LMT 1912 Ja 1 0u
 0 - GMT 2018 Ja 1 1
 1 - WAT
 Z Indian/Mahe 3:41:48 - LMT 1906 Jun
@@ -295,7 +295,8 @@ Z Antarctica/Casey 0 - -00 1969
 8 - +08 2011 O 28 2
 11 - +11 2012 F 21 17u
 8 - +08 2016 O 22
-11 - +11
+11 - +11 2018 Mar 11 4
+8 - +08
 Z Antarctica/Davis 0 - -00 1957 Ja 13
 7 - +07 1964 N
 0 - -00 1969 F
@@ -326,7 +327,7 @@ Z Antarctica/Rothera 0 - -00 1976 D
 Z Asia/Kabul 4:36:48 - LMT 1890
 4 - +04 1945
 4:30 - +0430
-R L 2011 o - Mar lastSun 2s 1 S
+R L 2011 o - Mar lastSun 2s 1 -
 R L 2011 o - O lastSun 2s 0 -
 Z Asia/Yerevan 2:58 - LMT 1924 May 2
 3 - +03 1957 Mar
@@ -335,7 +336,7 @@ Z Asia/Yerevan 2:58 - LMT 1924 May 2
 4 - +04 1997
 4 M +04/+05 2011
 4 L +04/+05
-R N 1997 2015 - Mar lastSun 4 1 S
+R N 1997 2015 - Mar lastSun 4 1 -
 R N 1997 2015 - O lastSun 5 0 -
 Z Asia/Baku 3:19:24 - LMT 1924 May 2
 3 - +03 1957 Mar
@@ -344,7 +345,7 @@ Z Asia/Baku 3:19:24 - LMT 1924 May 2
 4 - +04 1996
 4 O +04/+05 1997
 4 N +04/+05
-R P 2009 o - Jun 19 23 1 S
+R P 2009 o - Jun 19 23 1 -
 R P 2009 o - D 31 24 0 -
 Z Asia/Dhaka 6:1:40 - LMT 1890
 5:53:20 - HMT 1941 O
@@ -434,7 +435,7 @@ R U 1974 1977 - O Sun>=15 3:30 0 S
 R U 1975 1977 - Ap Sun>=15 3:30 1 D
 R U 1978 1980 - Ap Sun>=15 0 1 D
 R U 1978 1980 - O Sun>=15 0 0 S
-Z Asia/Macau 7:34:20 - LMT 1912
+Z Asia/Macau 7:34:20 - LMT 1911 D 31 16u
 8 U C%sT
 R V 1975 o - Ap 13 0 1 S
 R V 1975 o - O 12 0 0 -
@@ -505,68 +506,68 @@ Z Asia/Jayapura 9:22:48 - LMT 1932 N
 9 - +09 1944 S
 9:30 - +0930 1964
 9 - WIT
-R X 1978 1980 - Mar 21 0 1 D
-R X 1978 o - O 21 0 0 S
-R X 1979 o - S 19 0 0 S
-R X 1980 o - S 23 0 0 S
-R X 1991 o - May 3 0 1 D
-R X 1992 1995 - Mar 22 0 1 D
-R X 1991 1995 - S 22 0 0 S
-R X 1996 o - Mar 21 0 1 D
-R X 1996 o - S 21 0 0 S
-R X 1997 1999 - Mar 22 0 1 D
-R X 1997 1999 - S 22 0 0 S
-R X 2000 o - Mar 21 0 1 D
-R X 2000 o - S 21 0 0 S
-R X 2001 2003 - Mar 22 0 1 D
-R X 2001 2003 - S 22 0 0 S
-R X 2004 o - Mar 21 0 1 D
-R X 2004 o - S 21 0 0 S
-R X 2005 o - Mar 22 0 1 D
-R X 2005 o - S 22 0 0 S
-R X 2008 o - Mar 21 0 1 D
-R X 2008 o - S 21 0 0 S
-R X 2009 2011 - Mar 22 0 1 D
-R X 2009 2011 - S 22 0 0 S
-R X 2012 o - Mar 21 0 1 D
-R X 2012 o - S 21 0 0 S
-R X 2013 2015 - Mar 22 0 1 D
-R X 2013 2015 - S 22 0 0 S
-R X 2016 o - Mar 21 0 1 D
-R X 2016 o - S 21 0 0 S
-R X 2017 2019 - Mar 22 0 1 D
-R X 2017 2019 - S 22 0 0 S
-R X 2020 o - Mar 21 0 1 D
-R X 2020 o - S 21 0 0 S
-R X 2021 2023 - Mar 22 0 1 D
-R X 2021 2023 - S 22 0 0 S
-R X 2024 o - Mar 21 0 1 D
-R X 2024 o - S 21 0 0 S
-R X 2025 2027 - Mar 22 0 1 D
-R X 2025 2027 - S 22 0 0 S
-R X 2028 2029 - Mar 21 0 1 D
-R X 2028 2029 - S 21 0 0 S
-R X 2030 2031 - Mar 22 0 1 D
-R X 2030 2031 - S 22 0 0 S
-R X 2032 2033 - Mar 21 0 1 D
-R X 2032 2033 - S 21 0 0 S
-R X 2034 2035 - Mar 22 0 1 D
-R X 2034 2035 - S 22 0 0 S
-R X 2036 ma - Mar 21 0 1 D
-R X 2036 ma - S 21 0 0 S
+R X 1978 1980 - Mar 21 0 1 -
+R X 1978 o - O 21 0 0 -
+R X 1979 o - S 19 0 0 -
+R X 1980 o - S 23 0 0 -
+R X 1991 o - May 3 0 1 -
+R X 1992 1995 - Mar 22 0 1 -
+R X 1991 1995 - S 22 0 0 -
+R X 1996 o - Mar 21 0 1 -
+R X 1996 o - S 21 0 0 -
+R X 1997 1999 - Mar 22 0 1 -
+R X 1997 1999 - S 22 0 0 -
+R X 2000 o - Mar 21 0 1 -
+R X 2000 o - S 21 0 0 -
+R X 2001 2003 - Mar 22 0 1 -
+R X 2001 2003 - S 22 0 0 -
+R X 2004 o - Mar 21 0 1 -
+R X 2004 o - S 21 0 0 -
+R X 2005 o - Mar 22 0 1 -
+R X 2005 o - S 22 0 0 -
+R X 2008 o - Mar 21 0 1 -
+R X 2008 o - S 21 0 0 -
+R X 2009 2011 - Mar 22 0 1 -
+R X 2009 2011 - S 22 0 0 -
+R X 2012 o - Mar 21 0 1 -
+R X 2012 o - S 21 0 0 -
+R X 2013 2015 - Mar 22 0 1 -
+R X 2013 2015 - S 22 0 0 -
+R X 2016 o - Mar 21 0 1 -
+R X 2016 o - S 21 0 0 -
+R X 2017 2019 - Mar 22 0 1 -
+R X 2017 2019 - S 22 0 0 -
+R X 2020 o - Mar 21 0 1 -
+R X 2020 o - S 21 0 0 -
+R X 2021 2023 - Mar 22 0 1 -
+R X 2021 2023 - S 22 0 0 -
+R X 2024 o - Mar 21 0 1 -
+R X 2024 o - S 21 0 0 -
+R X 2025 2027 - Mar 22 0 1 -
+R X 2025 2027 - S 22 0 0 -
+R X 2028 2029 - Mar 21 0 1 -
+R X 2028 2029 - S 21 0 0 -
+R X 2030 2031 - Mar 22 0 1 -
+R X 2030 2031 - S 22 0 0 -
+R X 2032 2033 - Mar 21 0 1 -
+R X 2032 2033 - S 21 0 0 -
+R X 2034 2035 - Mar 22 0 1 -
+R X 2034 2035 - S 22 0 0 -
+R X 2036 ma - Mar 21 0 1 -
+R X 2036 ma - S 21 0 0 -
 Z Asia/Tehran 3:25:44 - LMT 1916
 3:25:44 - TMT 1946
 3:30 - +0330 1977 N
 4 X +04/+05 1979
 3:30 X +0330/+0430
-R Y 1982 o - May 1 0 1 D
-R Y 1982 1984 - O 1 0 0 S
-R Y 1983 o - Mar 31 0 1 D
-R Y 1984 1985 - Ap 1 0 1 D
-R Y 1985 1990 - S lastSun 1s 0 S
-R Y 1986 1990 - Mar lastSun 1s 1 D
-R Y 1991 2007 - Ap 1 3s 1 D
-R Y 1991 2007 - O 1 3s 0 S
+R Y 1982 o - May 1 0 1 -
+R Y 1982 1984 - O 1 0 0 -
+R Y 1983 o - Mar 31 0 1 -
+R Y 1984 1985 - Ap 1 0 1 -
+R Y 1985 1990 - S lastSun 1s 0 -
+R Y 1986 1990 - Mar lastSun 1s 1 -
+R Y 1991 2007 - Ap 1 3s 1 -
+R Y 1991 2007 - O 1 3s 0 -
 Z Asia/Baghdad 2:57:40 - LMT 1890
 2:57:36 - BMT 1918
 3 - +03 1982 May
@@ -753,9 +754,9 @@ Z Asia/Oral 3:25:24 - LMT 1924 May 2
 5 M +05/+06 1992 Mar 29 2s
 4 M +04/+05 2004 O 31 2s
 5 - +05
-R c 1992 1996 - Ap Sun>=7 0s 1 S
+R c 1992 1996 - Ap Sun>=7 0s 1 -
 R c 1992 1996 - S lastSun 0 0 -
-R c 1997 2005 - Mar lastSun 2:30 1 S
+R c 1997 2005 - Mar lastSun 2:30 1 -
 R c 1997 2004 - O lastSun 2:30 0 -
 Z Asia/Bishkek 4:58:24 - LMT 1924 May 2
 5 - +05 1930 Jun 21
@@ -814,7 +815,7 @@ R e 1993 1998 - S lastSun 0 0 -
 R e 1999 ma - O lastSun 0 0 -
 Z Asia/Beirut 2:22 - LMT 1880
 2 e EE%sT
-R f 1935 1941 - S 14 0 0:20 TS
+R f 1935 1941 - S 14 0 0:20 -
 R f 1935 1941 - D 14 0 0 -
 Z Asia/Kuala_Lumpur 6:46:46 - LMT 1901
 6:55:25 - SMT 1905 Jun
@@ -833,14 +834,14 @@ Z Asia/Kuching 7:21:20 - LMT 1926 Mar
 Z Indian/Maldives 4:54 - LMT 1880
 4:54 - MMT 1960
 5 - +05
-R g 1983 1984 - Ap 1 0 1 S
+R g 1983 1984 - Ap 1 0 1 -
 R g 1983 o - O 1 0 0 -
-R g 1985 1998 - Mar lastSun 0 1 S
+R g 1985 1998 - Mar lastSun 0 1 -
 R g 1984 1998 - S lastSun 0 0 -
-R g 2001 o - Ap lastSat 2 1 S
+R g 2001 o - Ap lastSat 2 1 -
 R g 2001 2006 - S lastSat 2 0 -
-R g 2002 2006 - Mar lastSat 2 1 S
-R g 2015 2016 - Mar lastSat 2 1 S
+R g 2002 2006 - Mar lastSat 2 1 -
+R g 2015 2016 - Mar lastSat 2 1 -
 R g 2015 2016 - S lastSat 0 0 -
 Z Asia/Hovd 6:6:36 - LMT 1905 Au
 6 - +06 1978
@@ -888,7 +889,7 @@ R i 2012 o - S 21 1 0 -
 R i 2013 o - S F>=21 0 0 -
 R i 2014 2015 - O F>=21 0 0 -
 R i 2015 o - Mar lastF 24 1 S
-R i 2016 ma - Mar lastSat 1 1 S
+R i 2016 ma - Mar Sat>=22 1 1 S
 R i 2016 ma - O lastSat 1 0 -
 Z Asia/Gaza 2:17:52 - LMT 1900 O
 2 Z EET/EEST 1948 May 15
@@ -908,11 +909,11 @@ Z Asia/Hebron 2:20:23 - LMT 1900 O
 2 Z I%sT 1996
 2 b EE%sT 1999
 2 i EE%sT
-R j 1936 o - N 1 0 1 S
+R j 1936 o - N 1 0 1 -
 R j 1937 o - F 1 0 0 -
-R j 1954 o - Ap 12 0 1 S
+R j 1954 o - Ap 12 0 1 -
 R j 1954 o - Jul 1 0 0 -
-R j 1978 o - Mar 22 0 1 S
+R j 1978 o - Mar 22 0 1 -
 R j 1978 o - S 21 0 0 -
 Z Asia/Manila -15:56 - LMT 1844 D 31
 8:4 - LMT 1899 May 11
@@ -1156,20 +1157,20 @@ Z Australia/Broken_Hill 9:25:48 - LMT 1895 F
 9:30 l AC%sT 1971
 9:30 s AC%sT 2000
 9:30 p AC%sT
-R t 1981 1984 - O lastSun 2 1 D
-R t 1982 1985 - Mar Sun>=1 2 0 S
-R t 1985 o - O lastSun 2 0:30 D
-R t 1986 1989 - Mar Sun>=15 2 0 S
-R t 1986 o - O 19 2 0:30 D
-R t 1987 1999 - O lastSun 2 0:30 D
-R t 1990 1995 - Mar Sun>=1 2 0 S
-R t 1996 2005 - Mar lastSun 2 0 S
-R t 2000 o - Au lastSun 2 0:30 D
-R t 2001 2007 - O lastSun 2 0:30 D
-R t 2006 o - Ap Sun>=1 2 0 S
-R t 2007 o - Mar lastSun 2 0 S
-R t 2008 ma - Ap Sun>=1 2 0 S
-R t 2008 ma - O Sun>=1 2 0:30 D
+R t 1981 1984 - O lastSun 2 1 -
+R t 1982 1985 - Mar Sun>=1 2 0 -
+R t 1985 o - O lastSun 2 0:30 -
+R t 1986 1989 - Mar Sun>=15 2 0 -
+R t 1986 o - O 19 2 0:30 -
+R t 1987 1999 - O lastSun 2 0:30 -
+R t 1990 1995 - Mar Sun>=1 2 0 -
+R t 1996 2005 - Mar lastSun 2 0 -
+R t 2000 o - Au lastSun 2 0:30 -
+R t 2001 2007 - O lastSun 2 0:30 -
+R t 2006 o - Ap Sun>=1 2 0 -
+R t 2007 o - Mar lastSun 2 0 -
+R t 2008 ma - Ap Sun>=1 2 0 -
+R t 2008 ma - O Sun>=1 2 0:30 -
 Z Australia/Lord_Howe 10:36:20 - LMT 1895 F
 10 - AEST 1981 Mar
 10:30 t +1030/+1130 1985 Jul
@@ -1186,15 +1187,15 @@ Z Indian/Christmas 7:2:52 - LMT 1895 F
 7 - +07
 Z Indian/Cocos 6:27:40 - LMT 1900
 6:30 - +0630
-R u 1998 1999 - N Sun>=1 2 1 S
+R u 1998 1999 - N Sun>=1 2 1 -
 R u 1999 2000 - F lastSun 3 0 -
-R u 2009 o - N 29 2 1 S
+R u 2009 o - N 29 2 1 -
 R u 2010 o - Mar lastSun 3 0 -
-R u 2010 2013 - O Sun>=21 2 1 S
+R u 2010 2013 - O Sun>=21 2 1 -
 R u 2011 o - Mar Sun>=1 3 0 -
 R u 2012 2013 - Ja Sun>=18 3 0 -
 R u 2014 o - Ja Sun>=18 2 0 -
-R u 2014 ma - N Sun>=1 2 1 S
+R u 2014 ma - N Sun>=1 2 1 -
 R u 2015 ma - Ja Sun>=14 3 0 -
 Z Pacific/Fiji 11:55:44 - LMT 1915 O 26
 12 u +12/+13
@@ -1213,11 +1214,11 @@ Z Pacific/Tarawa 11:32:4 - LMT 1901
 12 - +12
 Z Pacific/Enderbury -11:24:20 - LMT 1901
 -12 - -12 1979 O
--11 - -11 1995
+-11 - -11 1994 D 31
 13 - +13
 Z Pacific/Kiritimati -10:29:20 - LMT 1901
 -10:40 - -1040 1979 O
--10 - -10 1995
+-10 - -10 1994 D 31
 14 - +14
 Z Pacific/Majuro 11:24:48 - LMT 1901
 11 - +11 1969 O
@@ -1239,9 +1240,9 @@ Z Pacific/Nauru 11:7:40 - LMT 1921 Ja 15
 9 - +09 1944 Au 15
 11:30 - +1130 1979 May
 12 - +12
-R v 1977 1978 - D Sun>=1 0 1 S
+R v 1977 1978 - D Sun>=1 0 1 -
 R v 1978 1979 - F 27 0 0 -
-R v 1996 o - D 1 2s 1 S
+R v 1996 o - D 1 2s 1 -
 R v 1997 o - Mar 2 2s 0 -
 Z Pacific/Noumea 11:5:48 - LMT 1912 Ja 13
 11 v +11/+12
@@ -1253,23 +1254,23 @@ R w 1934 1940 - Ap lastSun 2 0 M
 R w 1934 1940 - S lastSun 2 0:30 S
 R w 1946 o - Ja 1 0 0 S
 R w 1974 o - N Sun>=1 2s 1 D
-R x 1974 o - N Sun>=1 2:45s 1 D
+R x 1974 o - N Sun>=1 2:45s 1 -
 R w 1975 o - F lastSun 2s 0 S
-R x 1975 o - F lastSun 2:45s 0 S
+R x 1975 o - F lastSun 2:45s 0 -
 R w 1975 1988 - O lastSun 2s 1 D
-R x 1975 1988 - O lastSun 2:45s 1 D
+R x 1975 1988 - O lastSun 2:45s 1 -
 R w 1976 1989 - Mar Sun>=1 2s 0 S
-R x 1976 1989 - Mar Sun>=1 2:45s 0 S
+R x 1976 1989 - Mar Sun>=1 2:45s 0 -
 R w 1989 o - O Sun>=8 2s 1 D
-R x 1989 o - O Sun>=8 2:45s 1 D
+R x 1989 o - O Sun>=8 2:45s 1 -
 R w 1990 2006 - O Sun>=1 2s 1 D
-R x 1990 2006 - O Sun>=1 2:45s 1 D
+R x 1990 2006 - O Sun>=1 2:45s 1 -
 R w 1990 2007 - Mar Sun>=15 2s 0 S
-R x 1990 2007 - Mar Sun>=15 2:45s 0 S
+R x 1990 2007 - Mar Sun>=15 2:45s 0 -
 R w 2007 ma - S lastSun 2s 1 D
-R x 2007 ma - S lastSun 2:45s 1 D
+R x 2007 ma - S lastSun 2:45s 1 -
 R w 2008 ma - Ap Sun>=1 2s 0 S
-R x 2008 ma - Ap Sun>=1 2:45s 0 S
+R x 2008 ma - Ap Sun>=1 2:45s 0 -
 Z Pacific/Auckland 11:39:4 - LMT 1868 N 2
 11:30 w NZ%sT 1946
 12 w NZ%sT
@@ -1277,9 +1278,9 @@ Z Pacific/Chatham 12:13:48 - LMT 1868 N 2
 12:15 - +1215 1946
 12:45 x +1245/+1345
 Li Pacific/Auckland Antarctica/McMurdo
-R y 1978 o - N 12 0 0:30 HS
+R y 1978 o - N 12 0 0:30 -
 R y 1979 1991 - Mar Sun>=1 0 0 -
-R y 1979 1990 - O lastSun 0 0:30 HS
+R y 1979 1990 - O lastSun 0 0:30 -
 Z Pacific/Rarotonga -10:39:4 - LMT 1901
 -10:30 - -1030 1978 N 12
 -10 y -10/-0930
@@ -1311,11 +1312,11 @@ Z Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5
 -11:22:48 - LMT 1911
 -11 - SST
 Li Pacific/Pago_Pago Pacific/Midway
-R z 2010 o - S lastSun 0 1 D
-R z 2011 o - Ap Sat>=1 4 0 S
-R z 2011 o - S lastSat 3 1 D
-R z 2012 ma - Ap Sun>=1 4 0 S
-R z 2012 ma - S lastSun 3 1 D
+R z 2010 o - S lastSun 0 1 -
+R z 2011 o - Ap Sat>=1 4 0 -
+R z 2011 o - S lastSat 3 1 -
+R z 2012 ma - Ap Sun>=1 4 0 -
+R z 2012 ma - S lastSun 3 1 -
 Z Pacific/Apia 12:33:4 - LMT 1892 Jul 5
 -11:26:56 - LMT 1911
 -11:30 - -1130 1950
@@ -1326,11 +1327,11 @@ Z Pacific/Guadalcanal 10:39:48 - LMT 1912 O
 Z Pacific/Fakaofo -11:24:56 - LMT 1901
 -11 - -11 2011 D 30
 13 - +13
-R ! 1999 o - O 7 2s 1 S
+R ! 1999 o - O 7 2s 1 -
 R ! 2000 o - Mar 19 2s 0 -
-R ! 2000 2001 - N Sun>=1 2 1 S
+R ! 2000 2001 - N Sun>=1 2 1 -
 R ! 2001 2002 - Ja lastSun 2 0 -
-R ! 2016 o - N Sun>=1 2 1 S
+R ! 2016 o - N Sun>=1 2 1 -
 R ! 2017 o - Ja Sun>=15 3 0 -
 Z Pacific/Tongatapu 12:19:20 - LMT 1901
 12:20 - +1220 1941
@@ -1340,12 +1341,12 @@ Z Pacific/Funafuti 11:56:52 - LMT 1901
 12 - +12
 Z Pacific/Wake 11:6:28 - LMT 1901
 12 - +12
-R $ 1983 o - S 25 0 1 S
+R $ 1983 o - S 25 0 1 -
 R $ 1984 1991 - Mar Sun>=23 0 0 -
-R $ 1984 o - O 23 0 1 S
-R $ 1985 1991 - S Sun>=23 0 1 S
+R $ 1984 o - O 23 0 1 -
+R $ 1985 1991 - S Sun>=23 0 1 -
 R $ 1992 1993 - Ja Sun>=23 0 0 -
-R $ 1992 o - O Sun>=23 0 1 S
+R $ 1992 o - O Sun>=23 0 1 -
 Z Pacific/Efate 11:13:16 - LMT 1912 Ja 13
 11 $ +11/+12
 Z Pacific/Wallis 12:15:20 - LMT 1901
@@ -1799,19 +1800,19 @@ Z Europe/Budapest 1:16:20 - LMT 1890 O
 1 ' CE%sT 1945
 1 ? CE%sT 1980 S 28 2s
 1 O CE%sT
-R @ 1917 1919 - F 19 23 1 S
+R @ 1917 1919 - F 19 23 1 -
 R @ 1917 o - O 21 1 0 -
 R @ 1918 1919 - N 16 1 0 -
-R @ 1921 o - Mar 19 23 1 S
+R @ 1921 o - Mar 19 23 1 -
 R @ 1921 o - Jun 23 1 0 -
-R @ 1939 o - Ap 29 23 1 S
+R @ 1939 o - Ap 29 23 1 -
 R @ 1939 o - O 29 2 0 -
-R @ 1940 o - F 25 2 1 S
+R @ 1940 o - F 25 2 1 -
 R @ 1940 1941 - N Sun>=2 1s 0 -
-R @ 1941 1942 - Mar Sun>=2 1s 1 S
-R @ 1943 1946 - Mar Sun>=1 1s 1 S
+R @ 1941 1942 - Mar Sun>=2 1s 1 -
+R @ 1943 1946 - Mar Sun>=1 1s 1 -
 R @ 1942 1948 - O Sun>=22 1s 0 -
-R @ 1947 1967 - Ap Sun>=1 1s 1 S
+R @ 1947 1967 - Ap Sun>=1 1s 1 -
 R @ 1949 o - O 30 1s 0 -
 R @ 1950 1966 - O Sun>=22 1s 0 -
 R @ 1967 o - O 29 1s 0 -
@@ -2077,7 +2078,7 @@ R ~ 1980 o - Mar lastSun 0s 1 S
 R ~ 1981 1982 - Mar lastSun 1s 1 S
 R ~ 1983 o - Mar lastSun 2s 1 S
 Z Europe/Lisbon -0:36:45 - LMT 1884
--0:36:45 - LMT 1912
+-0:36:45 - LMT 1912 Ja 1 0u
 0 ~ WE%sT 1966 Ap 3 2
 1 - CET 1976 S 26 1
 0 ~ WE%sT 1983 S 25 1s
@@ -2085,7 +2086,7 @@ Z Europe/Lisbon -0:36:45 - LMT 1884
 1 O CE%sT 1996 Mar 31 1u
 0 O WE%sT
 Z Atlantic/Azores -1:42:40 - LMT 1884
--1:54:32 - HMT 1912
+-1:54:32 - HMT 1912 Ja 1 2u
 -2 ~ -02/-01 1942 Ap 25 22s
 -2 ~ +00 1942 Au 15 22s
 -2 ~ -02/-01 1943 Ap 17 22s
@@ -2100,7 +2101,7 @@ Z Atlantic/Azores -1:42:40 - LMT 1884
 0 O WE%sT 1993 Mar 28 1u
 -1 O -01/+00
 Z Atlantic/Madeira -1:7:36 - LMT 1884
--1:7:36 - FMT 1912
+-1:7:36 - FMT 1912 Ja 1 1u
 -1 ~ -01/+00 1942 Ap 25 22s
 -1 ~ +01 1942 Au 15 22s
 -1 ~ -01/+00 1943 Ap 17 22s
@@ -3395,8 +3396,8 @@ R Ao 2006 o - May Sun>=1 0 1 D
 R Ao 2006 o - Au M>=1 0 0 S
 Z America/Tegucigalpa -5:48:52 - LMT 1921 Ap
 -6 Ao C%sT
-Z America/Jamaica -5:7:11 - LMT 1890
--5:7:11 - KMT 1912 F
+Z America/Jamaica -5:7:10 - LMT 1890
+-5:7:10 - KMT 1912 F
 -5 - EST 1974
 -5 AE E%sT 1984
 -5 - EST
@@ -3433,40 +3434,40 @@ Z America/Miquelon -3:44:40 - LMT 1911 May 15
 -3 - -03 1987
 -3 AT -03/-02
 Z America/Grand_Turk -4:44:32 - LMT 1890
--5:7:11 - KMT 1912 F
+-5:7:10 - KMT 1912 F
 -5 - EST 1979
 -5 AE E%sT 2015 N Sun>=1 2
 -4 - AST 2018 Mar 11 3
 -5 AE E%sT
-R Aq 1930 o - D 1 0 1 S
+R Aq 1930 o - D 1 0 1 -
 R Aq 1931 o - Ap 1 0 0 -
-R Aq 1931 o - O 15 0 1 S
+R Aq 1931 o - O 15 0 1 -
 R Aq 1932 1940 - Mar 1 0 0 -
-R Aq 1932 1939 - N 1 0 1 S
-R Aq 1940 o - Jul 1 0 1 S
+R Aq 1932 1939 - N 1 0 1 -
+R Aq 1940 o - Jul 1 0 1 -
 R Aq 1941 o - Jun 15 0 0 -
-R Aq 1941 o - O 15 0 1 S
+R Aq 1941 o - O 15 0 1 -
 R Aq 1943 o - Au 1 0 0 -
-R Aq 1943 o - O 15 0 1 S
+R Aq 1943 o - O 15 0 1 -
 R Aq 1946 o - Mar 1 0 0 -
-R Aq 1946 o - O 1 0 1 S
+R Aq 1946 o - O 1 0 1 -
 R Aq 1963 o - O 1 0 0 -
-R Aq 1963 o - D 15 0 1 S
+R Aq 1963 o - D 15 0 1 -
 R Aq 1964 1966 - Mar 1 0 0 -
-R Aq 1964 1966 - O 15 0 1 S
+R Aq 1964 1966 - O 15 0 1 -
 R Aq 1967 o - Ap 2 0 0 -
-R Aq 1967 1968 - O Sun>=1 0 1 S
+R Aq 1967 1968 - O Sun>=1 0 1 -
 R Aq 1968 1969 - Ap Sun>=1 0 0 -
-R Aq 1974 o - Ja 23 0 1 S
+R Aq 1974 o - Ja 23 0 1 -
 R Aq 1974 o - May 1 0 0 -
-R Aq 1988 o - D 1 0 1 S
+R Aq 1988 o - D 1 0 1 -
 R Aq 1989 1993 - Mar Sun>=1 0 0 -
-R Aq 1989 1992 - O Sun>=15 0 1 S
-R Aq 1999 o - O Sun>=1 0 1 S
+R Aq 1989 1992 - O Sun>=15 0 1 -
+R Aq 1999 o - O Sun>=1 0 1 -
 R Aq 2000 o - Mar 3 0 0 -
-R Aq 2007 o - D 30 0 1 S
+R Aq 2007 o - D 30 0 1 -
 R Aq 2008 2009 - Mar Sun>=15 0 0 -
-R Aq 2008 o - O Sun>=15 0 1 S
+R Aq 2008 o - O Sun>=15 0 1 -
 Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
 -4:16:48 - CMT 1920 May
 -4 - -04 1930 D
@@ -3570,7 +3571,7 @@ Z America/Argentina/Mendoza -4:35:16 - LMT 1894 O 31
 -3 Aq -03/-02 2008 O 18
 -3 - -03
 R Ar 2008 2009 - Mar Sun>=8 0 0 -
-R Ar 2007 2008 - O Sun>=8 0 1 S
+R Ar 2007 2008 - O Sun>=8 0 1 -
 Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31
 -4:16:48 - CMT 1920 May
 -4 - -04 1930 D
@@ -3612,63 +3613,63 @@ Z America/La_Paz -4:32:36 - LMT 1890
 -4:32:36 - CMT 1931 O 15
 -4:32:36 1 BST 1932 Mar 21
 -4 - -04
-R As 1931 o - O 3 11 1 S
+R As 1931 o - O 3 11 1 -
 R As 1932 1933 - Ap 1 0 0 -
-R As 1932 o - O 3 0 1 S
-R As 1949 1952 - D 1 0 1 S
+R As 1932 o - O 3 0 1 -
+R As 1949 1952 - D 1 0 1 -
 R As 1950 o - Ap 16 1 0 -
 R As 1951 1952 - Ap 1 0 0 -
 R As 1953 o - Mar 1 0 0 -
-R As 1963 o - D 9 0 1 S
+R As 1963 o - D 9 0 1 -
 R As 1964 o - Mar 1 0 0 -
-R As 1965 o - Ja 31 0 1 S
+R As 1965 o - Ja 31 0 1 -
 R As 1965 o - Mar 31 0 0 -
-R As 1965 o - D 1 0 1 S
+R As 1965 o - D 1 0 1 -
 R As 1966 1968 - Mar 1 0 0 -
-R As 1966 1967 - N 1 0 1 S
-R As 1985 o - N 2 0 1 S
+R As 1966 1967 - N 1 0 1 -
+R As 1985 o - N 2 0 1 -
 R As 1986 o - Mar 15 0 0 -
-R As 1986 o - O 25 0 1 S
+R As 1986 o - O 25 0 1 -
 R As 1987 o - F 14 0 0 -
-R As 1987 o - O 25 0 1 S
+R As 1987 o - O 25 0 1 -
 R As 1988 o - F 7 0 0 -
-R As 1988 o - O 16 0 1 S
+R As 1988 o - O 16 0 1 -
 R As 1989 o - Ja 29 0 0 -
-R As 1989 o - O 15 0 1 S
+R As 1989 o - O 15 0 1 -
 R As 1990 o - F 11 0 0 -
-R As 1990 o - O 21 0 1 S
+R As 1990 o - O 21 0 1 -
 R As 1991 o - F 17 0 0 -
-R As 1991 o - O 20 0 1 S
+R As 1991 o - O 20 0 1 -
 R As 1992 o - F 9 0 0 -
-R As 1992 o - O 25 0 1 S
+R As 1992 o - O 25 0 1 -
 R As 1993 o - Ja 31 0 0 -
-R As 1993 1995 - O Sun>=11 0 1 S
+R As 1993 1995 - O Sun>=11 0 1 -
 R As 1994 1995 - F Sun>=15 0 0 -
 R As 1996 o - F 11 0 0 -
-R As 1996 o - O 6 0 1 S
+R As 1996 o - O 6 0 1 -
 R As 1997 o - F 16 0 0 -
-R As 1997 o - O 6 0 1 S
+R As 1997 o - O 6 0 1 -
 R As 1998 o - Mar 1 0 0 -
-R As 1998 o - O 11 0 1 S
+R As 1998 o - O 11 0 1 -
 R As 1999 o - F 21 0 0 -
-R As 1999 o - O 3 0 1 S
+R As 1999 o - O 3 0 1 -
 R As 2000 o - F 27 0 0 -
-R As 2000 2001 - O Sun>=8 0 1 S
+R As 2000 2001 - O Sun>=8 0 1 -
 R As 2001 2006 - F Sun>=15 0 0 -
-R As 2002 o - N 3 0 1 S
-R As 2003 o - O 19 0 1 S
-R As 2004 o - N 2 0 1 S
-R As 2005 o - O 16 0 1 S
-R As 2006 o - N 5 0 1 S
+R As 2002 o - N 3 0 1 -
+R As 2003 o - O 19 0 1 -
+R As 2004 o - N 2 0 1 -
+R As 2005 o - O 16 0 1 -
+R As 2006 o - N 5 0 1 -
 R As 2007 o - F 25 0 0 -
-R As 2007 o - O Sun>=8 0 1 S
-R As 2008 2017 - O Sun>=15 0 1 S
+R As 2007 o - O Sun>=8 0 1 -
+R As 2008 2017 - O Sun>=15 0 1 -
 R As 2008 2011 - F Sun>=15 0 0 -
 R As 2012 o - F Sun>=22 0 0 -
 R As 2013 2014 - F Sun>=15 0 0 -
 R As 2015 o - F Sun>=22 0 0 -
 R As 2016 2022 - F Sun>=15 0 0 -
-R As 2018 ma - N Sun>=1 0 1 S
+R As 2018 ma - N Sun>=1 0 1 -
 R As 2023 o - F Sun>=22 0 0 -
 R As 2024 2025 - F Sun>=15 0 0 -
 R As 2026 o - F Sun>=22 0 0 -
@@ -3761,38 +3762,38 @@ Z America/Rio_Branco -4:31:12 - LMT 1914
 -5 - -05 2008 Jun 24
 -4 - -04 2013 N 10
 -5 - -05
-R At 1927 1931 - S 1 0 1 S
+R At 1927 1931 - S 1 0 1 -
 R At 1928 1932 - Ap 1 0 0 -
-R At 1968 o - N 3 4u 1 S
+R At 1968 o - N 3 4u 1 -
 R At 1969 o - Mar 30 3u 0 -
-R At 1969 o - N 23 4u 1 S
+R At 1969 o - N 23 4u 1 -
 R At 1970 o - Mar 29 3u 0 -
 R At 1971 o - Mar 14 3u 0 -
-R At 1970 1972 - O Sun>=9 4u 1 S
+R At 1970 1972 - O Sun>=9 4u 1 -
 R At 1972 1986 - Mar Sun>=9 3u 0 -
-R At 1973 o - S 30 4u 1 S
-R At 1974 1987 - O Sun>=9 4u 1 S
+R At 1973 o - S 30 4u 1 -
+R At 1974 1987 - O Sun>=9 4u 1 -
 R At 1987 o - Ap 12 3u 0 -
 R At 1988 1990 - Mar Sun>=9 3u 0 -
-R At 1988 1989 - O Sun>=9 4u 1 S
-R At 1990 o - S 16 4u 1 S
+R At 1988 1989 - O Sun>=9 4u 1 -
+R At 1990 o - S 16 4u 1 -
 R At 1991 1996 - Mar Sun>=9 3u 0 -
-R At 1991 1997 - O Sun>=9 4u 1 S
+R At 1991 1997 - O Sun>=9 4u 1 -
 R At 1997 o - Mar 30 3u 0 -
 R At 1998 o - Mar Sun>=9 3u 0 -
-R At 1998 o - S 27 4u 1 S
+R At 1998 o - S 27 4u 1 -
 R At 1999 o - Ap 4 3u 0 -
-R At 1999 2010 - O Sun>=9 4u 1 S
+R At 1999 2010 - O Sun>=9 4u 1 -
 R At 2000 2007 - Mar Sun>=9 3u 0 -
 R At 2008 o - Mar 30 3u 0 -
 R At 2009 o - Mar Sun>=9 3u 0 -
 R At 2010 o - Ap Sun>=1 3u 0 -
 R At 2011 o - May Sun>=2 3u 0 -
-R At 2011 o - Au Sun>=16 4u 1 S
+R At 2011 o - Au Sun>=16 4u 1 -
 R At 2012 2014 - Ap Sun>=23 3u 0 -
-R At 2012 2014 - S Sun>=2 4u 1 S
+R At 2012 2014 - S Sun>=2 4u 1 -
 R At 2016 ma - May Sun>=9 3u 0 -
-R At 2016 ma - Au Sun>=9 4u 1 S
+R At 2016 ma - Au Sun>=9 4u 1 -
 Z America/Santiago -4:42:46 - LMT 1890
 -4:42:46 - SMT 1910 Ja 10
 -5 - -05 1916 Jul
@@ -3829,7 +3830,7 @@ Z Antarctica/Palmer 0 - -00 1965
 -3 Aq -03/-02 1982 May
 -4 At -04/-03 2016 D 4
 -3 - -03
-R Au 1992 o - May 3 0 1 S
+R Au 1992 o - May 3 0 1 -
 R Au 1993 o - Ap 4 0 0 -
 Z America/Bogota -4:56:16 - LMT 1884 Mar 13
 -4:56:16 - BMT 1914 N 23
@@ -3839,7 +3840,7 @@ Z America/Curacao -4:35:47 - LMT 1912 F 12
 -4 - AST
 Li America/Curacao America/Lower_Princes
 Li America/Curacao America/Kralendijk
-R Av 1992 o - N 28 0 1 S
+R Av 1992 o - N 28 0 1 -
 R Av 1993 o - F 5 0 0 -
 Z America/Guayaquil -5:19:20 - LMT 1890
 -5:14 - QMT 1931
@@ -3847,18 +3848,18 @@ Z America/Guayaquil -5:19:20 - LMT 1890
 Z Pacific/Galapagos -5:58:24 - LMT 1931
 -5 - -05 1986
 -6 Av -06/-05
-R Aw 1937 1938 - S lastSun 0 1 S
+R Aw 1937 1938 - S lastSun 0 1 -
 R Aw 1938 1942 - Mar Sun>=19 0 0 -
-R Aw 1939 o - O 1 0 1 S
-R Aw 1940 1942 - S lastSun 0 1 S
+R Aw 1939 o - O 1 0 1 -
+R Aw 1940 1942 - S lastSun 0 1 -
 R Aw 1943 o - Ja 1 0 0 -
-R Aw 1983 o - S lastSun 0 1 S
+R Aw 1983 o - S lastSun 0 1 -
 R Aw 1984 1985 - Ap lastSun 0 0 -
-R Aw 1984 o - S 16 0 1 S
-R Aw 1985 2000 - S Sun>=9 0 1 S
+R Aw 1984 o - S 16 0 1 -
+R Aw 1985 2000 - S Sun>=9 0 1 -
 R Aw 1986 2000 - Ap Sun>=16 0 0 -
 R Aw 2001 2010 - Ap Sun>=15 2 0 -
-R Aw 2001 2010 - S Sun>=1 2 1 S
+R Aw 2001 2010 - S Sun>=1 2 1 -
 Z Atlantic/Stanley -3:51:24 - LMT 1890
 -3:51:24 - SMT 1912 Mar 12
 -4 Aw -04/-03 1983 May
@@ -3872,26 +3873,26 @@ Z America/Guyana -3:52:40 - LMT 1915 Mar
 -3:45 - -0345 1975 Jul 31
 -3 - -03 1991
 -4 - -04
-R Ax 1975 1988 - O 1 0 1 S
+R Ax 1975 1988 - O 1 0 1 -
 R Ax 1975 1978 - Mar 1 0 0 -
 R Ax 1979 1991 - Ap 1 0 0 -
-R Ax 1989 o - O 22 0 1 S
-R Ax 1990 o - O 1 0 1 S
-R Ax 1991 o - O 6 0 1 S
+R Ax 1989 o - O 22 0 1 -
+R Ax 1990 o - O 1 0 1 -
+R Ax 1991 o - O 6 0 1 -
 R Ax 1992 o - Mar 1 0 0 -
-R Ax 1992 o - O 5 0 1 S
+R Ax 1992 o - O 5 0 1 -
 R Ax 1993 o - Mar 31 0 0 -
-R Ax 1993 1995 - O 1 0 1 S
+R Ax 1993 1995 - O 1 0 1 -
 R Ax 1994 1995 - F lastSun 0 0 -
 R Ax 1996 o - Mar 1 0 0 -
-R Ax 1996 2001 - O Sun>=1 0 1 S
+R Ax 1996 2001 - O Sun>=1 0 1 -
 R Ax 1997 o - F lastSun 0 0 -
 R Ax 1998 2001 - Mar Sun>=1 0 0 -
 R Ax 2002 2004 - Ap Sun>=1 0 0 -
-R Ax 2002 2003 - S Sun>=1 0 1 S
-R Ax 2004 2009 - O Sun>=15 0 1 S
+R Ax 2002 2003 - S Sun>=1 0 1 -
+R Ax 2004 2009 - O Sun>=15 0 1 -
 R Ax 2005 2009 - Mar Sun>=8 0 0 -
-R Ax 2010 ma - O Sun>=1 0 1 S
+R Ax 2010 ma - O Sun>=1 0 1 -
 R Ax 2010 2012 - Ap Sun>=8 0 0 -
 R Ax 2013 ma - Mar Sun>=22 0 0 -
 Z America/Asuncion -3:50:40 - LMT 1890
@@ -3899,15 +3900,15 @@ Z America/Asuncion -3:50:40 - LMT 1890
 -4 - -04 1972 O
 -3 - -03 1974 Ap
 -4 Ax -04/-03
-R Ay 1938 o - Ja 1 0 1 S
+R Ay 1938 o - Ja 1 0 1 -
 R Ay 1938 o - Ap 1 0 0 -
-R Ay 1938 1939 - S lastSun 0 1 S
+R Ay 1938 1939 - S lastSun 0 1 -
 R Ay 1939 1940 - Mar Sun>=24 0 0 -
-R Ay 1986 1987 - Ja 1 0 1 S
+R Ay 1986 1987 - Ja 1 0 1 -
 R Ay 1986 1987 - Ap 1 0 0 -
-R Ay 1990 o - Ja 1 0 1 S
+R Ay 1990 o - Ja 1 0 1 -
 R Ay 1990 o - Ap 1 0 0 -
-R Ay 1994 o - Ja 1 0 1 S
+R Ay 1994 o - Ja 1 0 1 -
 R Ay 1994 o - Ap 1 0 0 -
 Z America/Lima -5:8:12 - LMT 1890
 -5:8:36 - LMT 1908 Jul 28
@@ -3934,57 +3935,63 @@ Li America/Port_of_Spain America/St_Lucia
 Li America/Port_of_Spain America/St_Thomas
 Li America/Port_of_Spain America/St_Vincent
 Li America/Port_of_Spain America/Tortola
-R Az 1923 o - O 2 0 0:30 HS
+R Az 1923 1925 - O 1 0 0:30 -
 R Az 1924 1926 - Ap 1 0 0 -
-R Az 1924 1925 - O 1 0 0:30 HS
-R Az 1933 1935 - O lastSun 0 0:30 HS
-R Az 1934 1936 - Mar Sat>=25 23:30s 0 -
-R Az 1936 o - N 1 0 0:30 HS
-R Az 1937 1941 - Mar lastSun 0 0 -
-R Az 1937 1940 - O lastSun 0 0:30 HS
-R Az 1941 o - Au 1 0 0:30 HS
-R Az 1942 o - Ja 1 0 0 -
-R Az 1942 o - D 14 0 1 S
+R Az 1933 1938 - O lastSun 0 0:30 -
+R Az 1934 1941 - Mar lastSat 24 0 -
+R Az 1939 o - O 1 0 0:30 -
+R Az 1940 o - O 27 0 0:30 -
+R Az 1941 o - Au 1 0 0:30 -
+R Az 1942 o - D 14 0 0:30 -
 R Az 1943 o - Mar 14 0 0 -
-R Az 1959 o - May 24 0 1 S
+R Az 1959 o - May 24 0 0:30 -
 R Az 1959 o - N 15 0 0 -
-R Az 1960 o - Ja 17 0 1 S
+R Az 1960 o - Ja 17 0 1 -
 R Az 1960 o - Mar 6 0 0 -
-R Az 1965 1967 - Ap Sun>=1 0 1 S
+R Az 1965 o - Ap 4 0 1 -
 R Az 1965 o - S 26 0 0 -
-R Az 1966 1967 - O 31 0 0 -
-R Az 1968 1970 - May 27 0 0:30 HS
-R Az 1968 1970 - D 2 0 0 -
-R Az 1972 o - Ap 24 0 1 S
-R Az 1972 o - Au 15 0 0 -
-R Az 1974 o - Mar 10 0 0:30 HS
-R Az 1974 o - D 22 0 1 S
-R Az 1976 o - O 1 0 0 -
-R Az 1977 o - D 4 0 1 S
-R Az 1978 o - Ap 1 0 0 -
-R Az 1979 o - O 1 0 1 S
-R Az 1980 o - May 1 0 0 -
-R Az 1987 o - D 14 0 1 S
-R Az 1988 o - Mar 14 0 0 -
-R Az 1988 o - D 11 0 1 S
-R Az 1989 o - Mar 12 0 0 -
-R Az 1989 o - O 29 0 1 S
-R Az 1990 1992 - Mar Sun>=1 0 0 -
-R Az 1990 1991 - O Sun>=21 0 1 S
-R Az 1992 o - O 18 0 1 S
+R Az 1968 o - May 27 0 0:30 -
+R Az 1968 o - D 1 0 0 -
+R Az 1970 o - Ap 25 0 1 -
+R Az 1970 o - Jun 14 0 0 -
+R Az 1972 o - Ap 23 0 1 -
+R Az 1972 o - Jul 16 0 0 -
+R Az 1974 o - Ja 13 0 1:30 -
+R Az 1974 o - Mar 10 0 0:30 -
+R Az 1974 o - S 1 0 0 -
+R Az 1974 o - D 22 0 1 -
+R Az 1975 o - Mar 30 0 0 -
+R Az 1976 o - D 19 0 1 -
+R Az 1977 o - Mar 6 0 0 -
+R Az 1977 o - D 4 0 1 -
+R Az 1978 1979 - Mar Sun>=1 0 0 -
+R Az 1978 o - D 17 0 1 -
+R Az 1979 o - Ap 29 0 1 -
+R Az 1980 o - Mar 16 0 0 -
+R Az 1987 o - D 14 0 1 -
+R Az 1988 o - F 28 0 0 -
+R Az 1988 o - D 11 0 1 -
+R Az 1989 o - Mar 5 0 0 -
+R Az 1989 o - O 29 0 1 -
+R Az 1990 o - F 25 0 0 -
+R Az 1990 1991 - O Sun>=21 0 1 -
+R Az 1991 1992 - Mar Sun>=1 0 0 -
+R Az 1992 o - O 18 0 1 -
 R Az 1993 o - F 28 0 0 -
-R Az 2004 o - S 19 0 1 S
+R Az 2004 o - S 19 0 1 -
 R Az 2005 o - Mar 27 2 0 -
-R Az 2005 o - O 9 2 1 S
-R Az 2006 o - Mar 12 2 0 -
-R Az 2006 2014 - O Sun>=1 2 1 S
-R Az 2007 2015 - Mar Sun>=8 2 0 -
-Z America/Montevideo -3:44:44 - LMT 1898 Jun 28
--3:44:44 - MMT 1920 May
+R Az 2005 o - O 9 2 1 -
+R Az 2006 2015 - Mar Sun>=8 2 0 -
+R Az 2006 2014 - O Sun>=1 2 1 -
+Z America/Montevideo -3:44:51 - LMT 1908 Jun 10
+-3:44:51 - MMT 1920 May
+-4 - -04 1923 O
 -3:30 Az -0330/-03 1942 D 14
+-3 Az -03/-0230 1960
 -3 Az -03/-02 1968
--3 Az -03/-0230 1971
+-3 Az -03/-0230 1970
 -3 Az -03/-02 1974
+-3 Az -03/-0130 1974 Mar 10
 -3 Az -03/-0230 1974 D 22
 -3 Az -03/-02
 Z America/Caracas -4:27:44 - LMT 1890
diff --git a/lib/pytz/zoneinfo/zone.tab b/lib/pytz/zoneinfo/zone.tab
index e1bfdee2ecae9904cf18659f40ba9dc71684e40e..f92c919b8a1e79ff60d4a5fbe47f3384b75a8b17 100644
--- a/lib/pytz/zoneinfo/zone.tab
+++ b/lib/pytz/zoneinfo/zone.tab
@@ -429,7 +429,7 @@ US	+593249-1394338	America/Yakutat	Alaska - Yakutat
 US	+643004-1652423	America/Nome	Alaska (west)
 US	+515248-1763929	America/Adak	Aleutian Islands
 US	+211825-1575130	Pacific/Honolulu	Hawaii
-UY	-3453-05611	America/Montevideo
+UY	-345433-0561245	America/Montevideo
 UZ	+3940+06648	Asia/Samarkand	Uzbekistan (west)
 UZ	+4120+06918	Asia/Tashkent	Uzbekistan (east)
 VA	+415408+0122711	Europe/Vatican
diff --git a/lib/pytz/zoneinfo/zone1970.tab b/lib/pytz/zoneinfo/zone1970.tab
index 4ee8ce519d9ab1fdab6f782e8df0bafff3472e8b..2d90ed72f1c647da6e97ac0f70315baa5c8e2b0b 100644
--- a/lib/pytz/zoneinfo/zone1970.tab
+++ b/lib/pytz/zoneinfo/zone1970.tab
@@ -12,7 +12,7 @@
 #     of ISO 3166 2-character country codes.  See the file 'iso3166.tab'.
 # 2.  Latitude and longitude of the zone's principal location
 #     in ISO 6709 sign-degrees-minutes-seconds format,
-#     either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS,
+#     either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS,
 #     first latitude (+ is north), then longitude (+ is east).
 # 3.  Zone name used in value of TZ environment variable.
 #     Please see the theory.html file for how zone names are chosen.
@@ -371,7 +371,7 @@ US	+593249-1394338	America/Yakutat	Alaska - Yakutat
 US	+643004-1652423	America/Nome	Alaska (west)
 US	+515248-1763929	America/Adak	Aleutian Islands
 US,UM	+211825-1575130	Pacific/Honolulu	Hawaii
-UY	-3453-05611	America/Montevideo
+UY	-345433-0561245	America/Montevideo
 UZ	+3940+06648	Asia/Samarkand	Uzbekistan (west)
 UZ	+4120+06918	Asia/Tashkent	Uzbekistan (east)
 VE	+1030-06656	America/Caracas
diff --git a/lib/subliminal/core.py b/lib/subliminal/core.py
index 1f715ea1fd2ec2f48dc57243c18b9a14c1a1d5e2..e879eb80f270e3da39897c5774d40c3347e4090e 100644
--- a/lib/subliminal/core.py
+++ b/lib/subliminal/core.py
@@ -388,7 +388,7 @@ def search_external_subtitles(path, directory=None):
     subtitles = {}
     for p in os.listdir(directory or dirpath):
         # keep only valid subtitle filenames
-        if not p.startswith(fileroot) or not p.endswith(SUBTITLE_EXTENSIONS):
+        if not p.startswith(fileroot) or not p.lower().endswith(SUBTITLE_EXTENSIONS):
             continue
 
         # extract the potential language code
@@ -420,7 +420,7 @@ def scan_video(path):
         raise ValueError('Path does not exist')
 
     # check video extension
-    if not path.endswith(VIDEO_EXTENSIONS):
+    if not path.lower().endswith(VIDEO_EXTENSIONS):
         raise ValueError('%r is not a valid video extension' % os.path.splitext(path)[1])
 
     dirpath, filename = os.path.split(path)
@@ -468,7 +468,7 @@ def scan_archive(path):
         rar = RarFile(path)
 
         # filter on video extensions
-        rar_filenames = [f for f in rar.namelist() if f.endswith(VIDEO_EXTENSIONS)]
+        rar_filenames = [f for f in rar.namelist() if f.lower().endswith(VIDEO_EXTENSIONS)]
 
         # no video found
         if not rar_filenames:
@@ -521,17 +521,26 @@ def scan_videos(path, age=None, archives=True):
             if dirname.startswith('.'):
                 logger.debug('Skipping hidden dirname %r in %r', dirname, dirpath)
                 dirnames.remove(dirname)
+            # Skip Sample folder
+            if dirname.lower() == 'sample':
+                logger.debug('Skipping sample dirname %r in %r', dirname, dirpath)
+                dirnames.remove(dirname)
 
         # scan for videos
         for filename in filenames:
             # filter on videos and archives
-            if not (filename.endswith(VIDEO_EXTENSIONS) or archives and filename.endswith(ARCHIVE_EXTENSIONS)):
+            if not (filename.lower().endswith(VIDEO_EXTENSIONS) or
+                    archives and filename.lower().endswith(ARCHIVE_EXTENSIONS)):
                 continue
 
             # skip hidden files
             if filename.startswith('.'):
                 logger.debug('Skipping hidden filename %r in %r', filename, dirpath)
                 continue
+            # skip 'sample' media files
+            if os.path.splitext(filename)[0].lower() == 'sample':
+                logger.debug('Skipping sample filename %r in %r', filename, dirpath)
+                continue
 
             # reconstruct the file path
             filepath = os.path.join(dirpath, filename)
@@ -553,13 +562,13 @@ def scan_videos(path, age=None, archives=True):
                     continue
 
             # scan
-            if filename.endswith(VIDEO_EXTENSIONS):  # video
+            if filename.lower().endswith(VIDEO_EXTENSIONS):  # video
                 try:
                     video = scan_video(filepath)
                 except ValueError:  # pragma: no cover
                     logger.exception('Error scanning video')
                     continue
-            elif archives and filename.endswith(ARCHIVE_EXTENSIONS):  # archive
+            elif archives and filename.lower().endswith(ARCHIVE_EXTENSIONS):  # archive
                 try:
                     video = scan_archive(filepath)
                 except (NotRarFile, RarCannotExec, ValueError):  # pragma: no cover
diff --git a/lib/subliminal/providers/addic7ed.py b/lib/subliminal/providers/addic7ed.py
index 2926081e0d5644343d839c20dfd8276d66d42b83..1832cf92c88fb122f4cdb483ab58c0c9d6fe07ae 100644
--- a/lib/subliminal/providers/addic7ed.py
+++ b/lib/subliminal/providers/addic7ed.py
@@ -179,7 +179,7 @@ class Addic7edProvider(Provider):
 
         # make the search
         logger.info('Searching show ids with %r', params)
-        r = self.session.get(self.server_url + 'search.php', params=params, timeout=10)
+        r = self.session.get(self.server_url + 'srch.php', params=params, timeout=10)
         r.raise_for_status()
         soup = ParserBeautifulSoup(r.content, ['lxml', 'html.parser'])
 
diff --git a/lib/subliminal/providers/legendastv.py b/lib/subliminal/providers/legendastv.py
index 925bb9ead723e21420a0bdcf898333dc311c692f..3468c65c1c82c62f1e7befa2226599d1d3c4d1d0 100644
--- a/lib/subliminal/providers/legendastv.py
+++ b/lib/subliminal/providers/legendastv.py
@@ -170,7 +170,7 @@ class LegendasTVProvider(Provider):
 
         # Provider needs UNRAR installed. If not available raise ConfigurationError
         try:
-            rarfile.custom_check(rarfile.UNRAR_TOOL)
+            rarfile.custom_check([rarfile.UNRAR_TOOL], True)
         except rarfile.RarExecError:
             raise ConfigurationError('UNRAR tool not available')
 
diff --git a/lib/subliminal/subtitle.py b/lib/subliminal/subtitle.py
index 726b28e3731eb74b557ef22666a7931300ea3c1d..83beab63a6c7494d81c9f30655ea34110dfb939d 100644
--- a/lib/subliminal/subtitle.py
+++ b/lib/subliminal/subtitle.py
@@ -238,7 +238,10 @@ def guess_matches(video, guess, partial=False):
     if video.resolution and 'screen_size' in guess and guess['screen_size'] == video.resolution:
         matches.add('resolution')
     # format
-    if video.format and 'format' in guess and guess['format'].lower() == video.format.lower():
+    # Guessit may return a list for `format`, which indicates a conflict in the guessing.
+    # We should match `format` only when it returns single value to avoid false `format` matches
+    if video.format and guess.get('format') and not isinstance(guess['format'], list) \
+            and guess['format'].lower() == video.format.lower():
         matches.add('format')
     # video_codec
     if video.video_codec and 'video_codec' in guess and guess['video_codec'] == video.video_codec: