pytz:仅从GMT偏移返回奥尔森时区名称

3
我需要为现有的一款应用程序补充一些数据。目前,我们有一个存储美国(和其领地)邮政编码的数据库表,以及一个GMT偏移量和一个标志,显示该邮政编码是否使用夏令时。这是从某个免费提供者下载的,但我现在找不到来源。
我现在需要用每个邮政编码的完整奥尔森名称(例如America/New York)来补充这个表,因为这似乎是将数据库中以本地时间存储的给定日期/时间转换为UTC感知日期时间对象的唯一好方法。
以下是表格的内容:
zip    state  city          lat      lon       gmt  dst 
00605  PR     AGUADILLA     18.4372  -67.1593  -4   f
02830  RI     HARRISVILLE   41.9782  -71.7679  -5   t
99503  AK     ANCHORAGE     61.1895  -149.874  -9   t

在另一个相关的表中,Purchases,我有一列timestamp without tz,它目前包含类似于2014-05-27T15:54:26的内容,表示某个时区的购买时间。(忽略将这些本地化的时间戳保存到数据库时剥离时区信息的愚蠢方法)
关键问题是:
如何为zipcode表中的每个邮政编码从该timestamp字符串创建规范化的UTC时间?假设该时间戳被写入数据库时是相对于zipcode表中每个示例行的本地时间。
例如,手动查找示例表中每个项目的Olson时区名称,我得出以下结果:
>>> timestring = '2014-05-27T15:54:26'
>>> dt_naive = datetime.strptime(timestring, '%Y-%m-%dT%H:%M:%S')

>>> # First example - Puerto Rico (no DST since 1945)
>>> print pytz.utc.normalize(pytz.timezone('America/Puerto_Rico').localize(dt_naive))
2014-05-27 19:54:26+00:00

# Second example - Road Island (At that timestamp, UTC Offset was same as PR because of DST)
>>> print pytz.utc.normalize(pytz.timezone('US/Eastern').localize(dt_naive))
>>> 2014-05-27 19:54:26+00:00

# Third Example - Anchorage, AK (AKDT at timestamp)
>>> print pytz.utc.normalize(pytz.timezone('America/Anchorage').localize(dt_naive))
2014-05-27 23:54:26+00:00

我看到有几个商业产品出售邮政编码数据库,可以给我一个邮政编码 -> 时区的查询。然而,它们似乎只给我一个给定时区的“EST”。因此,我认为我可以将美国时区(包括领土)的可能时区列表映射到每个奥尔森名称。这可能看起来像这样:

zipcode_olson_lookup = {
    ('PR', 'f', 'AST'): 'America/Puerto_Rico',
    ('AK', 'f', 'AKDT',): 'America/Anchorage',
    ('AK', 't', 'AKT',): 'America/Anchorage',
    ...
}

任何建议都非常受欢迎!

听起来你已经想通了,那你还需要我们做什么? - Mark Ransom
无关的:现在它可以工作了,但你不应该在这里使用 pytz.utc.normalize(),而应该使用 .astimezone(pytz.utc) - jfs
1个回答

2

仅使用UTC偏移量可能存在歧义(它可能对应于几个时区,在某些时间段可能具有不同的规则):

#!/usr/bin/env python
from datetime import datetime, timedelta
import pytz # $ pip install pytz

input_utc_offset = timedelta(hours=-4)
timezone_ids = set()
now = datetime.now(pytz.utc) #XXX: use date that corresponds to input_utc_offset instead!
for tz in map(pytz.timezone, pytz.all_timezones_set):
    dt = now.astimezone(tz)    
    tzinfos = getattr(tz, '_tzinfos',
                      [(dt.tzname(), dt.dst(), dt.utcoffset())])        
    if any(utc_offset == input_utc_offset for utc_offset, _, _ in tzinfos):
        # match timezones that have/had/will have the same utc offset 
        timezone_ids.add(tz.zone)
print(timezone_ids)

输出

{'America/Anguilla',
 'America/Antigua',
 'America/Argentina/Buenos_Aires',
 ...,
 'Cuba',
 'EST5EDT',
 'Jamaica',
 'US/East-Indiana',
 'US/Eastern',
 'US/Michigan'}

您甚至不能使用pytz.country_timezones['us']来限制列表,因为它将排除您的一个示例:'America/Puerto_Rico'


如果您知道坐标(纬度,经度); 您可以从形状文件中获取时区ID:您可以使用本地数据库或Web服务

#!/usr/bin/env python
from geopy import geocoders # pip install "geopy[timezone]"

g = geocoders.GoogleV3()
for coords in [(18.4372,  -67.159), (41.9782,  -71.7679), (61.1895,  -149.874)]:
    print(g.timezone(coords).zone)

输出

America/Puerto_Rico
America/New_York
America/Anchorage

注意:一些本地时间可能存在歧义,例如在夏令时结束期间时间回退时。您可以传递 is_dst=None.localize() 方法,在这种情况下引发异常。
不同版本的tz数据库可能在某些日期的某些时区具有不同的UTC偏移量,即仅存储UTC时间和时区ID是不够的(使用哪个版本取决于您的应用程序)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接