关于python:将UTC日期时间字符串转换为本地日期时间

Convert UTC datetime string to local datetime

我从来不用把时间转换成和从UTC转换。最近有一个请求让我的应用程序知道时区,我一直在循环运行。关于将本地时间转换为UTC的许多信息,我发现这是相当基本的(也许我做得也不对),但我找不到任何有关将UTC时间轻松转换为最终用户时区的信息。

简而言之,android应用程序会发送我(appengine应用程序)的数据,其中包含一个时间戳。要将时间戳存储到我使用的UTC时间,请执行以下操作:

1
datetime.utcfromtimestamp(timestamp)

这似乎奏效了。当我的应用程序存储数据时,它将在5小时前存储(我是EST-5)

数据存储在appengine的bigtable中,当检索到数据时,它会以字符串的形式出现,如下所示:

1
"2011-01-21 02:37:21"

如何将此字符串转换为用户正确时区中的日期时间?

另外,对于用户时区信息,建议的存储空间是什么?(您通常如何存储TZ信息,例如:"-5:00"或"est"等?)我确信第一个问题的答案可能包含一个参数,第二个问题的答案。


如果不想提供自己的tzinfo对象,请查看python-dateutil库。它在ZoneInfo(olson)数据库的基础上提供tzinfo实现,这样您就可以使用某种规范化的名称引用时区规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from datetime import datetime
from dateutil import tz

# METHOD 1: Hardcode zones:
from_zone = tz.gettz('UTC')
to_zone = tz.gettz('America/New_York')

# METHOD 2: Auto-detect zones:
from_zone = tz.tzutc()
to_zone = tz.tzlocal()

# utc = datetime.utcnow()
utc = datetime.strptime('2011-01-21 02:37:21', '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in UTC time zone since
# datetime objects are 'naive' by default
utc = utc.replace(tzinfo=from_zone)

# Convert time zone
central = utc.astimezone(to_zone)

编辑扩展示例以显示strptime用法

编辑2个固定的API用法以显示更好的入口点方法

编辑3包括时区自动检测方法(yarin)


这里有一个不依赖任何外部库的弹性方法:

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

def datetime_from_utc_to_local(utc_datetime):
    now_timestamp = time.time()
    offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp)
    return utc_datetime + offset

这避免了Delboyjay例子中的时间问题。以及埃里克·范·奥斯滕修正案中较小的时间问题。

作为一个有趣的脚注,上面计算的时区偏移量可能不同于以下看似等效的表达式,可能是由于夏令时规则的更改:

1
offset = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # NO!

更新:此代码段的缺点是使用当前时间的UTC偏移量,这可能与输入日期时间的UTC偏移量不同。有关其他解决方案,请参阅关于此答案的评论。

为了避开不同的时代,从过去的时间中抓取时代时间。我要做的是:

1
2
3
4
def utc2local (utc):
    epoch = time.mktime(utc.timetuple())
    offset = datetime.fromtimestamp (epoch) - datetime.utcfromtimestamp (epoch)
    return utc + offset


请参阅有关TZINFO对象的日期时间文档。你必须实现你想要支持自己的时区。以下是文档底部的示例。

下面是一个简单的例子:

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
from datetime import datetime,tzinfo,timedelta

class Zone(tzinfo):
    def __init__(self,offset,isdst,name):
        self.offset = offset
        self.isdst = isdst
        self.name = name
    def utcoffset(self, dt):
        return timedelta(hours=self.offset) + self.dst(dt)
    def dst(self, dt):
            return timedelta(hours=1) if self.isdst else timedelta(0)
    def tzname(self,dt):
         return self.name

GMT = Zone(0,False,'GMT')
EST = Zone(-5,False,'EST')

print datetime.utcnow().strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(GMT).strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(EST).strftime('%m/%d/%Y %H:%M:%S %Z')

t = datetime.strptime('2011-01-21 02:37:21','%Y-%m-%d %H:%M:%S')
t = t.replace(tzinfo=GMT)
print t
print t.astimezone(EST)

产量

1
2
3
4
5
01/22/2011 21:52:09
01/22/2011 21:52:09 GMT
01/22/2011 16:52:09 EST
2011-01-21 02:37:21+00:00
2011-01-20 21:37:21-05:00a


如果您想得到正确的结果,即使是与不明确的本地时间(例如,在DST转换期间)和/或本地UTC偏移量在本地时区的不同时间是不同的,则使用pytz时区:

1
2
3
4
5
6
7
8
#!/usr/bin/env python
from datetime import datetime
import pytz    # $ pip install pytz
import tzlocal # $ pip install tzlocal

local_timezone = tzlocal.get_localzone() # get pytz tzinfo
utc_time = datetime.strptime("2011-01-21 02:37:21","%Y-%m-%d %H:%M:%S")
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(local_timezone)

如果使用django,则可以使用timezone.localtime方法(请参见https://docs.djangoproject.com/en/dev/topics/i18n/timezones/)。

1
2
3
4
5
6
from django.utils import timezone
date
# datetime.datetime(2014, 8, 1, 20, 15, 0, 513000, tzinfo=<UTC>)

timezone.localtime(date)
# datetime.datetime(2014, 8, 1, 16, 15, 0, 513000, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)

如果您不想使用除datetime以外的任何其他模块,这个答案将很有帮助。

datetime.utcfromtimestamp(timestamp)返回一个幼稚的datetime对象(没有意识到)。有意识的人是有时区意识的,而幼稚的人不是。如果要在时区之间转换(例如,在UTC和本地时间之间转换),则需要一个已知的时区。

如果您不是实例化开始日期的人,但您仍然可以在UTC时间创建一个幼稚的datetime对象,那么您可能希望尝试使用此python 3.x代码来转换它:

1
2
3
4
5
6
import datetime

d=datetime.datetime.strptime("2011-01-21 02:37:21","%Y-%m-%d %H:%M:%S") #Get your naive datetime object
d=d.replace(tzinfo=datetime.timezone.utc) #Convert it to an aware datetime object in UTC time.
d=d.astimezone() #Convert it to your local timezone (still aware)
print(d.strftime("%d %b %Y (%I:%M:%S:%f %p) %Z")) #Print it with a directive of choice

小心不要错误地假定,如果您的时区当前是MDT,那么夏令时不适用于上述代码,因为它打印MST。您会注意到,如果您将月份更改为8月份,它将打印MDT。

另一种获取已知datetime对象(也在python 3.x中)的简单方法是使用指定的时区创建它。下面是一个使用UTC的示例:

1
2
3
4
5
6
7
8
9
10
11
12
import datetime, sys

aware_utc_dt_obj=datetime.datetime.now(datetime.timezone.utc) #create an aware datetime object
dt_obj_local=aware_utc_dt_obj.astimezone() #convert it to local time

#The following section is just code for a directive I made that I liked.
if sys.platform=="win32":
    directive="%#d %b %Y (%#I:%M:%S:%f %p) %Z"
else:
    directive="%-d %b %Y (%-I:%M:%S:%f %p) %Z"

print(dt_obj_local.strftime(directive))

如果使用python 2.x,则可能需要对datetime.tzinfo进行子类化,并使用它来帮助创建一个可感知的datetime对象,因为datetime.timezone在python 2.x中不存在。


您可以使用calendar.timegm将时间转换为unix epoch以来的秒数,使用time.localtime将时间转换回:

1
2
3
4
5
6
7
import calendar
import time

time_tuple = time.strptime("2011-01-21 02:37:21","%Y-%m-%d %H:%M:%S")
t = calendar.timegm(time_tuple)

print time.ctime(t)

Fri Jan 21 05:37:21 2011(因为我在UTC+03:00时区)。


这里有一个快速而肮脏的版本,它使用本地系统设置来计算时差。注意:如果需要转换为当前系统未运行的时区,则此操作将不起作用。我在英国标准时区的英国设置中测试过这个

1
2
3
4
5
6
7
8
9
10
11
12
from datetime import datetime
def ConvertP4DateTimeToLocal(timestampValue):
   assert isinstance(timestampValue, int)

   # get the UTC time from the timestamp integer value.
   d = datetime.utcfromtimestamp( timestampValue )

   # calculate time difference from utcnow and the local system time reported by OS
   offset = datetime.now() - datetime.utcnow()

   # Add offset to UTC time and return it
   return d + offset


传统上,我将时间延迟到前端——以时间戳或其他日期时间格式(UTC)从后端发送时间,然后让客户机计算出时区偏移量,并在适当的时区中呈现这些数据。

对于webapp来说,在javascript中很容易做到这一点——您可以使用内置方法很容易地计算出浏览器的时区偏移量,然后正确地从后端渲染数据。


从这里的答案中,您可以使用时间模块将UTC转换为计算机中设置的本地时间:

1
2
3
utc_time = time.strptime("2018-12-13T10:32:00.000","%Y-%m-%dT%H:%M:%S.%f")
utc_seconds = calendar.timegm(utc_time)
local_time = time.localtime(utc_seconds)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import datetime

def utc_str_to_local_str(utc_str: str, utc_format: str, local_format: str):
   """
    :param utc_str: UTC time string
    :param utc_format: format of UTC time string
    :param local_format: format of local time string
    :return: local time string
   """

    temp1 = datetime.datetime.strptime(utc_str, utc_format)
    temp2 = temp1.replace(tzinfo=datetime.timezone.utc)
    local_time = temp2.astimezone()
    return local_time.strftime(local_format)

utc = '2018-10-17T00:00:00.111Z'
utc_fmt = '%Y-%m-%dT%H:%M:%S.%fZ'
local_fmt = '%Y-%m-%dT%H:%M:%S+08:00'
local_string = utc_str_to_local_str(utc, utc_fmt, local_fmt)
print(local_string)   # 2018-10-17T08:00:00+08:00

例如,我的时区是"+08:00"。输入UTC=2018-10-17T00:00:00.111Z,则输出=2018-10-17T08:00:00+08:00


你可以用箭头

1
2
3
4
5
6
7
from datetime import datetime
import arrow

now = datetime.utcnow()

print(arrow.get(now).to('local').format())
# '2018-04-04 15:59:24+02:00'

你可以用任何东西喂arrow.get()。时间戳、ISO字符串等