python / cpython

The Python programming language
https://www.python.org
Other
62.89k stars 30.13k forks source link

[Windows] datetime.fromtimestamp(t) when 0 <= t <= 86399 fails on Python 3.6 #73283

Closed efeabc88-f2dc-4010-8451-b42e7783ad9d closed 6 years ago

efeabc88-f2dc-4010-8451-b42e7783ad9d commented 7 years ago
BPO 29097
Nosy @tim-one, @pfmoore, @abalkin, @vstinner, @tjguk, @bitdancer, @Preston-Landers, @jleclanche, @zware, @zooba, @ammaraskar, @pganssle
PRs
  • python/cpython#2385
  • python/cpython#8466
  • python/cpython#8498
  • Files
  • forego_fold_detection.patch
  • truncate_negatives.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields: ```python assignee = None closed_at = created_at = labels = ['3.8', 'type-bug', '3.7', 'OS-windows'] title = '[Windows] datetime.fromtimestamp(t) when 0 <= t <= 86399 fails on Python 3.6' updated_at = user = 'https://bugs.python.org/pekkaklarck' ``` bugs.python.org fields: ```python activity = actor = 'ammar2' assignee = 'none' closed = True closed_date = closer = 'ammar2' components = ['Windows'] creation = creator = 'pekka.klarck' dependencies = [] files = ['46091', '46092'] hgrepos = [] issue_num = 29097 keywords = ['patch'] message_count = 16.0 messages = ['284199', '284250', '284264', '284265', '284269', '284272', '284281', '284286', '284326', '296214', '296759', '296795', '309084', '322370', '322392', '322496'] nosy_count = 14.0 nosy_names = ['tim.peters', 'paul.moore', 'belopolsky', 'vstinner', 'tim.golden', 'r.david.murray', 'SilentGhost', 'planders', 'pekka.klarck', 'jleclanche', 'zach.ware', 'steve.dower', 'ammar2', 'p-ganssle'] pr_nums = ['2385', '8466', '8498'] priority = 'normal' resolution = 'fixed' stage = 'resolved' status = 'closed' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue29097' versions = ['Python 3.6', 'Python 3.7', 'Python 3.8'] ```

    efeabc88-f2dc-4010-8451-b42e7783ad9d commented 7 years ago

    For example:

    E:\>py -3.6 -c "import datetime; datetime.datetime.fromtimestamp(42)"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    OSError: [Errno 22] Invalid argument

    Works fine at least with Python 2.6-2.7 and 3.3-3.5. Only tested on Windows (missing deadsnakes for easy Linux testing).

    I was also surprised to get OSError in general, but apparently fromtimestamp uses that instead of ValueError nowadays in some error situations.

    8977102d-e623-4805-b309-cd0ad35b0d72 commented 7 years ago

    This doesn't seem likely that it's anything to do with datetime, could you try reproducing it in repl?

    ammaraskar commented 7 years ago

    Can recreate successfully on windows, but not on linux:

    > C:\Python36\python.exe -c "import datetime; datetime.datetime.fromtimestamp(42)"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    OSError: [Errno 22] Invalid argument
    > C:\Python36\python.exe
    Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import datetime; datetime.datetime.fromtimestamp(42)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 22] Invalid argument
    8977102d-e623-4805-b309-cd0ad35b0d72 commented 7 years ago

    Cannot reproduce this with the tip on linux

    bitdancer commented 7 years ago

    Sounds like it really is an OSError (that is, that the Windows OS is the source of the error). Whether or not we can or should do something about that is a separate question, though.

    ammaraskar commented 7 years ago

    The 86,399 upperbound comes from this line (max_fold_seconds=86400):

    https://github.com/python/cpython/blob/master/Modules/_datetimemodule.c#L4277-L4278

    The bug is introduced as part of the fold detection in this commit: https://github.com/python/cpython/commit/56376cacd2a0f6af834fe4d2a20b1a1095afb26f

    Window's locatime_s function gives an EINVAL error when time is less than 0. Which is where the OSError comes from.

    https://msdn.microsoft.com/en-us/library/a442x3ye.aspx

    ammaraskar commented 7 years ago

    I just ran the following script to check if there are any folds from timestamps [0, 86399] in any timezone.

      import datetime
      import pytz
    
      for tz in pytz.all_timezones:
          tz = pytz.timezone(tz)
          for i in range(86400):
              if datetime.datetime.fromtimestamp(i, tz).fold == 1:
                  print(str(tz)) 

    and it turns out there aren't any. I highly doubt any timezone is going to retroactively enable/disable DST during the epoch, so a potentially hacky fix is to just have a windows specific check for this value range.

    I'm adding the people involved in PEP-495 to the nosy list so they can give their input.

    abalkin commented 7 years ago

    I think we should just clip the negative lower probe value to 0 on Windows before passing it to local(). Also, if we still care about platforms with 32-bit time_t, we should check for over/under flow *before* calling local().

    ammaraskar commented 7 years ago

    I've attached two patches that fix this behavior, one by simply foregoing the fold detection for this time range on windows (the patch that I'd argue is simpler and more readable)

    And one that truncates the passed values to local to not be negative. This one would have been simple but unfortunately there's the complication that there are two local calls and additionally, the second one cannot be truncated to exactly 0 because then we incorrectly detect a fold for the timestamp 0.

    abalkin commented 7 years ago

    I just ran the following script to check if there are any folds from timestamps [0, 86399] in any timezone.

    Ammar, I am not sure pytz timezones support PEP-495 conventions. Can you repeat your experiment using the latest dateutil.tz or test.datetimetester.ZoneInfo from Python 3.6 test suit?

    See \http://dateutil.readthedocs.io/en/stable/tz.html\.

    ammaraskar commented 7 years ago

    Hey, sorry for the late response. I just ran:

      import datetime
      from dateutil.zoneinfo import get_zonefile_instance
      import dateutil.tz
    
      zonenames = list(get_zonefile_instance().zones)
    
      for tz in zonenames:
          tz = dateutil.tz.gettz(tz)
          for i in range(86400):
              if datetime.datetime.fromtimestamp(i, tz).fold == 1:
                  print(str(tz))

    tz uses your OS's zone info so also posting my distro version: Debian 8.8 - 3.16.0-4-amd64 #1 SMP Debian 3.16.43-2 (2017-04-30) x86_64 GNU/Linux

    And I got the same result, no timezone with folds in these range.

    ammaraskar commented 7 years ago

    Created a github pull request for the recommended patch along with a test: https://github.com/python/cpython/pull/2385

    c7180ac6-d9ee-44f8-88af-9e008d9a8b29 commented 6 years ago

    Anything holding up the PR? Looks good at a glance, would be nice to get this landed in time for 3.7.

    abalkin commented 6 years ago

    New changeset 96d1e69a12ed8ab80203277e1abdaf573457a964 by Alexander Belopolsky (Ammar Askar) in branch 'master': bpo-29097: Forego fold detection on windows for low timestamp values (GH-2385) https://github.com/python/cpython/commit/96d1e69a12ed8ab80203277e1abdaf573457a964

    abalkin commented 6 years ago

    New changeset 973649342cee3869409f341ff0f0e3d2b1547c2a by Alexander Belopolsky (Miss Islington (bot)) in branch '3.7': bpo-29097: Forego fold detection on windows for low timestamp values (GH-2385) (GH-8466) https://github.com/python/cpython/commit/973649342cee3869409f341ff0f0e3d2b1547c2a

    abalkin commented 6 years ago

    New changeset 6ea8a3a0ebf840ca57b6dba9cad26fbb0ddaa5d4 by Alexander Belopolsky (Ammar Askar) in branch '3.6': [3.6] bpo-29097: Forego fold detection on windows for low timestamp values (GH-2385) (GH-8498) https://github.com/python/cpython/commit/6ea8a3a0ebf840ca57b6dba9cad26fbb0ddaa5d4