Python 3.x 如何创建适用于所有日期的时区感知时间对象?
我已经为这个问题挣扎了一段时间了。我觉得我很了解时区,我想我知道如何正确使用pytz,所以我通常使用它,我通常不会有任何问题。也许我正试图使用错误的工具来满足我的需求 在我当前的应用程序中,我需要在抽象的Python 3.x 如何创建适用于所有日期的时区感知时间对象?,python-3.x,pytz,Python 3.x,Pytz,我已经为这个问题挣扎了一段时间了。我觉得我很了解时区,我想我知道如何正确使用pytz,所以我通常使用它,我通常不会有任何问题。也许我正试图使用错误的工具来满足我的需求 在我当前的应用程序中,我需要在抽象的time对象中工作。也就是说,我关心16点发生的事情。我不在乎它发生在12月23日16:00。pytz似乎更喜欢使用datetime对象:这是有道理的。由于夏时制和历史原因等原因,它无法计算出偏移量,因为它根据日期而不同 我试图让用户协调世界各地的日常活动。如果日本的用户说活动时间是每天21:0
time
对象中工作。也就是说,我关心16点发生的事情。我不在乎它发生在12月23日16:00。pytz似乎更喜欢使用datetime
对象:这是有道理的。由于夏时制和历史原因等原因,它无法计算出偏移量,因为它根据日期而不同
我试图让用户协调世界各地的日常活动。如果日本的用户说活动时间是每天21:00到23:00,我希望美国/中部的用户每天看到6:00到8:00。我还以为我有这个工作。。。直到两周前。看,DST在美国大部分地区刚刚结束,所以现在的6:00-8:00实际上是以前的7:00-9:00
这打破了我的想法,即我通常需要以UTC存储时间,然后转换它们仅用于查看。创建它的时区实际上非常重要。如果我们将其反转,并且美国时区的用户观察夏令时设置事件时间,那么日本的时间需要改变,即使他们不观察!如果我将该时间存储为UTC,在日本没有任何变化,但这不是我想要的功能
因此,我想用tzinfo
存储为time
对象。但是,如果没有日期,则无法创建具有精确pytztzinfo
的时间对象,但日期并不重要。如果我使用当前日期来计算tzinfo
,那么一旦时区发生变化,它实际上就不再准确了
我想我的问题是:存储“东部时间下午4点”的最佳方式是什么?这种方式可以在世界任何地方,在未来的任何时候检索到?包括东部!我希望在夏令时和夏令时以外的时间是下午4点。我不能将其存储为UTC,因为UTC 12:00在整个一年中与UTC 12:00的时间相同,但我不希望这样。我想我想要的是一个“抽象的”或“临时的”pytz.timezone
,在给出日期(查看日期)之前没有实际的偏移量。这是一件事吗?我在这个网站上读了无数的问题,包括python和pytz文档,但找不到类似的问题,也找不到任何有类似问题的人。一切似乎都在谈论特定的日期时间
或只在日期时间
内工作,但这似乎与我的问题无关
我的应用程序非常庞大,因此很难取出特定的部分,但我可以尝试展示我尝试过的内容以及为什么不起作用
event\u time=datetime.time(hour=12,tzinfo=pytz.timezone(“美国/东部”))
将是我理想的解决方案。但是使用pytz创建tzinfo不是一个好主意(由于历史原因,这会给我一个类似-5:04的偏移量)-有没有办法指定要使用的US/Eastern版本
datetime.now(pytz.timezone(“US/Eastern”)).replace(小时=12,分钟=0,秒=0,微秒=0)。timetz()
提供了一些看起来像我想要的东西,但只要US/Eastern没有改变,它就可以正常工作。如果我将此应用于DST更改之前我已移回的日期,它将为我提供13:00,这不是我想要的。看起来pytz通过“本地化”方法提供了此功能,如`
从:
如果您坚持与《当地时报》合作,该图书馆将提供一个明确构建它们的工具:
eastern = timezone('US/Eastern')
loc_dt = datetime(2002, 10, 27, 1, 30, 00)
est_dt = eastern.localize(loc_dt, is_dst=True)
edt_dt = eastern.localize(loc_dt, is_dst=False)
print(est_dt.strftime(fmt) + ' / ' + edt_dt.strftime(fmt))
2002-10-27 01:30:00 EDT-0400 / 2002-10-27 01:30:00 EST-0500`
通过存储本地时间和本地时区,您可以在使用时将其转换,并将其转换为另一个地区的时区。我构建了一个tzinfo类,自动处理DST的转换。我是从datetime文档中的USTimeZone类示例中得到这个想法的 这里的诀窍是pytz时区数据库拥有夏令时生效的所有历史日期。这也是为什么当您创建一个没有日期的datetime对象时,它会错误地转换DST;它基于数据库中的第一个条目
from datetime import datetime, tzinfo, timedelta
import pytz
ZERO = timedelta(0)
def format_timedelta(td):
if td < timedelta(0):
return '-' + format_timedelta(-td)
else:
# Change this to format positive timedeltas the way you want
return str(td)
class WorldTimeZone(tzinfo):
"""
A self adjusting according to DST rules in the PYTZ database tzinfo class
See pytz.all_timezones for a list of all zone names and offsets.
"""
def __init__(self, zone):
"""
:param zone: (str) Proper tzdatabase timze zone name.
"""
# initialize the pytz timezone with current time
# this is done to avoid confusing tznames found in the start of the tz database
# _utcoffset should always be STD rather than DST.
self.pytzinfo = self.__getSTD(zone)
self._utcoffset = self.pytzinfo._utcoffset
@staticmethod
def __getSTD(tname):
"""
This returns a pytz timezone object normalized to standard time for the zone requested.
If the zone does not follow DST or a future transition time cannot be found, it normalizes to NOW instead.
:param tname: Proper timezone name found in the tzdatabase. example: "US/Central"
"""
# This defaults to the STD time for the zone rather than current time which could be DST
tzone = pytz.timezone(tname)
NOW = datetime.now(tz=pytz.UTC)
std_date = NOW
hasdst = False
try:
#transitions are in UTC. They need to be converted to localtime once we find the correct STD transition.
for utcdate, info in zip(tzone._utc_transition_times, tzone._transition_info):
utcdate = utcdate.replace(tzinfo=pytz.UTC)
utcoffset, dstoffset, tzname = info
if dstoffset == ZERO:
std_date = utcdate
if utcdate > NOW:
hasdst = True
break
except AttributeError:
std_date = NOW
if not hasdst:
std_date = NOW
std_date = tzone.normalize(std_date)
return std_date.tzinfo
# This needs to be dynamic because pytzinfo updates everytime .dst() is called; which is a lot.
@property
def _dst(self):
return self.pytzinfo._dst
def __repr__(self):
# return self.pytzinfo.__repr__()
if self._dst:
dst = 'DST'
else:
dst = 'STD'
if self._utcoffset > timedelta(seconds=0):
msg = '<WorldTimeZone %r %s+%s %s>'
else:
msg = '<WorldTimeZone %r %s%s %s>'
return msg % (self.pytzinfo.zone, self.pytzinfo._tzname,
format_timedelta(self._utcoffset + self._dst), dst)
def __str__(self):
return "%s %s" % (self.pytzinfo._tzname, self.pytzinfo)
def tzname(self, dt):
# print " TZNAME called"
return "%s %s" % (self.pytzinfo._tzname, self.pytzinfo)
def utcoffset(self, dt):
# print " UTCOFFSET CALLED"
return self._utcoffset + self.dst(dt)
def dst(self, dt):
# print " DST CALLED"
if dt is None or dt.tzinfo is None:
# An exception may be sensible here, in one or both cases.
# It depends on how you want to treat them. The default
# fromutc() implementation (called by the default astimezone()
# implementation) passes a datetime with dt.tzinfo is self.
return ZERO
assert dt.tzinfo is self # WE ASSUME THE TZINFO ON THE DATE PASSED IN IS OUR TZINFO OBJECT.
tmpdt = self.pytzinfo.normalize(dt)
self.pytzinfo = tmpdt.tzinfo
return tmpdt.tzinfo._dst
输出
2018-11-01 01:30:00 >> 2018-11-01 01:30:00-04:00 >> 2018-10-31 22:30:00-07:00
2018-11-06 01:30:00 >> 2018-11-06 01:30:00-05:00 >> 2018-11-05 22:30:00-08:00
我会尽力解释你的建议,因为我不知道这是否解决了我的问题。我想我想要一个模棱两可的时间。建议存储定期事件创建时的确切日期时间(例如,11月20日,东部时间下午4:00),然后,在计算以后显示的时间时,首先获取相关的小时,使用创建时使用的原始时区名称(可能是不同的偏移量)构造新的日期时间与该小时一起,然后使用新的
Datetime
计算查看器的显示时间?我想这可能行得通……嗨,伙计,我想你解释得对。在您的示例中,您希望下午4点的约会保持在下午4点,即使DST发生变化。这将如何影响在非DST时区的预约,或者这个问题是否会出现?例如,美国东部时区和日本的用户会与同一事件交互吗?是的,没错!每个人都应该在同一时间一起参加同一个活动,但对于创造者来说,无论DST如何,它都将始终在定义的时间进行;我的想法是存储没有日期的“loc_dt”,检查DST,然后用正确的DST值本地化事件创建者的本地_dt。最后,这个时间可以转换为活动参与者的其他时区。Marcel的答案处理了自定义类中的所有歧义。这太棒了!我想你已经涵盖了一切。非常感谢你。
2018-11-01 01:30:00 >> 2018-11-01 01:30:00-04:00 >> 2018-10-31 22:30:00-07:00
2018-11-06 01:30:00 >> 2018-11-06 01:30:00-05:00 >> 2018-11-05 22:30:00-08:00