我从来没有不得不将时间与UTC转换。最近有人要求我的应用注意时区,并且我一直在圈子里奔波。我发现很多关于将本地时间转换为UTC的信息,这是相当基本的(也许我也做错了),但是我找不到任何有关将UTC时间轻松转换为最终用户时区的信息。简而言之,Android应用程序向我发送了(appengine应用程序)数据,其中包含时间戳记。要将时间戳存储为utc时间,我正在使用:

datetime.utcfromtimestamp(timestamp)


这似乎正在工作。当我的应用程序存储数据时,它将被提前5个小时存储(我是EST -5)

数据被存储在appengine的BigTable中,并且在检索时它像这样的字符串出现:

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


如何在用户正确的时区将此字符串转换为DateTime?

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

评论

相关:如何仅使用python标准库将python utc日期时间转换为本地日期时间?

此答案显示了如何以简单的方式解决此问题。

#1 楼

如果您不想提供自己的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用法以显示更好的入口点方法

编辑3包括的时区自动检测方法(雅林)

评论


在我之前的评论中,我能够导入dateutil.tz并将tz.tzutc()和tz.tzlocal()用作我正在寻找的时区对象。看来我系统上的时区数据库很好(我在/ usr / share / zoneinfo中签入了)。不知道发生了什么。

– Ben Kreeger
2011年6月3日14:18

@Benjamin您的目标正确。 tz模块是用于此库的正确入口点。我已经更新了答案以反映这一点。我之前显示的dateutil.zoneinfo模块在tz模块内部无法使用,如果无法找到系统的zoneinfo数据库,则将其作为备用。如果您在库中查看,则会发现如果找不到系统的数据库,则使用的包中将包含zoneinfo DB tar包。我的旧示例试图直接访问该数据库,而我猜您在加载该私有数据库时遇到了问题(以某种方式不在python路径上吗?)

–乔·霍洛威(Joe Holloway)
2011年6月3日15:01

@MattoTodd:utc-> dateutil中的本地转换已中断,请改用pytz。

– jfs
2015年10月2日,9:58



@ J.F.Sebastian,此问题固定在#225

–罗门
16-4-7在0:51



@briankip这取决于应用程序的复杂程度,但是在常规应用程序中,这是一个问题。首先,取决于一年中的什么时间,加/减小时数可能会发生变化,因为某些时区采用DST,而其他时区不采用DST。其次,时区规则会随时间任意更改,因此,如果您使用过去的日期/时间,则需要应用在该时间点有效的规则,而不是当前时间。这就是为什么最好使用在后台利用Olson TZ数据库的API。

–乔·霍洛威(Joe Holloway)
16年4月22日在17:10

#2 楼

这是一种不依赖任何外部库的弹性方法:

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示例中的计时问题。还有Erik van Oosten修正案中较小的计时问题。

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

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


更新:此摘录具有使用当前时间的UTC偏移量的缺点,该时间可能与输入日期时间的UTC偏移量不同。请参阅对此答案的评论以寻求其他解决方案。

要绕过不同的时间,请从传入的时间中获取新纪元时间。这是我的工作:

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


评论


这很好用,并且只需要时间和日期时间。

– Mike_K
13年4月4日在23:45

@Mike_K:但这是不正确的。它不支持过去由于其他原因而具有不同utc偏移量的DST或时区。没有像pytz这样的db的完全支持是不可能的,请参阅PEP-431。虽然您可以编写仅stdlib解决方案,但在具有历史时区数据库的系统(例如Linux,OS X)上可以在这种情况下使用,但请参阅我的答案。

– jfs
2013年12月14日20:17在

@ J.F.Sebastian:您说这段代码通常不起作用,但也说它适用于OS X和Linux。您是说该代码在Windows上不起作用吗?

–大卫·福斯特(David Foster)
2013年12月15日21:37

@DavidFoster:您的代码也可能在Linux和OS X上失败。您的解决方案与仅适用于我的stdlib的解决方案之间的区别在于,您的解决方案使用的当前偏移量可能与utc_datetime时的utc偏移量不同。

– jfs
2013年12月15日22:25

好决定。那是非常微妙的区别。 UTC偏移量也可以随时间变化。叹

–大卫·福斯特(David Foster)
2013年12月18日下午5:36

#3 楼

请参阅有关tzinfo对象的日期时间文档。您必须实现自己想要支持的时区。这些是文档底部的示例。

这是一个简单的示例:

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


评论


感谢你的接纳!我对其进行了略微更新,以翻译您的特定示例时间。解析时间会创建文档所说的“原始”时间。使用replace将时区设置为GMT并将其设为“感知”时区,然后使用astimezone转换为另一个时区。

– Mark Tolonen
2011年1月22日在21:54



抱歉换了乔的答案。从技术上讲,您的答案确切地解释了如何使用原始python(很高兴知道)来做,但是他建议的库可以让我更快地找到解决方案。

–马托·托德(MattoTodd)
2011年1月23日下午4:03

看起来好像它不会自动处理夏令时。

– Gringo Suave
2012年3月22日在18:19

@GringoSuave:这不是故意的。规则很复杂并且经常更改。

– Mark Tolonen
2012年3月22日21:41

#4 楼

如果即使在与不确定的本地时间相对应的时间(例如,在DST转换期间)也要获得正确的结果,并且/或者本地utc偏移在您本地时区的不同时间不同,那么请使用pytz时区:

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


#5 楼

如果您不想使用除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.x,则可能必须继承datetime.tzinfo的子类,并使用它来帮助您创建有意识的datetime对象,因为在Python 2.x中不存在datetime.timezone

#6 楼

如果使用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>)


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

#8 楼

我通常将其推迟到前端-以时间戳或UTC中的其他日期时间格式从后端发送时间,然后让客户端找出时区偏移量,并在正确的时区中呈现此数据。

对于Web应用程序,这在javascript中非常容易实现-您可以使用内置方法很容易地找出浏览器的时区偏移量,然后正确地从后端呈现数据。

评论


在很多情况下,您只需要本地化显示即可使用。但是,有时您需要根据用户的时区执行一些业务逻辑,并且希望能够进行应用服务器层的转换。

–乔·霍洛威(Joe Holloway)
2011年1月23日的1:32

#9 楼

您可以使用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时区)。

#10 楼

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

评论


您应该以纯文本形式更好地描述您的问题,在其中向其他用户解释您要达到的目标以及哪个问题

–m33n
18-10-17在10:59

#11 楼

从这里的答案中,您可以使用时间模块将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)


#12 楼

将弗兰克桑德斯的答案合并成一种方便的方法。

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


#13 楼

这是一个快速而肮脏的版本,它使用本地系统设置来计算时差。注意:如果您需要转换为当前系统未在其中运行的时区,则此方法将不起作用。我已经在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


评论


它应该是datetime.fromtimestamp(ts):posix timestamp(浮点秒数)->本地时间中的datetime对象(如果OS记住本地时区的utc偏移,即Unix,而不是Windows上的过去日期,则可以很好地工作) )。否则可以使用pytz。

– jfs
2013年6月3日14:33

我可以建议执行以下操作:offset = datetime.utcnow()。replace(minute = 0,second = 0,microsecond = 0)-datetime.now()。replace(minute = 0,second = 0,microsecond = 0 )如果没有它,我会感到奇怪的微秒差异。

– Erik van Oosten
2013年9月26日9:21



@ErikvanOosten:您是否尝试过datetime.fromtimestamp(ts)而不是答案?

– jfs
2013年12月14日20:22