【问题标题】:Convert UTC datetime string to local datetime将 UTC 日期时间字符串转换为本地日期时间
【发布时间】:2011-06-13 19:22:03
【问题描述】:

我从来不需要在 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 datetime utc localtime


【解决方案1】:

如果您不想提供自己的 tzinfo 对象,请查看 python-dateutil 库。它在 zoneinfo (Olson) database 之上提供了 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)

【讨论】:

  • 在我之前的评论中,我能够导入 dateutil.tz 并使用 tz.tzutc()tz.tzlocal() 作为我正在寻找的时区对象。看起来我系统上的时区数据库很好(我签入了/usr/share/zoneinfo)。不知道发生了什么。
  • @Benjamin 你在正确的轨道上。 tz 模块是用于此库的正确入口点。我已经更新了我的答案以反映这一点。我之前展示的dateutil.zoneinfo 模块由tz 模块在内部使用,如果它无法找到系统的zoneinfo DB。如果您查看库内部,您会看到包中有一个 zoneinfo DB tarball,如果找不到系统的 DB,它会使用它。我的旧示例试图直接访问该数据库,我猜您在加载该私有数据库时遇到问题(不知何故不在 python 路径上?)
  • @J.F.Sebastian 这已在#225中修复
  • @briankip 这取决于您的应用程序需要多么复杂,但在一般应用程序中,是的,这是一个问题。首先取决于它是一年中的什么时候,添加/减去的小时数可能会改变,因为某些时区尊重 DST 而其他时区则不。其次,时区规则会随时间任意更改,因此如果您使用过去的日期/时间,则需要应用在该时间点有效的规则,而不是当前时间。这就是为什么最好使用在幕后利用 Olson TZ 数据库的 API。
【解决方案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!

更新:这个sn-p有使用当前时间的UTC偏移量的弱点,它可能与输入日期时间的UTC偏移量不同。有关另一种解决方案,请参阅此答案的 cmets。

要绕过不同的时间,请从传入的时间中获取纪元时间。这就是我所做的:

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

【讨论】:

  • 这很好用,只需要时间和日期时间。
  • @Mike_K:但这是不正确的。由于其他原因,它不支持过去具有不同 UTC 偏移量的 DST 或时区。没有pytz-like db 的完全支持是不可能的,请参阅 PEP-431。尽管您可以编写仅适用于此类情况的 stdlib 解决方案,该解决方案适用于已经具有历史时区数据库的系统,例如 Linux、OS X,请参阅my answer
  • @J.F.Sebastian:你说这段代码在一般情况下不起作用,但也说它适用于 OS X 和 Linux。你的意思是这段代码不能在 Windows 上运行?
  • @DavidFoster:您的代码也可能在 Linux 和 OS X 上失败。您和mine stdlib-only solutions 之间的区别在于您使用的当前 偏移量可能与utc_datetime 时间的UTC 偏移量不同。
  • 好电话。这是一个非常微妙的区别。 UTC 偏移量也可以随时间变化。 叹息
【解决方案3】:

请参阅 datetime 文档以了解 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 转换为另一个时区。
  • 抱歉换成乔的答案。从技术上讲,您的回答准确地解释了如何使用原始 python(这很高兴知道),但他建议的库让我更快地找到解决方案。
  • 看起来它不会自动处理夏令时。
  • @GringoSuave:这不是故意的。其规则很复杂并且经常变化。
【解决方案4】:

如果您想获得正确的结果,即使是对应于不明确的本地时间(例如,在 DST 转换期间)和/或本地 utc 偏移量在本地时区的不同时间不同,请使用 @ 987654321@时区:

#!/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】:

        以下内容在美国西部的云环境中对我有用:

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

        【讨论】:

          【解决方案8】:

          您可以使用arrow

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:
                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)
                然后我会得到local_tz_str = 2018-10-17T08:00:00+08:00 (我的目标时区 +08:00)

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

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

                【讨论】:

                • 你应该用纯文本更好地描述你的问题,向其他用户解释你想要实现什么以及你的问题是什么
                【解决方案12】:

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

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

                【讨论】:

                • 这适用于很多情况下,您只是为了显示进行本地化。但是,有时您需要根据用户的时区执行一些业务逻辑,并且您希望能够在应用程序服务器层进行转换。
                【解决方案13】:

                here的回答中,您可以使用时间模块将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)
                

                【讨论】:

                  【解决方案14】:

                  这是一个快速而肮脏的版本,它使用本地系统设置来计算时差。注意:如果您需要转换为当前系统未运行的时区,这将不起作用。我已经在 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 (float seconds) -> datetime object in local time过去的日期))。否则可以使用 pytz。
                  • 我建议您执行以下操作:offset = datetime.utcnow().replace(minute=0, second=0, microsecond=0) - datetime.now().replace(minute=0, second=0, microsecond=0) 没有它,我得到了奇怪的微秒差异。
                  • @ErikvanOosten:你试过datetime.fromtimestamp(ts)而不是答案吗?
                  【解决方案15】:

                  这对我有用:

                  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)
                  

                  【讨论】:

                    【解决方案16】:

                    简短:

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

                    【讨论】:

                      猜你喜欢
                      • 2014-02-22
                      • 2012-10-07
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-10-24
                      • 2018-02-23
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多