16

Every time I use:

time.strftime("%z")

I get:

Eastern Daylight Time

However, I would like the UTC offset in the form +HHMM or -HHMM. I have even tried:

time.strftime("%Z")

Which still yields:

Eastern Daylight Time

I have read several other posts related to strftime() and %z always seems to return the UTC offset in the proper +HHMM or -HHMM format. How do I get strftime() to output in the +HHMM or -HHMM format for python 3.3?

Edit: I'm running Windows 7

jfs
  • 399,953
  • 195
  • 994
  • 1,670
Raeven
  • 613
  • 2
  • 8
  • 18
  • 1
    I can't reproduce what you've observed: `In [2]: time.strftime("%z") Out[2]: '-0500'`. Can you give us more details? What you're saying [contradicts the documentation](https://docs.python.org/3.4/library/time.html#time.strftime). – Patrick Collins Aug 07 '14 at 16:32
  • 1
    @PatrickCollins I’m also not getting an offset but `'Mitteleuropäische Sommerzeit'` instead (Windows 8, Python 2.7, 3.3, and 3.4). – poke Aug 07 '14 at 16:37
  • I'm getting similar results on Windows 7 `time.strftime("%z") -> "Eastern Daylight Time"` for Python 2.6.6, 2.7.2, 3.2.2, and 3.3.2. – Uyghur Lives Matter Aug 07 '14 at 17:13
  • 3
    Unfortunately the libraries under Windows don't support `%z`, and Python relies on those libraries. See http://msdn.microsoft.com/en-us/library/fe06s4ak.aspx – Mark Ransom Aug 07 '14 at 17:21
  • @MarkRansom So this looks like a documentation error on Python's part that `%z` does not work properly on Windows. – Uyghur Lives Matter Aug 07 '14 at 17:24
  • @cpburnz True, there should at least be a note saying you might get different results on different platforms. – Mark Ransom Aug 07 '14 at 17:26
  • 3
    Window's incorrect result for `%z` is an open bug: http://bugs.python.org/issue20010 – Uyghur Lives Matter Aug 07 '14 at 17:33
  • The bug is basically arguing for changing the documentation to better match the facts. (Also, for those interested in 2.x, the docs don't even mention `%z`, so the issue doesn't arise in the first place.) – abarnert Oct 14 '14 at 20:40
  • What answer do you actually want here? Eastern Time is defined as -04:56, with a 4-minute correction in 1920, and of course yearly corrections back and forth an hour as DST begins and ends. So, do you want -04:56, or -05:00, or either -05:00 or -04:00 depending on whether the program is being run during DST, or any of the above depending on which offset was in effect on a particular date, or…? – abarnert Oct 16 '14 at 18:23

4 Answers4

8

In 2.x, if you look at the docs for time.strftime, they don't even mention %z. It's not guaranteed to exist at all, much less to be consistent across platforms. In fact, as footnote 1 implies, it's left up to the C strftime function. In 3.x, on the other hand, they do mention %z, and the footnote that explains that it doesn't work the way you'd expect is not easy to see; that's an open bug.

However, in 2.6+ (including all 3.x versions), datetime.strftime is guaranteed to support %z as "UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive)." So, that makes for a pretty easy workaround: use datetime instead of time. Exactly how to change things depends on what exactly you're trying to do — using Python-dateutil tz then datetime.now(tz.tzlocal()).strftime('%z') is the way to get just the local timezone formatted as a GMT offset, but if you're trying to format a complete time the details will be a little different.

If you look at the source, time.strftime basically just checks the format string for valid-for-the-platform specifiers and calls the native strftime function, while datetime.strftime has a bunch of special handling for different specifiers, including %z; in particular, it will replace the %z with a formatted version of utcoffset before passing things on to strftime. The code has changed a few times since 2.7, and even been radically reorganized once, but the same difference is basically there even in the pre-3.5 trunk.

Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 1
    On Python 3, using only stdlib: `datetime.now(timezone.utc).astimezone().strftime('%z')` – jfs Oct 16 '14 at 08:42
7

For a proper solution, see abarnert’s answer below.


You can use time.altzone which returns a negative offset in seconds. For example, I’m on CEST at the moment (UTC+2), so I get this:

>>> time.altzone
-7200

And to put it in your desired format:

>>> '{}{:0>2}{:0>2}'.format('-' if time.altzone > 0 else '+', abs(time.altzone) // 3600, abs(time.altzone // 60) % 60)
'+0200'

As abarnert mentioned in the comments, time.altzone gives the offset when DST is active while time.timezone does for when DST is not active. To figure out which to use, you can do what J.F. Sebastian suggested in his answer to a different question. So you can get the correct offset like this:

time.altzone if time.daylight and time.localtime().tm_isdst > 0 else time.timezone

As also suggested by him, you can use the following in Python 3 to get the desired format using datetime.timezone:

>>> datetime.now(timezone.utc).astimezone().strftime('%z')
'+0200'
Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602
  • This is a good answer. I edited because your code had some syntax errors. I think it is also worth pointing out MarkRansom's comment: "Unfortunately the libraries under Windows don't support %z, and Python relies on those libraries. See msdn.microsoft.com/en-us/library/fe06s4ak.aspx" – Raeven Aug 07 '14 at 17:26
  • @Raeven Your attempted edit did not fix anything and was at such rejected. `//` is integer division and a valid operation in Python. Your edit broke the code to produce the desired output. – poke Aug 07 '14 at 17:44
  • That's odd. My edit runs fine for me while your code fails. However, I'm running Python 3. You are running Python 2 I guess? – Raeven Aug 07 '14 at 17:47
  • @Raeven No, it works on both Python 3 and 2. What error do you get? – poke Aug 07 '14 at 17:50
  • I'll have to take that back. It's working for me now. I must not have fully copied your text the first time. – Raeven Aug 07 '14 at 17:53
  • [`altzone`](https://docs.python.org/3.3/library/time.html#time.altzone) is "The offset of the local DST timezone, in seconds west of UTC, if one is defined. This is negative if the local DST timezone is east of UTC (as in Western Europe, including the UK). Only use this if daylight is nonzero." Of course if you're specifically asking for "Eastern Daylight Time" rather than "Eastern Time", this is the right answer, but in general, it's going to be wrong half the year in timezones with DST and useless in timezones without. – abarnert Oct 14 '14 at 20:46
  • 1. `time.daylight` does *not* say whether DST is in effect right now. 2. the whole approach might fail in some cases. To resolve both issues, see [my answer to "Getting computer's utc offset in Python"](http://stackoverflow.com/q/3168096/4279). – jfs Oct 16 '14 at 08:45
  • @J.F.Sebastian: Right, `time.daylight` says whether DST exists (or ever existed) in the timezone. If it's true, you don't know whether to use `altzone` or `timezone` unless you specify which date you're asking about. (Which isn't surprising, given that the question is meaningless unless you know which date you're asking about.) – abarnert Oct 16 '14 at 18:14
  • @abarnert: a) *"ever"* is incorrect: `daylight` may be zero for a timezone even it had observed DST in the past b) as I said the whole [daylight] approach is not flexible enough: [*"For years where the rules change, these constants can provide incorrect data."*](http://bugs.python.org/issue1647654), for example, see [mercurial bug mentioned there](http://bz.selenic.com/show_bug.cgi?id=2511). For the reliable way to get utc offset, see [my answer (with `pytz`)](http://stackoverflow.com/a/3168394/4279) c) the date is implied. `time.strftime("%z")` in the question works with the current time. – jfs Oct 16 '14 at 18:47
  • @poke: use `tm_isdst > 0` test. In general, `tm_isdst` can be less than zero. – jfs Oct 16 '14 at 18:48
  • @J.F.Sebastian: I was agreeing with you that "the whole approach is not flexible enough". I said as much in my comment yesterday. – abarnert Oct 16 '14 at 18:55
  • @abarnert I just linked to your answer now considering that it covers the problem better than mine. – poke Oct 16 '14 at 18:59
4

Use time.timezone to get the time offset in seconds.

Format it using :

("-" if time.timezone > 0 else "+") + time.strftime("%H:%M", time.gmtime(abs(time.timezone)))

to convert the same to +/-HH:MM format.

BTW isn't this supposed to be a bug ? According to strftime docs.

Also I thought this SO answer might help you to convert from Zone offset string to HH:MM format. But since "%z" is not working as expected, I feel its moot.

NOTE: The time.timezone is immune to Daylight savings.

Community
  • 1
  • 1
Raghav RV
  • 3,938
  • 2
  • 22
  • 27
  • Considering that the question asks about the offset for Eastern Daylight Time, I don't think he wants "immune to daylight savings". (Unfortunately, I think the question is underspecified as to what he actually _does_ want, but I don't think this is it.) – abarnert Oct 16 '14 at 18:17
0

It will come as no surprise that this bug persists in, what is the latest Windows version available currently, Win 10 Version 1703 (Creators). However, time marches on and there is a lovely date-and-time library called pendulum that does what the question asks for. Sébastien Eustace (principal author of the product?) has shown me this.

>>> pendulum.now().strftime('%z')
'-0400'

pendulum assumes UTC/GMT unless told otherwise, and keeps timezone with the date-time object. There are many other possibilities, amongst them these:

>>> pendulum.now(tz='Europe/Paris').strftime('%z')
'+0200'
>>> pendulum.create(year=2016, month=11, day=5, hour=16, minute=23, tz='America/Winnipeg').strftime('%z')
'-0500'
>>> pendulum.now(tz='America/Winnipeg').strftime('%z')
'-0500'
Bill Bell
  • 21,021
  • 5
  • 43
  • 58