将UTC日期时间字符串转换为本地日期时间

我从来没有在UTC和UTC之间转换过时间。最近有一个请求,让我的应用程序具有时区感知,我一直在兜圈子。大量关于将本地时间转换为UTC的信息,我发现这些信息相当简单(可能我也做错了),但我找不到任何关于将UTC时间轻松转换为最终用户时区的信息。

简而言之,和android应用程序发送给我(appengine应用程序)数据,在该数据是一个时间戳。存储时间戳到utc时间我正在使用:

datetime.utcfromtimestamp(timestamp)

这似乎起作用了。当我的应用程序存储数据时,它被存储为5小时前(我是EST -5)

数据被存储在appengine的BigTable上,当检索到它时,它是一个像这样的字符串:

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

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

另外,用户时区信息的推荐存储是什么?(你通常如何存储tz信息,如:“-5:00”或“EST”等?)我确信我第一个问题的答案可能包含第二个问题的答案的参数。

529671 次浏览

参见关于tzinfo对象的datetime文档。您必须实现您想要支持自己的时区。文档底部有一些示例。

这里有一个简单的例子:

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)

输出

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

我通常将此延迟到前端——从后端以UTC时间戳或其他日期时间格式发送时间,然后让客户端计算时区偏移量,并以适当的时区呈现此数据。

对于web应用程序,这在javascript中很容易做到——你可以使用内置方法很容易地计算出浏览器的时区偏移量,然后从后端正确地呈现数据。

如果你不想提供你自己的tzinfo对象,检查python-dateutil库。它在zoneinfo (Olson)数据库之上提供了tzinfo实现,这样你就可以用一个有点规范的名字来引用时区规则。

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使用显示更好的入口点方法

包含时区自动检测方法(Yarin)

下面是一个快速而简单的版本,它使用本地系统设置来计算时差。注意:如果您需要转换到当前系统未运行的时区,这将不起作用。我已经在BST时区下测试了英国设置

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

下面是一个不依赖于任何外部库的弹性方法:

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示例中的时间问题。还有埃里克·范·奥斯滕修正案中时间安排的问题。

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

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

更新:这个代码段的缺点是使用当前时间的UTC偏移量,这可能与输入datetime的UTC偏移量不同。有关另一种解决方案,请参阅对该答案的评论。

为了避开不同的时间,从过去的时间中抓取纪元时间。我是这么做的:

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

如果使用Django,你可以使用timezone.localtime方法:

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>)

如果你想获得正确的结果,即使时间对应于一个不明确的本地时间(例如,在DST转换期间)和/或本地utc偏移量在本地时区的不同时间是不同的,那么使用pytz timezones:

#!/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)

你可以使用calendar.timegm将时间转换为自Unix纪元以来的秒数,使用time.localtime将时间转换回来:

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时区)。

如果你不想使用datetime之外的任何其他模块,这个答案应该是有帮助的。

datetime.utcfromtimestamp(timestamp)返回一个朴素的datetime对象(不是一个有意识的对象)。有意识的人是时区意识的,而天真的人不是。如果你想在时区之间转换(例如在UTC和本地时间之间),你需要一个感知的。

如果你不是初始化日期的人,但你仍然可以在UTC时间创建一个朴素的datetime对象,你可能想尝试这个Python 3。X代码转换它:

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的例子:

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。在Python 2.x中,你可能必须继承datetime.tzinfo的子类,并使用它来帮助你创建一个感知的datetime对象,因为datetime.timezone在Python 2.x中不存在。

你可以使用箭头

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字符串等

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_tz_example_str = '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'


# call my function here
local_tz_str = utc_str_to_local_str(utc_tz_example_str, utc_fmt, local_fmt)
print(local_tz_str)   # 2018-10-17T08:00:00+08:00

当我输入utc_tz_example_str = 2018 - 10 - 17 - t00:00:00.111z, (UTC +00:00)
那么我将得到local_tz_str = 2018 - 10 - 17 t08:00:00 +喂饲(我的目标时区+08:00)

参数utc_format是由你的特定utc_tz_example_str确定的格式。
参数local_fmt是最终期望的格式

在我的例子中,我想要的格式是%Y-%m-%dT%H:%M:%S+08:00(+08:00时区)。您应该构造所需的格式。

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

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)

franksands中的答案合并到一个方便的方法中。

import calendar
import datetime


def to_local_datetime(utc_dt):
"""
convert from utc datetime to a locally aware datetime according to the host timezone


:param utc_dt: utc datetime
:return: local timezone datetime
"""
return datetime.datetime.fromtimestamp(calendar.timegm(utc_dt.timetuple()))

以下是我在US west的云环境中使用的方法:

import datetime
import pytz


#set the timezone
tzInfo = pytz.timezone('America/Los_Angeles')
dt = datetime.datetime.now(tz=tzInfo)
print(dt)

这招对我很管用:

from django.utils import timezone
from datetime import timedelta,datetime


ist_time = timezone.now() + timedelta(hours=5,minutes=30)


#second method


ist_time = datetime.now() + timedelta(hours=5,minutes=30)

简短而简单:

from datetime import datetime


t = "2011-01-21 02:37:21"
datetime.fromisoformat(t) + (datetime.now() - datetime.utcnow())