Closes issue #24773: Implement PEP 495 (Local Time Disambiguation).
diff --git a/Lib/datetime.py b/Lib/datetime.py
index b1321a3..19d2f67 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -250,9 +250,9 @@
     if not isinstance(offset, timedelta):
         raise TypeError("tzinfo.%s() must return None "
                         "or timedelta, not '%s'" % (name, type(offset)))
-    if offset % timedelta(minutes=1) or offset.microseconds:
+    if offset.microseconds:
         raise ValueError("tzinfo.%s() must return a whole number "
-                         "of minutes, got %s" % (name, offset))
+                         "of seconds, got %s" % (name, offset))
     if not -timedelta(1) < offset < timedelta(1):
         raise ValueError("%s()=%s, must be strictly between "
                          "-timedelta(hours=24) and timedelta(hours=24)" %
@@ -930,7 +930,7 @@
 
     # Pickle support.
 
-    def _getstate(self):
+    def _getstate(self, protocol=3):
         yhi, ylo = divmod(self._year, 256)
         return bytes([yhi, ylo, self._month, self._day]),
 
@@ -938,8 +938,8 @@
         yhi, ylo, self._month, self._day = string
         self._year = yhi * 256 + ylo
 
-    def __reduce__(self):
-        return (self.__class__, self._getstate())
+    def __reduce_ex__(self, protocol):
+        return (self.__class__, self._getstate(protocol))
 
 _date_class = date  # so functions w/ args named "date" can get at the class
 
@@ -947,6 +947,7 @@
 date.max = date(9999, 12, 31)
 date.resolution = timedelta(days=1)
 
+
 class tzinfo:
     """Abstract base class for time zone info classes.
 
@@ -1038,11 +1039,11 @@
     dst()
 
     Properties (readonly):
-    hour, minute, second, microsecond, tzinfo
+    hour, minute, second, microsecond, tzinfo, fold
     """
-    __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode'
+    __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'
 
-    def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
+    def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):
         """Constructor.
 
         Arguments:
@@ -1050,8 +1051,9 @@
         hour, minute (required)
         second, microsecond (default to zero)
         tzinfo (default to None)
+        fold (keyword only, default to True)
         """
-        if isinstance(hour, bytes) and len(hour) == 6 and hour[0] < 24:
+        if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24:
             # Pickle support
             self = object.__new__(cls)
             self.__setstate(hour, minute or None)
@@ -1067,6 +1069,7 @@
         self._microsecond = microsecond
         self._tzinfo = tzinfo
         self._hashcode = -1
+        self._fold = fold
         return self
 
     # Read-only field accessors
@@ -1095,6 +1098,10 @@
         """timezone info object"""
         return self._tzinfo
 
+    @property
+    def fold(self):
+        return self._fold
+
     # Standard conversions, __hash__ (and helpers)
 
     # Comparisons of time objects with other.
@@ -1160,9 +1167,13 @@
     def __hash__(self):
         """Hash."""
         if self._hashcode == -1:
-            tzoff = self.utcoffset()
+            if self.fold:
+                t = self.replace(fold=0)
+            else:
+                t = self
+            tzoff = t.utcoffset()
             if not tzoff:  # zero or None
-                self._hashcode = hash(self._getstate()[0])
+                self._hashcode = hash(t._getstate()[0])
             else:
                 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
                               timedelta(hours=1))
@@ -1186,10 +1197,11 @@
             else:
                 sign = "+"
             hh, mm = divmod(off, timedelta(hours=1))
-            assert not mm % timedelta(minutes=1), "whole minute"
-            mm //= timedelta(minutes=1)
+            mm, ss = divmod(mm, timedelta(minutes=1))
             assert 0 <= hh < 24
             off = "%s%02d%s%02d" % (sign, hh, sep, mm)
+            if ss:
+                off += ':%02d' % ss.seconds
         return off
 
     def __repr__(self):
@@ -1206,6 +1218,9 @@
         if self._tzinfo is not None:
             assert s[-1:] == ")"
             s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
+        if self._fold:
+            assert s[-1:] == ")"
+            s = s[:-1] + ", fold=1)"
         return s
 
     def isoformat(self, timespec='auto'):
@@ -1284,7 +1299,7 @@
         return offset
 
     def replace(self, hour=None, minute=None, second=None, microsecond=None,
-                tzinfo=True):
+                tzinfo=True, *, fold=None):
         """Return a new time with new values for the specified fields."""
         if hour is None:
             hour = self.hour
@@ -1296,14 +1311,19 @@
             microsecond = self.microsecond
         if tzinfo is True:
             tzinfo = self.tzinfo
-        return time(hour, minute, second, microsecond, tzinfo)
+        if fold is None:
+            fold = self._fold
+        return time(hour, minute, second, microsecond, tzinfo, fold=fold)
 
     # Pickle support.
 
-    def _getstate(self):
+    def _getstate(self, protocol=3):
         us2, us3 = divmod(self._microsecond, 256)
         us1, us2 = divmod(us2, 256)
-        basestate = bytes([self._hour, self._minute, self._second,
+        h = self._hour
+        if self._fold and protocol > 3:
+            h += 128
+        basestate = bytes([h, self._minute, self._second,
                            us1, us2, us3])
         if self._tzinfo is None:
             return (basestate,)
@@ -1313,12 +1333,18 @@
     def __setstate(self, string, tzinfo):
         if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
             raise TypeError("bad tzinfo state arg")
-        self._hour, self._minute, self._second, us1, us2, us3 = string
+        h, self._minute, self._second, us1, us2, us3 = string
+        if h > 127:
+            self._fold = 1
+            self._hour = h - 128
+        else:
+            self._fold = 0
+            self._hour = h
         self._microsecond = (((us1 << 8) | us2) << 8) | us3
         self._tzinfo = tzinfo
 
-    def __reduce__(self):
-        return (time, self._getstate())
+    def __reduce_ex__(self, protocol):
+        return (time, self._getstate(protocol))
 
 _time_class = time  # so functions w/ args named "time" can get at the class
 
@@ -1335,8 +1361,8 @@
     __slots__ = date.__slots__ + time.__slots__
 
     def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
-                microsecond=0, tzinfo=None):
-        if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2] <= 12:
+                microsecond=0, tzinfo=None, *, fold=0):
+        if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12:
             # Pickle support
             self = object.__new__(cls)
             self.__setstate(year, month)
@@ -1356,6 +1382,7 @@
         self._microsecond = microsecond
         self._tzinfo = tzinfo
         self._hashcode = -1
+        self._fold = fold
         return self
 
     # Read-only field accessors
@@ -1384,6 +1411,10 @@
         """timezone info object"""
         return self._tzinfo
 
+    @property
+    def fold(self):
+        return self._fold
+
     @classmethod
     def _fromtimestamp(cls, t, utc, tz):
         """Construct a datetime from a POSIX timestamp (like time.time()).
@@ -1402,7 +1433,23 @@
         converter = _time.gmtime if utc else _time.localtime
         y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
         ss = min(ss, 59)    # clamp out leap seconds if the platform has them
-        return cls(y, m, d, hh, mm, ss, us, tz)
+        result = cls(y, m, d, hh, mm, ss, us, tz)
+        if tz is None:
+            # As of version 2015f max fold in IANA database is
+            # 23 hours at 1969-09-30 13:00:00 in Kwajalein.
+            # Let's probe 24 hours in the past to detect a transition:
+            max_fold_seconds = 24 * 3600
+            y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
+            probe1 = cls(y, m, d, hh, mm, ss, us, tz)
+            trans = result - probe1 - timedelta(0, max_fold_seconds)
+            if trans.days < 0:
+                y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]
+                probe2 = cls(y, m, d, hh, mm, ss, us, tz)
+                if probe2 == result:
+                    result._fold = 1
+        else:
+            result = tz.fromutc(result)
+        return result
 
     @classmethod
     def fromtimestamp(cls, t, tz=None):
@@ -1412,10 +1459,7 @@
         """
         _check_tzinfo_arg(tz)
 
-        result = cls._fromtimestamp(t, tz is not None, tz)
-        if tz is not None:
-            result = tz.fromutc(result)
-        return result
+        return cls._fromtimestamp(t, tz is not None, tz)
 
     @classmethod
     def utcfromtimestamp(cls, t):
@@ -1443,7 +1487,7 @@
             raise TypeError("time argument must be a time instance")
         return cls(date.year, date.month, date.day,
                    time.hour, time.minute, time.second, time.microsecond,
-                   time.tzinfo)
+                   time.tzinfo, fold=time.fold)
 
     def timetuple(self):
         "Return local time tuple compatible with time.localtime()."
@@ -1458,12 +1502,46 @@
                                   self.hour, self.minute, self.second,
                                   dst)
 
+    def _mktime(self):
+        """Return integer POSIX timestamp."""
+        epoch = datetime(1970, 1, 1)
+        max_fold_seconds = 24 * 3600
+        t = (self - epoch) // timedelta(0, 1)
+        def local(u):
+            y, m, d, hh, mm, ss = _time.localtime(u)[:6]
+            return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)
+
+        # Our goal is to solve t = local(u) for u.
+        a = local(t) - t
+        u1 = t - a
+        t1 = local(u1)
+        if t1 == t:
+            # We found one solution, but it may not be the one we need.
+            # Look for an earlier solution (if `fold` is 0), or a
+            # later one (if `fold` is 1).
+            u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]
+            b = local(u2) - u2
+            if a == b:
+                return u1
+        else:
+            b = t1 - u1
+            assert a != b
+        u2 = t - b
+        t2 = local(u2)
+        if t2 == t:
+            return u2
+        if t1 == t:
+            return u1
+        # We have found both offsets a and b, but neither t - a nor t - b is
+        # a solution.  This means t is in the gap.
+        return (max, min)[self.fold](u1, u2)
+
+
     def timestamp(self):
         "Return POSIX timestamp as float"
         if self._tzinfo is None:
-            return _time.mktime((self.year, self.month, self.day,
-                                 self.hour, self.minute, self.second,
-                                 -1, -1, -1)) + self.microsecond / 1e6
+            s = self._mktime()
+            return s + self.microsecond / 1e6
         else:
             return (self - _EPOCH).total_seconds()
 
@@ -1482,15 +1560,16 @@
 
     def time(self):
         "Return the time part, with tzinfo None."
-        return time(self.hour, self.minute, self.second, self.microsecond)
+        return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)
 
     def timetz(self):
         "Return the time part, with same tzinfo."
         return time(self.hour, self.minute, self.second, self.microsecond,
-                    self._tzinfo)
+                    self._tzinfo, fold=self.fold)
 
     def replace(self, year=None, month=None, day=None, hour=None,
-                minute=None, second=None, microsecond=None, tzinfo=True):
+                minute=None, second=None, microsecond=None, tzinfo=True,
+                *, fold=None):
         """Return a new datetime with new values for the specified fields."""
         if year is None:
             year = self.year
@@ -1508,46 +1587,45 @@
             microsecond = self.microsecond
         if tzinfo is True:
             tzinfo = self.tzinfo
-        return datetime(year, month, day, hour, minute, second, microsecond,
-                        tzinfo)
+        if fold is None:
+            fold = self.fold
+        return datetime(year, month, day, hour, minute, second,
+                          microsecond, tzinfo, fold=fold)
+
+    def _local_timezone(self):
+        if self.tzinfo is None:
+            ts = self._mktime()
+        else:
+            ts = (self - _EPOCH) // timedelta(seconds=1)
+        localtm = _time.localtime(ts)
+        local = datetime(*localtm[:6])
+        try:
+            # Extract TZ data if available
+            gmtoff = localtm.tm_gmtoff
+            zone = localtm.tm_zone
+        except AttributeError:
+            delta = local - datetime(*_time.gmtime(ts)[:6])
+            zone = _time.strftime('%Z', localtm)
+            tz = timezone(delta, zone)
+        else:
+            tz = timezone(timedelta(seconds=gmtoff), zone)
+        return tz
 
     def astimezone(self, tz=None):
         if tz is None:
-            if self.tzinfo is None:
-                raise ValueError("astimezone() requires an aware datetime")
-            ts = (self - _EPOCH) // timedelta(seconds=1)
-            localtm = _time.localtime(ts)
-            local = datetime(*localtm[:6])
-            try:
-                # Extract TZ data if available
-                gmtoff = localtm.tm_gmtoff
-                zone = localtm.tm_zone
-            except AttributeError:
-                # Compute UTC offset and compare with the value implied
-                # by tm_isdst.  If the values match, use the zone name
-                # implied by tm_isdst.
-                delta = local - datetime(*_time.gmtime(ts)[:6])
-                dst = _time.daylight and localtm.tm_isdst > 0
-                gmtoff = -(_time.altzone if dst else _time.timezone)
-                if delta == timedelta(seconds=gmtoff):
-                    tz = timezone(delta, _time.tzname[dst])
-                else:
-                    tz = timezone(delta)
-            else:
-                tz = timezone(timedelta(seconds=gmtoff), zone)
-
+            tz = self._local_timezone()
         elif not isinstance(tz, tzinfo):
             raise TypeError("tz argument must be an instance of tzinfo")
 
         mytz = self.tzinfo
         if mytz is None:
-            raise ValueError("astimezone() requires an aware datetime")
+            mytz = self._local_timezone()
 
         if tz is mytz:
             return self
 
         # Convert self to UTC, and attach the new time zone object.
-        myoffset = self.utcoffset()
+        myoffset = mytz.utcoffset(self)
         if myoffset is None:
             raise ValueError("astimezone() requires an aware datetime")
         utc = (self - myoffset).replace(tzinfo=tz)
@@ -1594,9 +1672,11 @@
             else:
                 sign = "+"
             hh, mm = divmod(off, timedelta(hours=1))
-            assert not mm % timedelta(minutes=1), "whole minute"
-            mm //= timedelta(minutes=1)
+            mm, ss = divmod(mm, timedelta(minutes=1))
             s += "%s%02d:%02d" % (sign, hh, mm)
+            if ss:
+                assert not ss.microseconds
+                s += ":%02d" % ss.seconds
         return s
 
     def __repr__(self):
@@ -1613,6 +1693,9 @@
         if self._tzinfo is not None:
             assert s[-1:] == ")"
             s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
+        if self._fold:
+            assert s[-1:] == ")"
+            s = s[:-1] + ", fold=1)"
         return s
 
     def __str__(self):
@@ -1715,6 +1798,12 @@
         else:
             myoff = self.utcoffset()
             otoff = other.utcoffset()
+            # Assume that allow_mixed means that we are called from __eq__
+            if allow_mixed:
+                if myoff != self.replace(fold=not self.fold).utcoffset():
+                    return 2
+                if otoff != other.replace(fold=not other.fold).utcoffset():
+                    return 2
             base_compare = myoff == otoff
 
         if base_compare:
@@ -1782,9 +1871,13 @@
 
     def __hash__(self):
         if self._hashcode == -1:
-            tzoff = self.utcoffset()
+            if self.fold:
+                t = self.replace(fold=0)
+            else:
+                t = self
+            tzoff = t.utcoffset()
             if tzoff is None:
-                self._hashcode = hash(self._getstate()[0])
+                self._hashcode = hash(t._getstate()[0])
             else:
                 days = _ymd2ord(self.year, self.month, self.day)
                 seconds = self.hour * 3600 + self.minute * 60 + self.second
@@ -1793,11 +1886,14 @@
 
     # Pickle support.
 
-    def _getstate(self):
+    def _getstate(self, protocol=3):
         yhi, ylo = divmod(self._year, 256)
         us2, us3 = divmod(self._microsecond, 256)
         us1, us2 = divmod(us2, 256)
-        basestate = bytes([yhi, ylo, self._month, self._day,
+        m = self._month
+        if self._fold and protocol > 3:
+            m += 128
+        basestate = bytes([yhi, ylo, m, self._day,
                            self._hour, self._minute, self._second,
                            us1, us2, us3])
         if self._tzinfo is None:
@@ -1808,14 +1904,20 @@
     def __setstate(self, string, tzinfo):
         if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
             raise TypeError("bad tzinfo state arg")
-        (yhi, ylo, self._month, self._day, self._hour,
+        (yhi, ylo, m, self._day, self._hour,
          self._minute, self._second, us1, us2, us3) = string
+        if m > 127:
+            self._fold = 1
+            self._month = m - 128
+        else:
+            self._fold = 0
+            self._month = m
         self._year = yhi * 256 + ylo
         self._microsecond = (((us1 << 8) | us2) << 8) | us3
         self._tzinfo = tzinfo
 
-    def __reduce__(self):
-        return (self.__class__, self._getstate())
+    def __reduce_ex__(self, protocol):
+        return (self.__class__, self._getstate(protocol))
 
 
 datetime.min = datetime(1, 1, 1)