Changeset 1902:93d4fddb80c3


Ignore:
Timestamp:
Jan 2, 2020, 10:49:00 PM (3 weeks ago)
Author:
Stefan Schwarzer <sschwarzer@…>
Branch:
default
amend_source:
e5a913424a9420a31c5bc7e9a2c8ea7193551a53
Message:
Fix and refactor `parse_unix_time`

While changing `parse_unix_time` to use `datetime` instead of time
tuples, I noticed that time shift wasn't taken into account for the
case where only a year but no time was given.

I noticed that the unix time parser tests had the time shift set to 0,
so these bugs weren't found.

That said, it's still to decide if it makes sense to subtract a time
shift if we now a time only up to day precision. But I think we should
apply the same rules regarding time shift regardless of the presence
of hour and minute. This is just consistent and needs fewer special
rules (the one for the unknown hour:minute case).

TODO: I still need to improve the tests to use a time shift so that
the previous bug is revealed. I want to run these improved tests on
the old code to make sure the bug is present there.

`_mktime` isn't deleted yet because it's also used by the MS line
parser, but I plan to switch this to `datetime` usage, too.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • ftputil/stat.py

    r1896 r1902  
    240240        return max(0.0, time_float)
    241241
     242    @staticmethod
     243    def _datetime(year, month, day, hour, minute, second):
     244        """
     245        Return naive `datetime.datetime` object for the given year, month, day,
     246        hour, minute and second.
     247
     248        If there are invalid values, for example minute > 59, raise a
     249        `ParserError`.
     250        """
     251        try:
     252            return datetime.datetime(year, month, day, hour, minute, second)
     253        except ValueError:
     254            invalid_datetime = (
     255                f"{year:04d}-{month:02d}-{day:02d} "
     256                f"{hour:02d}:{minute:02d}:{second:02d}"
     257            )
     258            raise ftputil.error.ParserError(
     259                "invalid datetime {0!r}".format(invalid_datetime)
     260            )
     261
    242262    def parse_unix_time(
    243263        self, month_abbreviation, day, year_or_time, time_shift, with_precision=False
     
    278298            )
    279299        day = self._as_int(day, "day")
    280         if ":" not in year_or_time:
    281             # `year_or_time` is really a year.
    282             st_mtime_precision = DAY_PRECISION
    283             year, hour, minute = self._as_int(year_or_time, "year"), 0, 0
    284             st_mtime = self._mktime((year, month, day, hour, minute, 0, 0, 0, -1))
    285         else:
     300        year_is_unknown = ":" in year_or_time
     301        if year_is_unknown:
    286302            # `year_or_time` is a time hh:mm.
    287303            st_mtime_precision = MINUTE_PRECISION
     
    296312                datetime.datetime.now() + datetime.timedelta(seconds=time_shift)
    297313            ).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)
     314        else:
     315            # `year_or_time` is really a year.
     316            st_mtime_precision = DAY_PRECISION
     317            server_year, hour, minute = self._as_int(year_or_time, "year"), 0, 0
     318        server_datetime = self._datetime(server_year, month, day, hour, minute, 0)
     319        # Datetime in the local client time
     320        client_datetime = server_datetime - datetime.timedelta(seconds=time_shift)
     321        if year_is_unknown:
    303322            # If the client datetime is in the future, the timestamp is actually
    304323            # in the past, from last year. Add the deviation (the `timedelta`
     
    309328            ):
    310329                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()
     330        # According to
     331        # https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp
     332        # this assumes that the datetime is in local time and returns the
     333        # seconds since the epoch, just what we want.
     334        st_mtime = client_datetime.timestamp()
    316335        # If we had a datetime before the epoch, the resulting value 0.0 doesn't
    317336        # tell us anything about the precision.
    318         if st_mtime == 0.0:
     337        if st_mtime < 0.0:
    319338            st_mtime_precision = UNKNOWN_PRECISION
     339            st_mtime = 0.0
    320340        #
    321341        if with_precision:
Note: See TracChangeset for help on using the changeset viewer.