-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add cross-platform support for %s strftime-format code #56959
Comments
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. |
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 :(. |
On 15/08/2011, at 23:39, R. David Murray wrote:
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).
That is a pity :(
OK.. I am only using 2.7 so I can't try that :)
|
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. |
On 16/08/2011, at 1:06, R. David Murray wrote:
OK. I did try this and it seems broken though.. In [21]: now.replace(tzinfo = pytz.utc) In [22]: datetime.datetime.strftime(now, "%s") In [23]: now [ur 8:22] ~ >date -ujr 1313414653 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)
|
OK. At a minimum there is a doc issue here, so I'm reopening. |
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. |
On 17/08/2011, at 10:30, Alexander Belopolsky wrote:
Hmm I see, it would appear the problem lies with strftime(). [ur 10:34] ~ >ipython-2.7 In [48]: now = datetime.datetime.utcnow() Wed 17 Aug 2011 01:54:52 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 :) |
Yes, strftime('%s') ignores tzinfo at the moment. This is not a bug. Support for '%s' format code is incidental and not documented in Python. Nevertheless I think this is a good feature request. I am changing the title to make it more explicit. |
On 17/08/2011, at 12:42, Alexander Belopolsky wrote:
OK thanks! |
I made a patch for datetime.strftime('%s'). it takes tzinfo into consideration. >>> datetime.datetime(1970,1,1).strftime("%s")
'-7200'
>>> datetime.datetime(1970,1,1, tzinfo=datetime.timezone.utc).strftime("%s")
'0'
datetime.date still behave as naive datetime.datetime
>>> datetime.date(1970,1,1).strftime("%s")
'-7200' |
I would like to hear from others on this feature. One concern that I have is whether it is wise to truncate the fractional seconds part in '%s'. Also, if we support '%s' in strftime we should probably support it in strptime as well. |
*If* the support for %s strftime format code is added then it should Currently, datetime.strftime delegates to a platform strftime(3) for
%s is not defined in C, POSIX but is already defined on Linux, BSD 2
Unsupported format code is *undefined behavior* (crash, launch a Support for additional codes on some platforms is explicitly mentioned
'%d' produces the wrong rounding on my machine: >>> from datetime import datetime, timezone
>>> dt = datetime(1969, 1, 1, 0,0,0, 600000, tzinfo=timezone.utc)
>>> '%d' % dt.timestamp()
'-31535999'
>>> dt.astimezone().strftime('%s')
'-31536000'
>>> '%d' % math.floor(dt.timestamp())
'-31536000' There is no issue with the round-trip via a float timestamp for >>> import calendar
>>> calendar.timegm(dt.astimezone(timezone.utc).timetuple())
-31536000 Note: dt.utctimetuple() is not used to avoid producing the wrong The result is equivalent to --- It is not clear what the returned value for %s strptime should be:
The result is an aware datetime object in UTC timezone. |
I would start conservatively and require %z to be used with %s. In this case, we can easily produce aware datetime objects. I suspect that in the absence of %z, the most useful option would be to return naive datetime in the local timezone, but that can be added later. |
Naive datetime in the local timezone may lose information that is contained in the input timestamp: >>> import os
>>> import time
>>> from datetime import datetime
>>> import pytz
>>> os.environ['TZ'] = ':America/New_York'
>>> time.tzset()
>>> naive_dt = datetime(2014, 11, 2, 1, 30)
>>> naive_dt.timestamp()
1414906200.0
>>> naive_dt.strftime('%s')
'1414906200'
>>> pytz.timezone('America/New_York').localize(naive_dt, is_dst=False).timestamp()
1414909800.0
>>> pytz.timezone('America/New_York').localize(naive_dt, is_dst=True).timestamp()
1414906200.0
>>> pytz.timezone('America/New_York').localize(naive_dt, is_dst=None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "~/.virtualenvs/py3.4/lib/python3.4/site-packages/pytz/tzinfo.py", line 349, in localize
raise AmbiguousTimeError(dt)
pytz.exceptions.AmbiguousTimeError: 2014-11-02 01:30:00 1414906200 timestamp corresponds to 2014-11-02 01:30:00-04:00 It would be nice if datetime.strptime() would allow the round-trip whatever the local timezone is: >>> ts = '1414906800' it is possible if strptime() returns timezone-aware datetime object. |
I added an improved patch according to akira's explanation for strftime and rounding problem. |
On the second thought, I don't think accepting this should be contingent on any decision with respect to strptime. |
Can you explain why math.floor rather than builtin round is the correct function to use? |
To avoid breaking existing scripts that use >>> from datetime import datetime, timezone
>>> dt = datetime(1969, 1, 1, 0,0,0, 600000, tzinfo=timezone.utc)
>>> '%d' % dt.timestamp()
'-31535999'
>>> round(dt.timestamp())
-31535999
>>> dt.astimezone().strftime('%s') # <-- existing behavior
'-31536000'
>>> '%d' % math.floor(dt.timestamp())
'-31536000'
>>> import calendar
>>> calendar.timegm(dt.astimezone(timezone.utc).timetuple())
-31536000 |
Here is the simpler demonstration of the "floor" behavior on Linux: >>> from datetime import datetime
>>> datetime.fromtimestamp(-0.1).strftime('%s')
'-1'
>>> datetime.fromtimestamp(-1.1).strftime('%s')
'-2'
>>> datetime.fromtimestamp(0.1).strftime('%s')
'0'
>>> datetime.fromtimestamp(1.1).strftime('%s')
'1' |
Could you, please add tests for non-fixed offset timezones? There are several defined in datetimetester.py already. |
The patch should update documentation. See https://docs.python.org/3.5/library/datetime.html#strftime-and-strptime-behavior |
+ t = datetime(1969, 1, 1, 0,0,0, 600000, tzinfo=timezone.utc) Please add spaces after commas. |
more tests and some documentation added. |
strftime('%s') is not portable but it *is* supported on some It would be preferable that datetime.strftime would reject format This issue could be titled: add cross-platform support for %s --- If the implementation uses floats to get an integer result; it should |
bpo-22246 discusses the reverse: strptime('12345', '%s') |
Moving this back to patch needed: the patch was reviewed by a committer and changes requested. |
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. |
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. |
I agree with @AdamWill here, I just ran into this today. |
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:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: