Raeven
Raeven

Reputation: 623

Python: strftime() UTC Offset Not working as Expected in Windows

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

Upvotes: 16

Views: 21240

Answers (5)

enricosuarve
enricosuarve

Reputation: 83

I came here looking for a solution as I needed to output the full date and time including timezone for another program which required it in RFC3339 format https://datatracker.ietf.org/doc/html/rfc3339 (yeah picky right?!) :)

The output from strftime doesn't confirm to this particular standard it appears, so in the end I ended up using:

from datetime import datetime, timezone
print(datetime.now(timezone.utc).astimezone().isoformat())

which outputs the following:

2024-03-06T10:43:00.348657+00:00

I know this isn't quite an answer to the question, but it seemed a sensible place to put it in case anyone is headed down the same rabbit hole as I was :)

Upvotes: 0

Bill Bell
Bill Bell

Reputation: 21643

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'

Upvotes: 0

abarnert
abarnert

Reputation: 365737

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.

Upvotes: 8

poke
poke

Reputation: 387687

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'

Upvotes: 7

Raghav RV
Raghav RV

Reputation: 4076

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.

Upvotes: 4

Related Questions