Skip to content

add cross-platform support for %s strftime-format code #56959

Description

@DanielOConnor
BPO 12750
Nosy @tim-one, @abalkin, @vstinner, @rbtcollins, @bitdancer, @4kir4, @pganssle, @AdamWill
Files
  • strftime.patch: patch for strftime("%s")
  • strftime2.patch: rounding problem fixed with math.floor
  • strftime3.patch: more tests and some documentation added.
  • 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:

    assignee = 'https://github.com/abalkin'
    closed_at = None
    created_at = <Date 2011-08-15.02:31:12.597>
    labels = ['extension-modules', 'easy', 'type-feature', 'library']
    title = 'add cross-platform support for %s strftime-format code'
    updated_at = <Date 2018-07-05.15:45:46.504>
    user = 'https://bugs.python.org/DanielOConnor'

    bugs.python.org fields:

    activity = <Date 2018-07-05.15:45:46.504>
    actor = 'p-ganssle'
    assignee = 'belopolsky'
    closed = False
    closed_date = None
    closer = None
    components = ['Extension Modules', 'Library (Lib)']
    creation = <Date 2011-08-15.02:31:12.597>
    creator = "Daniel.O'Connor"
    dependencies = []
    files = ['27231', '35785', '35816']
    hgrepos = []
    issue_num = 12750
    keywords = ['patch', 'easy']
    message_count = 29.0
    messages = ['142095', '142129', '142130', '142131', '142150', '142190', '142244', '142245', '142249', '142250', '170808', '221353', '221385', '221386', '221602', '221606', '221620', '221622', '221868', '221872', '221873', '221875', '221877', '222030', '222047', '225643', '247372', '270533', '313176']
    nosy_count = 13.0
    nosy_names = ['tim.peters', 'belopolsky', 'vstinner', 'rbcollins', 'r.david.murray', 'santoso.wijaya', 'akira', 'bignose', "Daniel.O'Connor", 'mumino', 'shanmbic', 'p-ganssle', 'adamwill']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue12750'
    versions = ['Python 3.6']

    Linked PRs

    Activity

    1. DanielOConnor commented on Aug 15, 2011

      DanielOConnormannequin
      MannequinAuthor

      It isn't possible to add a timezone to a naive datetime object which means that if you are getting them from some place you can't directly control there is no way to set the TZ.

      eg pywws' DataStore returns naive datetime's which are in UTC. There is no way to set this and hence strftime seems to think they are in local time.

      I can sort of see why you would disallow changing a TZ once set but it doesn't make sense to prevent this for naive DTs.

      Also, utcnow() returns a naive DT whereas it would seem to be more sensible to return it with a UTC TZ.

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-featureA feature request or enhancement
      on Aug 15, 2011
    3. bitdancer commented on Aug 15, 2011

      @bitdancer
      Member

      In what way does 'replace' not satisfy your need to set the tzinfo?

      As for utcnow, we can't change what it returns for backward compatibility reasons, but you can get a non-naive utc datatime by doing datetime.now(timezone.utc). (I must admit, however, that at least this morning I can't wrap my head around how that works based on the docs :(.

    4. DanielOConnor commented on Aug 15, 2011

      DanielOConnormannequin
      MannequinAuthor

      On 15/08/2011, at 23:39, R. David Murray wrote:

      R. David Murray <rdmurray@bitdance.com> added the comment:

      In what way does 'replace' not satisfy your need to set the tzinfo?

      Ahh that would work, although it is pretty clumsy since you have to specify everything else as well.

      In the end I used calendar.timegm (which I only found out about after this).

      As for utcnow, we can't change what it returns for backward compatibility reasons, but you can get a non-naive utc datatime by doing ´

      That is a pity :(

      datetime.now(timezone.utc). (I must admit, however, that at least this morning I can't wrap my head around how that works based on the docs :(.

      OK.. I am only using 2.7 so I can't try that :)

      ----------
      nosy: +r.david.murray


      Python tracker <report@bugs.python.org>
      <http://bugs.python.org/issue12750\>


    5. bitdancer commented on Aug 15, 2011

      @bitdancer
      Member

      Ah. Well, pre-3.2 datetime itself did not generate *any* non-naive datetimes.

      Nor do you need to specify everything for replace. dt.replace(tzinfo=tz) should work just fine.

    6. DanielOConnor commented on Aug 15, 2011

      DanielOConnormannequin
      MannequinAuthor

      On 16/08/2011, at 1:06, R. David Murray wrote:

      R. David Murray <rdmurray@bitdance.com> added the comment:

      Ah. Well, pre-3.2 datetime itself did not generate *any* non-naive datetimes.

      Nor do you need to specify everything for replace. dt.replace(tzinfo=tz) should work just fine.

      OK.

      I did try this and it seems broken though..
      In [19]: now = datetime.datetime.utcnow()

      In [21]: now.replace(tzinfo = pytz.utc)
      Out[21]: datetime.datetime(2011, 8, 15, 22, 54, 13, 173110, tzinfo=<UTC>)

      In [22]: datetime.datetime.strftime(now, "%s")
      Out[22]: '1313414653'

      In [23]: now
      Out[23]: datetime.datetime(2011, 8, 15, 22, 54, 13, 173110)

      [ur 8:22] ~ >date -ujr 1313414653
      Mon 15 Aug 2011 13:24:13 UTC

      i.e. it appears that replace() applies the TZ offset to a naive datetime object effectively assuming it is local time rather than un-timezoned (which is what the docs imply to me)

      ----------
      resolution: -> invalid
      stage: -> committed/rejected
      status: open -> closed


      Python tracker <report@bugs.python.org>
      <http://bugs.python.org/issue12750\>


    7. bitdancer commented on Aug 16, 2011

      @bitdancer
      Member

      OK. At a minimum there is a doc issue here, so I'm reopening.

    8. changed the title [-]datetime.datetime timezone problems[/-] [+]datetime.datetime how to correctly attach a timezone to an existing naive datetime[/+] on Aug 16, 2011
    9. abalkin commented on Aug 17, 2011

      @abalkin
      Member

      i.e. it appears that replace() applies the TZ offset to a naive datetime
      object effectively assuming it is local time rather than un-timezoned
      (which is what the docs imply to me)

      I don't understand your issue. The replace method does not assume anything, it just replaces whatever fields you specify with new values. You can replace tzinfo just like any other field, year, month, day, etc while preserving the other fields. I think this is fairly well documented. I think what you are looking for is the astimezone() method which, however may not work well on naive datetime instances simply because a naive instance may be ambiguous in presence of DST. However, if you start with an aware UTC datetime, you should be able to use astimezone() to convert to any local TZ.

    10. DanielOConnor commented on Aug 17, 2011

      DanielOConnormannequin
      MannequinAuthor

      On 17/08/2011, at 10:30, Alexander Belopolsky wrote:

      Alexander Belopolsky <alexander.belopolsky@gmail.com> added the comment:

      > i.e. it appears that replace() applies the TZ offset to a naive datetime
      > object effectively assuming it is local time rather than un-timezoned
      > (which is what the docs imply to me)

      I don't understand your issue. The replace method does not assume anything, it just replaces whatever fields you specify with new values. You can replace tzinfo just like any other field, year, month, day, etc while preserving the other fields. I think this is fairly well documented. I think what you are looking for is the astimezone() method which, however may not work well on naive datetime instances simply because a naive instance may be ambiguous in presence of DST. However, if you start with an aware UTC datetime, you should be able to use astimezone() to convert to any local TZ.

      Hmm I see, it would appear the problem lies with strftime().

      [ur 10:34] ~ >ipython-2.7
      Python 2.7.2 (default, Aug 6 2011, 23:46:16)
      Type "copyright", "credits" or "license" for more information.
      IPython 0.10.2 -- An enhanced Interactive Python.
      ? -> Introduction and overview of IPython's features.
      %quickref -> Quick reference.
      help -> Python's own help system.
      object? -> Details about 'object'. ?object also works, ?? prints more.

      In [48]: now = datetime.datetime.utcnow()
      In [49]: nowtz = now.replace(tzinfo = pytz.utc)
      In [50]: nowadl = now.replace(tzinfo = pytz.timezone('Australia/Adelaide'))
      In [51]: now
      Out[51]: datetime.datetime(2011, 8, 17, 1, 53, 51, 451118)
      In [52]: nowtz
      Out[52]: datetime.datetime(2011, 8, 17, 1, 53, 51, 451118, tzinfo=<UTC>)
      In [53]: nowadl
      Out[53]: datetime.datetime(2011, 8, 17, 1, 53, 51, 451118, tzinfo=<DstTzInfo 'Australia/Adelaide' CST+9:30:00 STD>)
      In [54]: now.strftime("%F %r %s")
      Out[54]: '2011-08-17 01:53:51 AM 1313511831'
      In [55]: nowtz.strftime("%F %r %s")
      Out[55]: '2011-08-17 01:53:51 AM 1313511831'
      In [56]: nowadl.strftime("%F %r %s")
      Out[56]: '2011-08-17 01:53:51 AM 1313511831'

      Wed 17 Aug 2011 01:54:52 UTC
      [ur 11:24] ~ >date +%s
      1313546093
      [ur 11:24] ~ >date -ujr date +%s
      Wed 17 Aug 2011 01:54:59 UTC
      [ur 11:24] ~ >date -ujr 1313511831
      Tue 16 Aug 2011 16:23:51 UTC

      i.e. strftime disregards tzinfo and seems to treat the time as LT (I think).

      It certainly doesn't behave the way I'd expect after using strftime(3) et al :)

    11. 16 remaining items

    12. abalkin commented on Jun 29, 2014

      @abalkin
      Member

      Could you, please add tests for non-fixed offset timezones? There are several defined in datetimetester.py already.

    13. abalkin commented on Jun 29, 2014

      @abalkin
      Member
    14. abalkin commented on Jun 29, 2014

      @abalkin
      Member

      + t = datetime(1969, 1, 1, 0,0,0, 600000, tzinfo=timezone.utc)

      Please add spaces after commas.

    15. mumino commented on Jul 1, 2014

      muminomannequin
      Mannequin

      more tests and some documentation added.

    16. 4kir4 commented on Jul 1, 2014

      4kir4mannequin
      Mannequin

      %s format code behaviour was undefined and incidental.

      strftime('%s') is not portable but it *is* supported on some
      platforms i.e., it is *not* undefined and it is *not* incidental
      on these platforms. datetime.strftime *delegates* to the platform
      strftime(3) and some platforms do support %s format code. See the
      quote from the datetime docs in msg221385.

      It would be preferable that datetime.strftime would reject format
      codes that it doesn't support explicitly (like datetime.strptime
      does) so that datetime.strftime were portable but that ship
      has sailed.

      This issue could be titled: add cross-platform support for %s
      strftime-format code (and fix its behavior (add support) for
      timezone-aware datetime objects).

      ---

      If the implementation uses floats to get an integer result; it should
      have tests for edge cases (datetime.min, datetime.max at least). I
      don't see such tests, please, correct me if I'm wrong.

    17. 4kir4 commented on Aug 22, 2014

      4kir4mannequin
      Mannequin

      bpo-22246 discusses the reverse: strptime('12345', '%s')

    18. rbtcollins commented on Jul 25, 2015

      @rbtcollins
      Member

      Moving this back to patch needed: the patch was reviewed by a committer and changes requested.

    19. abalkin commented on Jul 16, 2016

      @abalkin
      Member

      Given that we have the .timestamp() method, I am not sure this would be a very useful feature, but maybe it is a way to eliminate an attractive nuisance.

      If anyone is still interested in getting this in - please check with python-ideas.

    20. changed the title [-]datetime.strftime('%s') should respect tzinfo[/-] [+]add cross-platform support for %s strftime-format code[/+] on Jul 16, 2016
    21. adamwill commented on Mar 3, 2018

      adamwillmannequin
      Mannequin

      On the "attractive nuisance" angle: I just ran right into this problem, and reported https://bugs.python.org/issue32988 .

      As I suggested there, if Python doesn't try to fix this, I'd suggest it should at least *explicitly document* that using %s is unsupported and dangerous in more than one way (might not work on all platforms, does not do what it should for 'aware' datetimes on platforms where it *does* work). I think explicitly telling people NOT to use it would be better than just not mentioning it. At least for me, when I saw real code using it and that the docs just didn't mention it, my initial thought was "I guess it must be OK, and the docs just missed it out for some reason". If I'd gone to the docs and seen an explicit note that it's not supported and doesn't work right, that would've been much clearer and I wouldn't have had to figure that out for myself :)

      For Python 2, btw, the arrow library might be a suitable alternative to suggest: you can do something like this, assuming you have an aware datetime object called 'awaredate' you want to get the timestamp for:

      import arrow
      ts = arrow.get(awaredate).timestamp

      and it does the right thing.

    22. transferred this issue fromon Apr 10, 2022
    23. lunaynx commented on Nov 2, 2024

      @lunaynx

      I agree with @AdamWill here, I just ran into this today.

    24. marked add strptime(s, '%s') #66442 as a duplicate of this issue on Mar 21, 2025
    25. simpleprogrammer2 commented on Jul 24, 2026

      @simpleprogrammer2

      @abalkin can I add PR at least covering documentation update as suggested by @AdamWill also supported by other user @lunaynx. More people may report this issue again and again.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    easyextension-modulesC modules in the Modules dirstdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions