Python Timezone Conversion

How to convert a UTC datetime to a local datetime using only standard library?

I think I figured it out: computes number of seconds since epoch, then converts to a local timzeone using time.localtime, and then converts the time struct back into a datetime...

EPOCH_DATETIME = datetime.datetime(1970,1,1)
SECONDS_PER_DAY = 24*60*60

def utc_to_local_datetime( utc_datetime ):
delta = utc_datetime - EPOCH_DATETIME
utc_epoch = SECONDS_PER_DAY * delta.days + delta.seconds
time_struct = time.localtime( utc_epoch )
dt_args = time_struct[:6] + (delta.microseconds,)
return datetime.datetime( *dt_args )

It applies the summer/winter DST correctly:

>>> utc_to_local_datetime( datetime.datetime(2010, 6, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 6, 6, 19, 29, 7, 730000)
>>> utc_to_local_datetime( datetime.datetime(2010, 12, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 12, 6, 18, 29, 7, 730000)

Python (datetime) timezone conversion off by 4 minutes

From pytz documentation:

This library differs from the documented Python API for tzinfo implementations; if you want to create local wallclock times you need to use the localize() method documented in this document. In addition, if you perform date arithmetic on local times that cross DST boundaries, the result may be in an incorrect timezone (ie. subtract 1 minute from 2002-10-27 1:00 EST and you get 2002-10-27 0:59 EST instead of the correct 2002-10-27 1:59 EDT).

So, you are using pytz incorrectly.

Following is both correct, and erroneous code Following code shows results of your use of pytz (datetime.replace(tzinfo=pytz.timezone)), and the recommended way of using pytz with datetime (pytz.timezone.localize(datetime)).

from datetime import datetime, date, time, timezone
from dateutil import tz
import pytz

d = date(2019, 1, 27)
t = time(19, 32, 00)

t1 = datetime.combine(d, t)
t1_epoch = t1.timestamp()
print("t1_epoch " + str(t1_epoch))
print("t1 " + str(t1))

# your approach/code
nytz = pytz.timezone('America/New_York')
t3 = t1.replace(tzinfo=nytz)
t3_epoch = t3.timestamp()
print("t3_epoch " + str(t3_epoch))
print("t3 " + str(t3))

# recommended approach/code using localize
nytz = pytz.timezone('America/New_York')
t6 = nytz.localize(t1)
t6_epoch = t6.timestamp()
print("t6_epoch " + str(t6_epoch))
print("t6 " + str(t6))

Output of above code:

t1_epoch 1548617520.0
t1 2019-01-27 19:32:00
t3_epoch 1548635280.0
t3 2019-01-27 19:32:00-04:56
t6_epoch 1548635520.0
t6 2019-01-27 19:32:00-05:00

t3 is what you are doing, and it is giving incorrect offset (-4:56). Note that POSIX time is also incorrect in this case. POSIX time, by definition, does not change with timezone.

t6 has been created using pytz.timezone.localize() method, and gives correct UTC offset (-5:00).

Update: Updated language of the answer as one user found the answer confusing.

How do you convert a datetime/timestamp from one timezone to another timezone?

If you know your origin timezone and the new timezone that you want to convert it to, it turns out to be very straightforward:

  1. Make two pytz.timezone objects, one for the current timezone and one for the new timezone e.g. pytz.timezone("US/Pacific"). You can find a list of all official timezones in pytz library: import pytz; pytz.all_timezones

  2. Localize the datetime/timestamp of interest to the current timezone e.g.

current_timezone = pytz.timezone("US/Eastern")
localized_timestamp = current_timezone.localize(timestamp)

  1. Convert to new timezone using .astimezone() on the newly localized datetime/timestamp from step 2 with the desired timezone's pytz object as input e.g. localized_timestamp.astimezone(new_timezone).

Done!

As a full example:

import datetime
import pytz

# a timestamp I'd like to convert
my_timestamp = datetime.datetime.now()

# create both timezone objects
old_timezone = pytz.timezone("US/Eastern")
new_timezone = pytz.timezone("US/Pacific")

# two-step process
localized_timestamp = old_timezone.localize(my_timestamp)
new_timezone_timestamp = localized_timestamp.astimezone(new_timezone)

# or alternatively, as an one-liner
new_timezone_timestamp = old_timezone.localize(my_timestamp).astimezone(new_timezone)

Bonus: but if all you need is the current time in a specific timezone, you can conveniently pass that timezone directly into datetime.now() to get the current times directly:

datetime.datetime.now(new_timezone)

When it comes to needing timezones conversions generally, I would strongly advise that one should store all timestamps in your database in UTC, which has no daylight savings time (DST) transition. And as a good practice, one should always choose to enable time zone support (even if your users are all in a single time zone!). This will help you avoid the DST transition problem that plagues so much software today.

Beyond DST, time in software can be generally quite tricky. To get a sense of just how difficult it is to deal with time in software in general, here is a potentially enlightening resource: http://yourcalendricalfallacyis.com

Even a seemingly simple operation as converting a datetime/timestamp into a date can become non-obvious. As this helpful documentation points out:

A datetime represents a point in time. It’s absolute: it doesn’t depend on anything. On the contrary, a date is a calendaring concept. It’s a period of time whose bounds depend on the time zone in which the date is considered. As you can see, these two concepts are fundamentally different.

Understanding this difference is a key step towards avoiding time-based bugs. Good luck.

Conversion of timezone of datetime.time

add an appropriate date to the input string and this works as you expect:

import arrow

start = '2021-02-01 18:30+0000'
start_time = arrow.get(start)
print(start_time.to('Australia/Melbourne').format('hh:mm A ZZ'))
# 05:30 AM +11:00

Python time zone conversion from UTC to EST

you need to set UTC first, then convert to the desired tz:

from datetime import datetime, timezone
from zoneinfo import ZoneInfo # Python 3.9+ standard lib

l = [['File_1','2021-09-09 07:05:10'],['File_2','2021-09-08 08:05:11']]

out = [[i[0], datetime.fromisoformat(i[1]) # Python 3.7+
.replace(tzinfo=timezone.utc)
.astimezone(ZoneInfo("America/New_York"))] for i in l]

print(out)
# [['File_1', datetime.datetime(2021, 9, 9, 3, 5, 10, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))], ['File_2', datetime.datetime(2021, 9, 8, 4, 5, 11, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))]]

# with pytz:
import pytz

outp = [[i[0], datetime.fromisoformat(i[1]) # Python 3.7+
.replace(tzinfo=timezone.utc)
.astimezone(pytz.timezone("America/New_York"))] for i in l]
print(outp)
# [['File_1', datetime.datetime(2021, 9, 9, 3, 5, 10, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)], ['File_2', datetime.datetime(2021, 9, 8, 4, 5, 11, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)]]

If you want a string instead of datetime object, use strftime or even simpler: .isoformat().

Python: Convert local time to another time zone

You can use the datetime module instead. Adapted from http://docs.python.org/library/datetime.html#datetime.tzinfo.fromutc

from datetime import tzinfo, timedelta, datetime

class FixedOffset(tzinfo):
def __init__(self, offset):
self.__offset = timedelta(hours=offset)
self.__dst = timedelta(hours=offset-1)
self.__name = ''

def utcoffset(self, dt):
return self.__offset

def tzname(self, dt):
return self.__name

def dst(self, dt):
return self.__dst

print datetime.now()
print datetime.now(FixedOffset(9))

Gives:

2011-03-12 00:28:32.214000
2011-03-12 14:28:32.215000+09:00

When I run it (I'm UTC-0500 for another day, then DST begins)

How to convert local time string to UTC?

Thanks @rofly, the full conversion from string to string is as follows:

time.strftime("%Y-%m-%d %H:%M:%S", 
time.gmtime(time.mktime(time.strptime("2008-09-17 14:04:00",
"%Y-%m-%d %H:%M:%S"))))

My summary of the time/calendar functions:

time.strptime
string --> tuple (no timezone applied, so matches string)

time.mktime
local time tuple --> seconds since epoch (always local time)

time.gmtime
seconds since epoch --> tuple in UTC

and

calendar.timegm
tuple in UTC --> seconds since epoch

time.localtime
seconds since epoch --> tuple in local timezone



Related Topics



Leave a reply



Submit