简体   繁体   English

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

[英]Elegant way to adjust date timezones in Python

I'm based in the UK, and grappling with summer time BST and timezones. 我的总部设在英国,并且正在努力应对夏令时BST和时区。

Here's my code: 这是我的代码:

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

Setting a variable like this feels very wrong, but I can't find any elegant way to achieve the above without hideous amounts of code. 设置这样的变量感觉非常错误,但我找不到任何优雅的方法来实现上述而没有大量的代码。 Am I missing something, and is there no way to (for example) read the system timezone? 我错过了什么,是否有办法(例如)读取系统时区?

To convert UTC to given timezone: 要将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

See also How to convert a python utc datetime to a local datetime using only python standard library? 另请参见如何仅使用python标准库将python utc datetime转换为本地日期时间? .

You seem to be asking a few separate questions here. 你似乎在这里问几个单独的问题。

First, if you only care about your own machine's current local timezone, you don't need to know what it is. 首先,如果您只关心自己机器当前的本地时区,则无需知道它是什么。 Just use the local-to-UTC functions. 只需使用local-to-UTC功能即可。 There are a few holes in the API, but even if you can't find the function you need, you can always just get from local to UTC or vice-versa by going through the POSIX timestamp and the fromtimestamp and utcfromtimestamp methods. API中有一些漏洞,但即使您找不到所需的功能,也可以通过POSIX时间戳和fromtimestamp以及utcfromtimestamp方法从本地到UTC,反之亦然。

If you want to be able to deal with any timezone, see the top of the docs for the difference between aware and naive objects, but basically: an aware object is one that knows its timezone. 如果您希望能够处理任何时区,请参阅文档的顶部以了解有意识和天真对象之间的区别,但基本上:知道对象是知道其时区的对象。 So, that's what you need. 所以,这就是你所需要的。 The problem is that, as the docs say: 问题在于,正如文档所说:

Note that no concrete tzinfo classes are supplied by the datetime module. 请注意,datetime模块不提供具体的tzinfo类。 Supporting timezones at whatever level of detail is required is up to the application. 在任何详细程度都需要支持时区取决于应用程序。

The easiest way to support timezones is to install and use the third-party library pytz . 支持时区的最简单方法是安装和使用第三方库pytz

Meanwhile, as strftime() and strptime() Behavior sort-of explains, strptime always returns a naive object. 同时,作为strftime()strptime()行为排序解释, strptime总是返回一个天真的对象。 You then have to call replace and/or astimezone (depending on whether the string was a UTC time or a local time) to get an aware object imbued with the right timezone. 然后,您必须调用replace和/或astimezone (取决于字符串是UTC时间还是本地时间)以获得充满正确时区的感知对象。

But, even with all this, you still need to know what local timezone you're in, which means you still need a constant. 但是,即使有了这一切,你仍然需要知道你所在的当地时区,这意味着你仍然需要一个常数。 In other words: 换一种说法:

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

So, how do you get the OS to give you the local timezone? 那么,你如何让操作系统为你提供当地时区? Well, that's different for different platforms, and Python has nothing built in to help. 嗯,这对于不同的平台来说是不同的,并且Python没有内置任何帮助。 But there are a few different third-party libraries that do, such as dateutil . 但是有一些不同的第三方库可以做到,比如dateutil For example: 例如:

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

But now we've come full circle. 但是现在我们已经完全循环了。 If all you wanted was the local timezone, you didn't really need the timezone at all (at least for your simple use case). 如果您想要的只是本地时区,那么您根本不需要时区(至少对于您的简单用例)。 So, this is only necessary if you need to support any timezone, and also want to be able to, eg, default to your local timezone (without having to write two copies of all of your code for the aware and naive cases). 因此,只有在您需要支持任何时区时才需要这样做,并且希望能够(例如)默认为您的本地时区(无需为知晓和幼稚案例编写所有代码的两个副本)。

(Also, if you're going to use dateutil in the first place, you might want to use it for more than just getting the timezone—it can basically replacing everything you're doing with both datetime and pytz .) (另外,如果你打算首先使用dateutil ,你可能想要使用它不仅仅是获取时区 - 它基本上可以用datetimepytz替换你正在做的所有事情。)

Of course there are other options besides these libraries—search PyPI, Google, and/or the ActiveState recipes. 当然除了这些库之外还有其他选项 - 搜索PyPI,Google和/或ActiveState配方。

If you want to convert a UTC input into a local time, regardless of which timezone you're in, try this: 如果要将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)

Quite long code, but what it does is it simply adds the difference between time.gmtime() and time.localtime() to the time tuple created from the input. 相当长的代码,但它的作用是简单地将time.gmtime()和time.localtime()之间的差异添加到从输入创建的时间元组。

Here's a function I use to do what I think you want. 这是我用来做我想你想要的功能。 This assumes that the input is really a gmt, or more precisely, a utc datetime object: 这假设输入实际上是一个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))

Like you said, this relies on the system time zone, which may give you shaky results, as some of the comments have pointed out. 就像你说的那样,这依赖于系统时区,这可能会给你带来不稳定的结果,正如一些评论所指出的那样。 It has worked perfectly for me on Windows, however. 但是,它在Windows上对我来说非常完美。

A simple function to check if a UCT corresponds to BST in London or GMT (for setting TIME_OFFSET above) 一个简单的函数,用于检查UCT是否与伦敦或GMT中的BST相对应(用于设置上面的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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM