266

我从来不需要在 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”等?)我确定我的第一个问题的答案可能包含第二个答案的参数。

4

16 回答 16

506

如果您不想提供自己的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包含的时区自动检测方法(Yarin)

于 2011-01-23T01:23:37.850 回答
61

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

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
于 2013-10-08T03:24:25.520 回答
32

请参阅有关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
于 2011-01-22T21:22:15.987 回答
26

如果您想获得正确的结果,即使是对应于不明确的本地时间(例如,在 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)
于 2015-10-02T09:57:55.637 回答
18

This answer should be helpful if you don't want to use any other modules besides datetime.

datetime.utcfromtimestamp(timestamp) returns a naive datetime object (not an aware one). Aware ones are timezone aware, and naive are not. You want an aware one if you want to convert between timezones (e.g. between UTC and local time).

If you aren't the one instantiating the date to start with, but you can still create a naive datetime object in UTC time, you might want to try this Python 3.x code to convert it:

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

Be careful not to mistakenly assume that if your timezone is currently MDT that daylight savings doesn't work with the above code since it prints MST. You'll note that if you change the month to August, it'll print MDT.

Another easy way to get an aware datetime object (also in Python 3.x) is to create it with a timezone specified to start with. Here's an example, using 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))

If you use Python 2.x, you'll probably have to subclass datetime.tzinfo and use that to help you create an aware datetime object, since datetime.timezone doesn't exist in Python 2.x.

于 2017-09-21T08:53:40.807 回答
10

如果使用 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>)
于 2014-08-04T14:08:23.553 回答
4

以下内容在美国西部的云环境中为我工作:

import datetime
import pytz

#set the timezone
tzInfo = pytz.timezone('America/Los_Angeles')
dt = datetime.datetime.now(tz=tzInfo)
print(dt)
于 2021-05-11T17:08:00.330 回答
3

将来自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()))
于 2020-02-21T05:51:15.473 回答
3

你可以使用箭头

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

于 2018-04-04T14:07:13.327 回答
2
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-17T00:00:00.111Z , (UTC +00:00)<br /> 然后我会得到local_tz_str= 2018-10-17T08:00:00+08:00 (我的目标时区 +08 :00)</p>

参数utc_format是由您具体确定的格式utc_tz_example_str
参数local_fmt是最终所需的格式。

就我而言,我想要的格式是%Y-%m-%dT%H:%M:%S+08:00(+08:00 时区)。你应该构造你想要的格式。

于 2018-10-17T10:50:52.093 回答
2

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

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

于 2017-06-08T15:52:15.620 回答
1

我传统上将此延迟到前端 - 从后端发送时间作为时间戳或UTC中的其他日期时间格式,然后让客户端找出时区偏移并在适当的时区呈现这些数据。

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

于 2011-01-23T00:58:06.890 回答
1

这里的答案,您可以使用 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)
于 2018-12-13T11:52:48.300 回答
0

这是一个快速而肮脏的版本,它使用本地系统设置来计算时差。注意:如果您需要转换为当前系统未运行的时区,这将不起作用。我已经在 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
于 2013-04-15T09:38:26.420 回答
0

这对我有用:

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)
于 2022-01-21T13:08:03.300 回答
0

简短而简单:

from datetime import datetime

t = "2011-01-21 02:37:21"
datetime.fromisoformat(t) + (datetime.now() - datetime.utcnow())
于 2022-02-09T18:44:36.910 回答