首页 文章

仅使用python标准库将python UTC日期时间转换为本地日期时间?

提问于
浏览
108

我有一个使用datetime.utcnow()创建并保存在数据库中的python datetime实例 .

为了显示,我想使用默认的本地时区将从数据库检索的日期时间实例转换为本地日期时间(即,就像使用datetime.now()创建日期时间一样) .

如何仅使用python标准库(例如,没有pytz依赖项)将UTC日期时间转换为本地日期时间?

似乎一个解决方案是使用datetime.astimezone(tz),但是如何获得默认的本地时区?

9 回答

  • 0

    在Python 3.3中:

    from datetime import timezone
    
    def utc_to_local(utc_dt):
        return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
    

    在Python 2/3中:

    import calendar
    from datetime import datetime, timedelta
    
    def utc_to_local(utc_dt):
        # get integer timestamp to avoid precision lost
        timestamp = calendar.timegm(utc_dt.timetuple())
        local_dt = datetime.fromtimestamp(timestamp)
        assert utc_dt.resolution >= timedelta(microseconds=1)
        return local_dt.replace(microsecond=utc_dt.microsecond)
    

    使用 pytz (Python 2/3):

    import pytz
    
    local_tz = pytz.timezone('Europe/Moscow') # use your local timezone name here
    # NOTE: pytz.reference.LocalTimezone() would produce wrong result here
    
    ## You could use `tzlocal` module to get local timezone on Unix and Win32
    # from tzlocal import get_localzone # $ pip install tzlocal
    
    # # get local timezone    
    # local_tz = get_localzone()
    
    def utc_to_local(utc_dt):
        local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
        return local_tz.normalize(local_dt) # .normalize might be unnecessary
    

    示例

    def aslocaltimestr(utc_dt):
        return utc_to_local(utc_dt).strftime('%Y-%m-%d %H:%M:%S.%f %Z%z')
    
    print(aslocaltimestr(datetime(2010,  6, 6, 17, 29, 7, 730000)))
    print(aslocaltimestr(datetime(2010, 12, 6, 17, 29, 7, 730000)))
    print(aslocaltimestr(datetime.utcnow()))
    

    输出

    Python 3.3

    2010-06-06 21:29:07.730000 MSD+0400
    2010-12-06 20:29:07.730000 MSK+0300
    2012-11-08 14:19:50.093745 MSK+0400
    

    Python 2

    2010-06-06 21:29:07.730000 
    2010-12-06 20:29:07.730000 
    2012-11-08 14:19:50.093911
    

    pytz

    2010-06-06 21:29:07.730000 MSD+0400
    2010-12-06 20:29:07.730000 MSK+0300
    2012-11-08 14:19:50.146917 MSK+0400
    

    注意:它考虑了DST和MSK时区的最近utc偏移变化 .

    我不知道非pytz解决方案是否适用于Windows .

  • 37

    你可以't do it with only the standard library as the standard library doesn't有任何时区 . 你需要pytzdateutil .

    >>> from datetime import datetime
    >>> now = datetime.utcnow()
    >>> from dateutil import tz
    >>> HERE = tz.tzlocal()
    >>> UTC = tz.gettz('UTC')
    
    The Conversion:
    >>> gmt = now.replace(tzinfo=UTC)
    >>> gmt.astimezone(HERE)
    datetime.datetime(2010, 12, 30, 15, 51, 22, 114668, tzinfo=tzlocal())
    

    或者,你可以通过实现自己的时区来实现没有pytz或dateutil . 但这很愚蠢 .

  • 159

    我想我想出来了:计算自纪元以来的秒数,然后使用time.localtime转换为本地timzeone,然后将时间结构转换回日期时间......

    EPOCH_DATETIME = datetime.datetime(1970,1,1)
    SECONDS_PER_DAY = 24*60*60
    
    def utc_to_local_datetime( utc_datetime ):
        delta = utc_datetime - EPOCH_DATETIME
        utc_epoch = SECONDS_PER_DAY * delta.days + delta.seconds
        time_struct = time.localtime( utc_epoch )
        dt_args = time_struct[:6] + (delta.microseconds,)
        return datetime.datetime( *dt_args )
    

    它适用于夏季/冬季DST:

    >>> utc_to_local_datetime( datetime.datetime(2010, 6, 6, 17, 29, 7, 730000) )
    datetime.datetime(2010, 6, 6, 19, 29, 7, 730000)
    >>> utc_to_local_datetime( datetime.datetime(2010, 12, 6, 17, 29, 7, 730000) )
    datetime.datetime(2010, 12, 6, 18, 29, 7, 730000)
    
  • 4

    标准Python库根本没有任何 tzinfo 实现 . 我一直认为这是日期时间模块的一个令人惊讶的缺点 .

    documentation for the tzinfo class确实附带了一些有用的例子 . 在该部分的末尾查找大代码块 .

  • 0

    你不能用标准库来做 . 使用 pytz 模块,您可以将任何天真/感知日期时间对象转换为任何其他时区 . 让我们看一些使用Python 3的例子 .

    通过类方法utcnow()创建的朴素对象

    要将 naive 对象转换为任何其他时区,首先必须将其转换为 aware datetime对象 . 您可以使用 replace 方法将 naive datetime对象转换为 aware datetime对象 . 然后,要将 aware datetime对象转换为任何其他时区,您可以使用 astimezone 方法 .

    变量 pytz.all_timezones 为您提供pytz模块中所有可用时区的列表 .

    import datetime,pytz
    
    dtobj1=datetime.datetime.utcnow()   #utcnow class method
    print(dtobj1)
    
    dtobj3=dtobj1.replace(tzinfo=pytz.UTC) #replace method
    
    dtobj_hongkong=dtobj3.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
    print(dtobj_hongkong)
    

    通过类方法创建的朴素对象now()

    因为 now 方法返回当前日期和时间,所以您必须首先了解datetime对象时区 . localize 函数将 naive datetime对象转换为时区感知日期时间对象 . 然后,您可以使用 astimezone 方法将其转换为另一个时区 .

    dtobj2=datetime.datetime.now()
    
    mytimezone=pytz.timezone("Europe/Vienna") #my current timezone
    dtobj4=mytimezone.localize(dtobj2)        #localize function
    
    dtobj_hongkong=dtobj4.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
    print(dtobj_hongkong)
    
  • 6

    以阿列克谢的评论为基础 . 这也适用于夏令时 .

    import time
    import datetime
    
    def utc_to_local(dt):
        if time.localtime().tm_isdst:
            return dt - datetime.timedelta(seconds = time.altzone)
        else:
            return dt - datetime.timedelta(seconds = time.timezone)
    
  • 4

    一种简单(但可能有缺陷)的方法适用于Python 2和3:

    import time
    import datetime
    
    def utc_to_local(dt):
        return dt - datetime.timedelta(seconds = time.timezone)
    

    它的优点是编写反函数是微不足道的

  • 0

    我找到的最简单的方法是获取你所在位置的时间偏移量,然后从小时中减去它 .

    def format_time(ts,offset):
        if not ts.hour >= offset:
            ts = ts.replace(day=ts.day-1)
            ts = ts.replace(hour=ts.hour-offset)
        else:
            ts = ts.replace(hour=ts.hour-offset)
        return ts
    

    这适用于我,在Python 3.5.2中 .

  • 3

    这是另一种改变日期时间格式时区的方法(我知道我浪费了我的精力,但我没有看到这个页面,所以我不知道如何)没有分钟 . 和秒因为我的项目不需要它:

    def change_time_zone(year, month, day, hour):
          hour = hour + 7 #<-- difference
          if hour >= 24:
            difference = hour - 24
            hour = difference
            day += 1
            long_months = [1, 3, 5, 7, 8, 10, 12]
            short_months = [4, 6, 9, 11]
            if month in short_months:
              if day >= 30:
                day = 1
                month += 1
                if month > 12:
                  year += 1
            elif month in long_months:
              if day >= 31:
                day = 1
                month += 1
                if month > 12:
                  year += 1
            elif month == 2:
              leap_years = [2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048]
              if year in leap_years:
                if day >= 29:
                  day = 1
                  month += 1
                  if month > 12:
                    year += 1
              elif year not in leap_years:
                if day >= 28:
                  day = 1
                  month += 1
                  if month > 12:
                    year += 1
          return datetime(int(year), int(month), int(day), int(hour), 00)
    

相关问题