Changeset 1872:435b04a1c040


Ignore:
Timestamp:
Jan 1, 2020, 1:03:59 AM (3 weeks ago)
Author:
Stefan Schwarzer <sschwarzer@…>
Branch:
default
amend_source:
7826bcef9143365ddcc8d5f85b0c23cd82561ebf
Message:
Fix bug around year change (ticket #131)

The previous code failed around a year change when the local client
time was still in one year and the local server time already in the
next year. See the ticket for details.
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • ftputil/stat.py

    r1860 r1872  
    284284            st_mtime_precision = DAY_PRECISION
    285285        else:
     286            st_mtime_precision = MINUTE_PRECISION
    286287            # `year_or_time` is a time hh:mm.
    287288            hour, minute = year_or_time.split(":")
     
    291292                self._as_int(minute, "minute"),
    292293            )
    293             # Try the current year
    294             year = time.localtime()[0]
    295             st_mtime = self._mktime((year, month, day, hour, minute, 0, 0, 0, -1))
    296             st_mtime_precision = MINUTE_PRECISION
    297             # Rhs of comparison: Transform client time to server time
    298             # (as on the lhs), so both can be compared with respect
    299             # to the set time shift (see the definition of the time
    300             # shift in `FTPHost.set_time_shift`'s docstring). The
    301             # last addend allows for small deviations between the
    302             # supposed (rounded) and the actual time shift.
    303             #
    304             # XXX: The downside of this "correction" is that there is
    305             # a one-minute time interval exactly one year ago that
    306             # may cause that datetime to be recognized as the current
    307             # datetime, but after all the datetime from the server
    308             # can only be exact up to a minute.
    309             if st_mtime > time.time() + time_shift + st_mtime_precision:
    310                 # If it's in the future, use previous year.
    311                 st_mtime = self._mktime(
    312                     (year - 1, month, day, hour, minute, 0, 0, 0, -1)
    313                 )
    314         # If we had a datetime before the epoch, the resulting value
    315         # 0.0 doesn't tell us anything about the precision.
     294            # Year and datetime in the local server time
     295            server_year = (
     296                datetime.datetime.now() + datetime.timedelta(seconds=time_shift)
     297            ).year
     298            server_datetime = datetime.datetime(
     299                server_year, month, day, hour, minute, 0
     300            )
     301            # Datetime in the local client time
     302            client_datetime = server_datetime - datetime.timedelta(seconds=time_shift)
     303            # If the client datetime is in the future, the timestamp is actually
     304            # in the past, from last year. Add the deviation (the `timedelta`
     305            # value to account for differences because of limited precision in
     306            # the directory listing.
     307            if client_datetime > datetime.datetime.now() + datetime.timedelta(
     308                seconds=st_mtime_precision
     309            ):
     310                client_datetime = client_datetime.replace(year=client_datetime.year - 1)
     311            # According to
     312            # https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp
     313            # this assumes that the datetime is in local time and returns the
     314            # seconds since the epoch, just what we want.
     315            st_mtime = client_datetime.timestamp()
     316        # If we had a datetime before the epoch, the resulting value 0.0 doesn't
     317        # tell us anything about the precision.
    316318        if st_mtime == 0.0:
    317319            st_mtime_precision = UNKNOWN_PRECISION
  • test/test_stat.py

    r1848 r1872  
    528528            host._stat._parser = ftputil.stat.UnixParser()
    529529            host.set_time_shift(supposed_time_shift)
    530             server_time = time.time() + supposed_time_shift + deviation
     530            utc_time = time.time()
     531            server_time = utc_time + supposed_time_shift + deviation
    531532            stat_result = host._stat._parser.parse_line(
    532533                self.dir_line(server_time), host.time_shift()
    533534            )
    534             self.assert_equal_times(stat_result.st_mtime, server_time)
     535            # We expect `st_mtime` in UTC.
     536            self.assert_equal_times(stat_result.st_mtime, utc_time + deviation)
    535537
    536538    def test_time_shifts(self):
Note: See TracChangeset for help on using the changeset viewer.