仅使用python标准库将python UTC日期时间转换为本地日期时间?

Convert a python UTC datetime to a local datetime using only python standard library?

我有一个使用datetime.utcnow()创建并持久化在数据库中的python datetime实例。

为了进行显示,我想使用默认的本地时区(即,如果日期时间是使用datetime.now()创建的),将从数据库中检索到的日期时间实例转换为本地日期时间。

如何仅使用Python标准库(例如,不依赖pytz)将UTC日期时间转换为本地日期时间?

似乎有一种解决方案是使用datetime.astimezone(tz),但是如何获得默认的本地时区?


在python 3.3+中:

1
2
3
4
from datetime import datetime, timezone

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)

在python 2/3中:

1
2
3
4
5
6
7
8
9
import calendar
from datetime import datetime, timedelta

def utc_to_local(utc_dt):
    # get integer timestamp to avoid precision lost
    timestamp = calendar.timegm(utc_dt.timetuple())
    local_dt = datetime.fromtimestamp(timestamp)
    assert utc_dt.resolution >= timedelta(microseconds=1)
    return local_dt.replace(microsecond=utc_dt.microsecond)

使用pytz(两个python 2/3):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pytz

local_tz = pytz.timezone('Europe/Moscow') # use your local timezone name here
# NOTE: pytz.reference.LocalTimezone() would produce wrong result here

## You could use `tzlocal` module to get local timezone on Unix and Win32
# from tzlocal import get_localzone # $ pip install tzlocal

# # get local timezone    
# local_tz = get_localzone()

def utc_to_local(utc_dt):
    local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
    return local_tz.normalize(local_dt) # .normalize might be unnecessary

。例子

1
2
3
4
5
6
def aslocaltimestr(utc_dt):
    return utc_to_local(utc_dt).strftime('%Y-%m-%d %H:%M:%S.%f %Z%z')

print(aslocaltimestr(datetime(2010,  6, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime(2010, 12, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime.utcnow()))

。产量Python3.3

1
2
3
2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.093745 MSK+0400

Python2

1
2
3
2010-06-06 21:29:07.730000
2010-12-06 20:29:07.730000
2012-11-08 14:19:50.093911

。吡兹

1
2
3
2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.146917 MSK+0400

注:它考虑了MSK时区的DST和最近的UTC偏移量变化。

我不知道非pytz解决方案是否适用于Windows。


你不能只使用标准库,因为标准库没有任何时区。你需要Pytz或Dateutil。

1
2
3
4
5
6
7
8
9
10
>>> from datetime import datetime
>>> now = datetime.utcnow()
>>> from dateutil import tz
>>> HERE = tz.tzlocal()
>>> UTC = tz.gettz('UTC')

The Conversion:
>>> gmt = now.replace(tzinfo=UTC)
>>> gmt.astimezone(HERE)
datetime.datetime(2010, 12, 30, 15, 51, 22, 114668, tzinfo=tzlocal())

或者,通过实现自己的时区,您可以在没有pytz或dateutil的情况下完成它。但那是愚蠢的。


我想我知道了:计算出从epoch开始的秒数,然后使用time.localtime转换为本地timzeone,然后将time结构转换回datetime…

1
2
3
4
5
6
7
8
9
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 )

它正确应用夏/冬DST:

1
2
3
4
>>> 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)


你不能用标准库。使用pytz模块,您可以将任何naive/aware日期时间对象转换为任何其他时区。让我们看一些使用python 3的例子。

Naive objects created through class method utcnow()

要将一个幼稚的对象转换为任何其他时区,首先必须将其转换为感知的日期时间对象。可以使用replace方法将原始日期时间对象转换为已知的日期时间对象。然后,要将感知的日期时间对象转换为任何其他时区,可以使用astimezone方法。

变量pytz.all_timezones给出了pytz模块中所有可用时区的列表。

1
2
3
4
5
6
7
8
9
import datetime,pytz

dtobj1=datetime.datetime.utcnow()   #utcnow class method
print(dtobj1)

dtobj3=dtobj1.replace(tzinfo=pytz.UTC) #replace method

dtobj_hongkong=dtobj3.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)

Naive objects created through class method now()

因为now方法返回当前的日期和时间,所以必须首先使date time对象timezone知道。localize函数将一个简单的datetime对象转换为一个时区感知的datetime对象。然后可以使用astimezone方法将其转换为另一个时区。

1
2
3
4
5
6
7
dtobj2=datetime.datetime.now()

mytimezone=pytz.timezone("Europe/Vienna") #my current timezone
dtobj4=mytimezone.localize(dtobj2)        #localize function

dtobj_hongkong=dtobj4.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)


基于阿列克谢的评论。这也应该适用于DST。

1
2
3
4
5
6
7
8
import time
import datetime

def utc_to_local(dt):
    if time.localtime().tm_isdst:
        return dt - datetime.timedelta(seconds = time.altzone)
    else:
        return dt - datetime.timedelta(seconds = time.timezone)


标准的python库根本不附带任何tzinfo实现。我一直认为这是datetime模块的一个令人惊讶的缺点。

TZINFO类的文档确实提供了一些有用的示例。在本节末尾查找大型代码块。


这是一种糟糕的方法,但它避免了创建定义。它符合基本python3库的要求。

1
2
3
4
5
6
# Adjust from UST to Eastern Standard Time (dynamic)
# df.my_localtime should already be in datetime format, so just in case
df['my_localtime'] = pd.to_datetime.df['my_localtime']

df['my_localtime'] = df['my_localtime'].dt.tz_localize('UTC').dt.tz_convert('America/New_York').astype(str)
df['my_localtime'] = pd.to_datetime(df.my_localtime.str[:-6])


这里有另一种方法可以改变datetime格式的时区(我知道我在这上面浪费了精力,但是我没有看到这个页面,所以我不知道怎么做),不需要分秒。因为我的项目不需要它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def change_time_zone(year, month, day, hour):
      hour = hour + 7 #<-- difference
      if hour >= 24:
        difference = hour - 24
        hour = difference
        day += 1
        long_months = [1, 3, 5, 7, 8, 10, 12]
        short_months = [4, 6, 9, 11]
        if month in short_months:
          if day >= 30:
            day = 1
            month += 1
            if month > 12:
              year += 1
        elif month in long_months:
          if day >= 31:
            day = 1
            month += 1
            if month > 12:
              year += 1
        elif month == 2:
          if not year%4==0:
            if day >= 29:
              day = 1
              month += 1
              if month > 12:
                year += 1
          else:
            if day >= 28:
              day = 1
              month += 1
              if month > 12:
                year += 1
      return datetime(int(year), int(month), int(day), int(hour), 00)


我发现最简单的方法是得到你所在位置的时间偏移量,然后从小时中减去它。

1
2
3
4
5
6
7
def format_time(ts,offset):
    if not ts.hour >= offset:
        ts = ts.replace(day=ts.day-1)
        ts = ts.replace(hour=ts.hour-offset)
    else:
        ts = ts.replace(hour=ts.hour-offset)
    return ts

这对我很有用,在Python3.5.2中。


在Python2和3中工作的简单(但可能有缺陷)方法:

1
2
3
4
5
import time
import datetime

def utc_to_local(dt):
    return dt - datetime.timedelta(seconds = time.timezone)

它的优点是写一个反函数很简单