我有一个使用datetime.utcnow()创建的python datetime实例,并持久化在数据库中。

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

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

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


当前回答

使用timedelta在时区之间切换。您所需要的只是时区之间的小时偏移量。不必为datetime对象的所有6个元素设置边界。Timedelta也可以轻松处理闰年、闰世纪等。你必须首先

from datetime import datetime, timedelta

如果offset是时区的delta(以小时为单位):

超时 = 时间 + 时间增量(小时 = 偏移量)

其中timein和timeout是datetime对象。如。

时间 + 时间增量(小时 = -8)

从格林尼治标准时间转换为太平洋标准时间。

那么,如何确定偏移量呢?这里是一个简单的函数,前提是你只有一些转换的可能性,而不使用时区“感知”的datetime对象,而其他一些答案很好地做到了。有点手动,但有时清晰是最好的。

def change_timezone(timein, timezone, timezone_out):
    '''
    changes timezone between predefined timezone offsets to GMT
    timein - datetime object
    timezone - 'PST', 'PDT', 'GMT' (can add more as needed)
    timezone_out - 'PST', 'PDT', 'GMT' (can add more as needed)
    ''' 
    # simple table lookup        
    tz_offset =  {'PST': {'GMT': 8, 'PDT': 1, 'PST': 0}, \
                  'GMT': {'PST': -8, 'PDT': -7, 'GMT': 0}, \
                  'PDT': {'GMT': 7, 'PST': -1, 'PDT': 0}}
    try:
        offset = tz_offset[timezone][timezone_out]
    except:
        msg = 'Input timezone=' + timezone + ' OR output time zone=' + \
            timezone_out + ' not recognized'
        raise DateTimeError(msg)

    return timein + timedelta(hours = offset)

在看了大量的答案和我能想到的最严格的代码之后(目前),似乎最好的是所有应用程序,其中时间是重要的,混合时区必须考虑在内,应该真正努力使所有datetime对象“感知”。那么,最简单的答案似乎是:

timeout = timein.astimezone(pytz.timezone("GMT"))

例如,转换为格林尼治时间。当然,要转换到或从您希望的任何其他时区(本地或其他时区),只需使用pytz理解的适当的时区字符串(来自pytz.all_timezones)。日光节约时间也被考虑在内。

其他回答

根据阿列克谢的评论。这也适用于DST。

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)

使用时间。timezone,它给出一个“UTC以西秒数”的整数。

例如:

from datetime import datetime, timedelta, timezone
import time

# make datetime from timestamp, thus no timezone info is attached
now = datetime.fromtimestamp(time.time())

# make local timezone with time.timezone
local_tz = timezone(timedelta(seconds=-time.timezone))

# attach different timezones as you wish
utc_time = now.astimezone(timezone.utc)
local_time = now.astimezone(local_tz)

print(utc_time.isoformat(timespec='seconds')) 
print(local_time.isoformat(timespec='seconds'))

在我的PC上(Python 3.7.3),它给出:

2021-05-07T12:50:46+00:00
2021-05-07T20:50:46+08:00

非常简单,只使用标准库~

下面是另一种在datetime格式中更改时区的方法(我知道我在这上面浪费了精力,但我没有看到这个页面,所以我不知道怎么做),没有min.和sec.因为我的项目不需要它:

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:
          if not year%4==0:
            if day >= 29:
              day = 1
              month += 1
              if month > 12:
                year += 1
          else:
            if day >= 28:
              day = 1
              month += 1
              if month > 12:
                year += 1
      return datetime(int(year), int(month), int(day), int(hour), 00)

在Python 2和3中工作的简单(但可能有缺陷)方法:

import time
import datetime

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

它的优点是写一个逆函数很简单

在Python 3.3+中:

from datetime import datetime, 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。