66

I have a datetime in utc time zone, for example:

utc_time = datetime.datetime.utcnow()

And a pytz timezone object:

tz = timezone('America/St_Johns')

What is the proper way to convert utc_time to the given timezone?

Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
Tzach
  • 12,889
  • 11
  • 68
  • 115
  • related: [How to convert a python utc datetime to a local datetime using only python standard library?](http://stackoverflow.com/q/4563272/4279) – jfs Aug 31 '14 at 17:00

6 Answers6

79

I think I got it:

pytz.utc.localize(utc_time, is_dst=None).astimezone(tz)

This line first converts the naive (time zone unaware) utc_time datetime object to a datetime object that contains a timezone (UTC). Then it uses the astimezone function to adjust the time according to the requested time zone.

Mohammed Shareef C
  • 3,829
  • 25
  • 35
Tzach
  • 12,889
  • 11
  • 68
  • 115
  • 6
    `utc_time.replace(tzinfo=pytz.utc).astimezone(tz)`. Note: `pytz.utc` is a special case (zero utc offset, always). You don't need `localize()` call in this case. If you do keep it (for generality); then add `tz.normalize()` call around the expression ([it might be necessary if the source timezone is not UTC](http://pytz.sourceforge.net/#localized-times-and-date-arithmetic)). – jfs Aug 31 '14 at 16:40
  • @J.F.Sebastian the call to `localize` is needed because `utc_time` doesn't contain timezone information. Without it `astimezone` won't work. – Tzach Sep 01 '14 at 06:51
  • 2
    @Tzach what do you think the `replace()` call does in my previous comment? – jfs Sep 01 '14 at 07:27
  • It works also for utc_timestamp as int with datetime.utcfromtimestamp(utc_timestamp), which returns a timezone not aware datetime object. But utc_time.replace doesn't do the time schift and only add a tzinfo. – Yingding Wang May 08 '19 at 16:56
  • @YingdingWang The question from almost 5 years ago is not using `utcfromtimestamp`. Also `datetime.datetime.utcnow()` sets tzinfo as None. – CrackerJack9 Jul 13 '19 at 22:48
  • `localize` is about 7% slower than `replace` (which is also the example used in the docs) – CrackerJack9 Jul 13 '19 at 22:49
  • Use `fromutc` (see [@user8808265's answer](https://stackoverflow.com/a/46979496/4653485)). – Jérôme Mar 15 '21 at 15:16
28

It's the exact purpose of fromutc function:

tz.fromutc(utc_time)

(astimezone function calls fromutc under the hood, but tries to convert to UTC first, which is unneeded in your case)

user8808265
  • 1,893
  • 1
  • 17
  • 25
12

I agree with Tzach's answer. Just wanted to include that the is_dst parameter is not required:

pytz.utc.localize(datetime.utcnow()).astimezone(tz)

That code converts the current UTC time to a timezone aware current datetime.

Whereas the code below converts the current UTC time to a timezone aware datetime which is not necessarily current. The timezone is just appended into the UTC time value.

tz.localize(datetime.utcnow())
paolov
  • 2,139
  • 1
  • 34
  • 43
  • 2
    If you already have a timezone-aware datetime, then you just need to use `astimezone`: `dt.astimezone(tz)`. See also [this answer](https://stackoverflow.com/a/18862958/648162) and the [pytz docs](https://pypi.python.org/pypi/pytz). – qris Apr 06 '18 at 08:35
  • 2
    user8808265's answer is much simpler. – Rufflewind May 06 '19 at 01:33
8

May I recommend to use arrow? If I understood the question:

>>> import arrow
>>> utc = arrow.utcnow()
>>> utc
<Arrow [2014-08-12T13:01:28.071624+00:00]>    
>>> local = utc.to("America/St_Johns")
>>> local
<Arrow [2014-08-12T10:31:28.071624-02:30]>

You can also use

tz.fromutc(utc_time)
xbello
  • 7,223
  • 3
  • 28
  • 41
  • 4
    Thank you. This library indeed looks as a good alternative to `datetime`, however my project is big and I don't want to create more confusion by using another date-time library. I will definitely consider using it in other projects. – Tzach Aug 12 '14 at 13:24
4

Another very easy way:

Because utcnow method returns a naive object, so you have to convert the naive object into aware object. Using replace method you can convert a naive object into aware object. Then you can use the astimezone method to create new datetime object in a different time zone.

from datetime import datetime
import pytz    
utc_time = datetime.utcnow()
tz = pytz.timezone('America/St_Johns')

utc_time =utc_time.replace(tzinfo=pytz.UTC) #replace method      
st_john_time=utc_time.astimezone(tz)        #astimezone method
print(st_john_time)
N Randhawa
  • 8,773
  • 3
  • 43
  • 47
0

You can also use the sample below, I use it for similar task

tz = pytz.timezone('America/St_Johns')
time_difference=tz.utcoffset(utc_time).total_seconds() #time difference between UTC and local timezones in 5:30:00 format
utc_time = date + timedelta(0,time_difference)

It works fast and you don't need to import additional libraries.

awaik
  • 10,143
  • 2
  • 44
  • 50
  • This is wrong for timezones with daylight savings, because it doesn't account for them. See [this answer](https://stackoverflow.com/a/18862958/648162). – qris Apr 06 '18 at 08:36