在Python中调整日期时区的优雅方式



我住在英国,正在努力适应夏令时和时区。

下面是我的代码:

TIME_OFFSET = 1 # 0 for GMT, 1 for BST
def RFC3339_to_localHHMM(input):
                 # Take an XML date (2013-04-08T22:35:00Z)
                 # return e.g. 08/04 23:35
                 return (datetime.datetime.strptime(input, '%Y-%m-%dT%H:%M:%SZ') +
datetime.timedelta(hours=TIME_OFFSET)).strftime('%d/%m %H:%M')
像这样设置变量感觉非常错误,但我找不到任何优雅的方法来实现上面的目标,而不需要大量的代码。我是否错过了什么,是否没有办法(例如)读取系统时区?

将UTC转换为给定的时区:

from datetime import datetime
import pytz
local_tz = pytz.timezone("Europe/London") # time zone name from Olson database
def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
rfc3339s = "2013-04-08T22:35:00Z"
utc_dt = datetime.strptime(rfc3339s, '%Y-%m-%dT%H:%M:%SZ')
local_dt = utc_to_local(utc_dt)
print(local_dt.strftime('%d/%m %H:%M')) # -> 08/04 23:35

请参见如何仅使用python标准库将python utc日期时间转换为本地日期时间?

你好像问了几个不同的问题。

首先,如果你只关心你自己的机器当前的本地时区,你不需要知道它是什么。只需使用local-to-UTC函数。API中有一些漏洞,但即使你找不到你需要的函数,你也可以通过POSIX时间戳和fromtimestamputcfromtimestamp方法从本地到UTC或反之亦然。

如果你希望能够处理任何时区,请参阅文档顶部的感知对象和朴素对象之间的区别,但基本上:感知对象是知道其时区的对象。这就是你需要的。问题是,正如文档所说:

注意datetime模块没有提供具体的tzinfo类。支持时区的详细程度取决于应用程序。

支持时区最简单的方法是安装并使用第三方库pytz

同时,正如strftime()strptime()的行为所解释的那样,strptime总是返回一个朴素对象。然后,您必须调用replace和/或astimezone(取决于字符串是UTC时间还是本地时间)来获得具有正确时区的感知对象。

但是,即使有了这些,您仍然需要知道您所在的当地时区,这意味着您仍然需要一个常量。换句话说:

TIMEZONE = pytz.timezone('Europe/London')
def RFC3339_to_localHHMM(input):
    # Take an XML date (2013-04-08T22:35:00Z)
    # return e.g. 08/04 23:35
    utc_naive = datetime.datetime.strptime(input, '%Y-%m-%dT%H:%M:%SZ')
    utc = utc_naive.replace(pytz.utc)
    bst = utc.astimezone(TIMEZONE)
    return bst.strftime('%d/%m %H:%M')

那么,如何让操作系统给你本地时区呢?好吧,这对于不同的平台是不同的,Python没有内置任何帮助。但是有一些不同的第三方库可以这样做,比如dateutil。例如:

def RFC3339_to_localHHMM(input):
    # Take an XML date (2013-04-08T22:35:00Z)
    # return e.g. 08/04 23:35
    utc = datetime.datetime.strptime(input, '%Y-%m-%dT%H:%M:%SZ')
    bst = utc.astimezone(dateutil.tz.tzlocal())
    return bst.strftime('%d/%m %H:%M')

但是现在我们又回到了原点。如果您想要的只是本地时区,那么实际上根本不需要时区(至少对于您的简单用例来说是这样)。因此,只有当您需要支持任何时区,并且也希望能够,例如,默认为您的本地时区时,才需要这样做(无需为感知和非感知情况编写所有代码的两个副本)。

(另外,如果您打算首先使用dateutil,您可能希望使用它不仅仅是获取时区-它基本上可以取代您使用datetimepytz所做的一切。)

当然,除了这些库之外,还有其他选择——搜索PyPI、Google和/或ActiveState食谱。

如果您想将UTC输入转换为本地时间,而不管您在哪个时区,请尝试以下操作:

def utctolocal(input):
    if time.localtime()[-1] == 1: st=3600
    else: st=0
    return time.localtime(time.time()-time.mktime(time.gmtime())+time.mktime(time.localtime(time.mktime(time.strptime(input, '%Y-%m-%dT%H:%M:%SZ'))))+st)

相当长的代码,但它所做的只是将time.gmtime()和time.localtime()之间的差值添加到从输入创建的时间元组中。

这是我用来做我认为你想要的一个函数。这假定输入实际上是一个gmt,或者更准确地说,是一个utc datetime对象:

def utc_to_local(utc_dt):
    '''Converts a utc datetime obj to local datetime obj.'''
    t = utc_dt.timetuple()
    secs = calendar.timegm(t)
    loc = time.localtime(secs)
    return datetime.datetime.fromtimestamp(time.mktime(loc))

就像你说的,这依赖于系统时区,这可能会给你不稳定的结果,正如一些评论所指出的。不过,它在我的Windows系统上运行得非常好。

检查UCT是否对应于伦敦的BST或GMT(用于设置上面的TIME_OFFSET)的简单函数

import datetime
def is_BST(input_date):
    if input_date.month in range(4,9):
        return True
    if input_date.month in [11,12,1,2]:
        return False
    # Find start and end dates for current year
    current_year = input_date.year
    for day in range(25,32):
        if datetime.datetime(current_year,3,day).weekday()==6:
            BST_start = datetime.datetime(current_year,3,day,1)
        if datetime.datetime(current_year,10,day).weekday()==6:
            BST_end = datetime.datetime(current_year,10,day,1)
    if (input_date > BST_start) and (input_date < BST_end):
        return True
    return False

相关内容

  • 没有找到相关文章

最新更新