59

Let's say I have a variable t that's set to this:

datetime.datetime(2009, 7, 10, 18, 44, 59, 193982, tzinfo=<UTC>)

If I say str(t), i get:

'2009-07-10 18:44:59.193982+00:00'

How can I get a similar string, except printed in the local timezone rather than UTC?

martineau
  • 119,623
  • 25
  • 170
  • 301
mike
  • 46,876
  • 44
  • 102
  • 112
  • related: [How to convert a python utc datetime to a local datetime using only python standard library?](http://stackoverflow.com/a/13287083/4279) – jfs Dec 03 '13 at 13:53
  • 4
    Python >= 3.6: use `astimezone()`, e.g. `datetime.datetime(2010, 4, 27, 12, 0, 0, 0).astimezone().isoformat()` -> `'2010-04-27T12:00:00+02:00'` – FObersteiner Jun 12 '20 at 10:45

6 Answers6

74

As of python 3.6 calling astimezone() without a timezone object defaults to the local zone (docs). This means you don't need to import tzlocal and can simply do the following:

#!/usr/bin/env python3

from datetime import datetime, timezone

utc_dt = datetime.now(timezone.utc)

print("Local time {}".format(utc_dt.astimezone().isoformat()))

This script demonstrates a few other ways to show the local timezone using astimezone():

#!/usr/bin/env python3

import pytz
from datetime import datetime, timezone
from tzlocal import get_localzone

utc_dt = datetime.now(timezone.utc)

PST = pytz.timezone("US/Pacific")
EST = pytz.timezone("US/Eastern")
JST = pytz.timezone("Asia/Tokyo")
NZST = pytz.timezone("Pacific/Auckland")

print("Pacific time {}".format(utc_dt.astimezone(PST).isoformat()))
print("Eastern time {}".format(utc_dt.astimezone(EST).isoformat()))
print("UTC time     {}".format(utc_dt.isoformat()))
print("Japan time   {}".format(utc_dt.astimezone(JST).isoformat()))

# Use astimezone() without an argument
print("Local time   {}".format(utc_dt.astimezone().isoformat()))

# Use tzlocal get_localzone
print("Local time   {}".format(utc_dt.astimezone(get_localzone()).isoformat()))

# Explicitly create a pytz timezone object
# Substitute a pytz.timezone object for your timezone
print("Local time   {}".format(utc_dt.astimezone(NZST).isoformat()))

It outputs the following:

$ ./timezones.py 
Pacific time 2019-02-22T17:54:14.957299-08:00
Eastern time 2019-02-22T20:54:14.957299-05:00
UTC time     2019-02-23T01:54:14.957299+00:00
Japan time   2019-02-23T10:54:14.957299+09:00
Local time   2019-02-23T14:54:14.957299+13:00
Local time   2019-02-23T14:54:14.957299+13:00
Local time   2019-02-23T14:54:14.957299+13:00
htaccess
  • 2,800
  • 26
  • 31
41

Think your should look around: datetime.astimezone()

http://docs.python.org/library/datetime.html#datetime.datetime.astimezone

Also see pytz module - it's quite easy to use -- as example:

eastern = timezone('US/Eastern')

http://pytz.sourceforge.net/

Example:

from datetime import datetime
import pytz
from tzlocal import get_localzone # $ pip install tzlocal

utc_dt = datetime(2009, 7, 10, 18, 44, 59, 193982, tzinfo=pytz.utc)
print(utc_dt.astimezone(get_localzone())) # print local time
# -> 2009-07-10 14:44:59.193982-04:00
jfs
  • 399,953
  • 195
  • 994
  • 1,670
Mikhail Churbanov
  • 4,436
  • 1
  • 28
  • 36
13

I believe the best way to do this is to use the LocalTimezone class defined in the datetime.tzinfo documentation (goto http://docs.python.org/library/datetime.html#tzinfo-objects and scroll down to the "Example tzinfo classes" section):

Assuming Local is an instance of LocalTimezone

t = datetime.datetime(2009, 7, 10, 18, 44, 59, 193982, tzinfo=utc)
local_t = t.astimezone(Local)

then str(local_t) gives:

'2009-07-11 04:44:59.193982+10:00'

which is what you want.

(Note: this may look weird to you because I'm in New South Wales, Australia which is 10 or 11 hours ahead of UTC)

HughE
  • 523
  • 1
  • 4
  • 10
  • 1
    If you've defined Local per the datetime docs, this also works: `str(datetime.datetime.now(tz=Local))` – hobs Apr 01 '12 at 07:15
  • Be aware that this will print any use with the UTC offset that is currently in effect, which may not be the same that was or will be in effect at the time given by the `datetime` instance (due to DST). – Feuermurmel Sep 15 '13 at 11:56
  • `LocalTimezone` might fail for past dates if underlying `time` implementation doesn't use a historical timezone database (Windows is notably in this category). – jfs Dec 03 '13 at 13:40
  • 1
    I think the argument to astimezone should be empty: `t.astimezone()` – wordsforthewise Jul 12 '18 at 05:02
4

As of python 3.2, using only standard library functions:

u_tm = datetime.datetime.utcfromtimestamp(0)
l_tm = datetime.datetime.fromtimestamp(0)
l_tz = datetime.timezone(l_tm - u_tm)

t = datetime.datetime(2009, 7, 10, 18, 44, 59, 193982, tzinfo=l_tz)
str(t)
'2009-07-10 18:44:59.193982-07:00'

Just need to use l_tm - u_tm or u_tm - l_tm depending whether you want to show as + or - hours from UTC. I am in MST, which is where the -07 comes from. Smarter code should be able to figure out which way to subtract.

And only need to calculate the local timezone once. That is not going to change. At least until you switch from/to Daylight time.

mMerlin
  • 109
  • 7
  • This doesn't take DST into account. – Beefster Oct 19 '20 at 18:10
  • In base setup, it uses the delta between UTC and local for the epoch date. Anything else can not be 'automatic'. When displaying the final time, do you want to use DST if the *current* date is DST, or if the displayed date is/was/will be DST? So that needs to be determined by the surrounding program logic, and l_tz adjusted when appropriate. Possibly by taking a similar delta between Jan 1 and either the current or target date for the current or target year. – mMerlin Oct 20 '20 at 21:43
0

I wrote something like this the other day:

import time, datetime
def nowString():
    # we want something like '2007-10-18 14:00+0100'
    mytz="%+4.4d" % (time.timezone / -(60*60) * 100) # time.timezone counts westwards!
    dt  = datetime.datetime.now()
    dts = dt.strftime('%Y-%m-%d %H:%M')  # %Z (timezone) would be empty
    nowstring="%s%s" % (dts,mytz)
    return nowstring

So the interesting part for you is probably the line starting with "mytz=...". time.timezone returns the local timezone, albeit with opposite sign compared to UTC. So it says "-3600" to express UTC+1.

Despite its ignorance towards Daylight Saving Time (DST, see comment), I'm leaving this in for people fiddling around with time.timezone.

ThomasH
  • 22,276
  • 13
  • 61
  • 62
  • 2
    -1. This will return a wrong result half of the time, if you're in a timezone that uses DST. `time.timezone` contain the offset to UTC that is used when DST is not in effect, regardless of whether it currently is in effect. – Feuermurmel Sep 15 '13 at 11:49
0

I use this function datetime_to_local_timezone(), which seems overly convoluted but I found no simpler version of a function that converts a datetime instance to the local time zone, as configured in the operating system, with the UTC offset that was in effect at that time:

import time, datetime

def datetime_to_local_timezone(dt):
    epoch = dt.timestamp() # Get POSIX timestamp of the specified datetime.
    st_time = time.localtime(epoch) #  Get struct_time for the timestamp. This will be created using the system's locale and it's time zone information.
    tz = datetime.timezone(datetime.timedelta(seconds = st_time.tm_gmtoff)) # Create a timezone object with the computed offset in the struct_time.

    return dt.astimezone(tz) # Move the datetime instance to the new time zone.

utc = datetime.timezone(datetime.timedelta())
dt1 = datetime.datetime(2009, 7, 10, 18, 44, 59, 193982, utc) # DST was in effect
dt2 = datetime.datetime(2009, 1, 10, 18, 44, 59, 193982, utc) # DST was not in effect

print(dt1)
print(datetime_to_local_timezone(dt1))

print(dt2)
print(datetime_to_local_timezone(dt2))

This example prints four dates. For two moments in time, one in January and one in July 2009, each, it prints the timestamp once in UTC and once in the local time zone. Here, where CET (UTC+01:00) is used in the winter and CEST (UTC+02:00) is used in the summer, it prints the following:

2009-07-10 18:44:59.193982+00:00
2009-07-10 20:44:59.193982+02:00

2009-01-10 18:44:59.193982+00:00
2009-01-10 19:44:59.193982+01:00
Feuermurmel
  • 9,490
  • 10
  • 60
  • 90
  • 3
    It looks like Python 3.3+ code (`.timestamp()` method) In this case you could use a simpler code: [`dt.astimezone(tz=None)`](http://stackoverflow.com/a/13287083/4279). Also use `timezone.utc` instead of `timezone(datetime.timedelta())`. – jfs Dec 03 '13 at 14:01