Python:给定 UTC 中的当前时间,如何确定特定时区一天的开始和结束时间?
我正在使用 Google App Engine,我了解到时区固定为 UTC。我想确定用户本地时区当天的开始和结束时间。基本上,给定 UTC 的当前时间,如何确定当天的开始和结束时间,同时考虑到夏令时转换。
我有一些笨重的示例代码。请注意,我意识到,如果我手动指定日期,我也可以指定明天的日期,但它们只是示例,我想以编程方式确定它。我的主要问题是,如果我将 timedelta 添加到带有时区的日期时间,然后将其标准化(就像 pytz 文档中建议的那样),我会得到一个在夏令时切换期间关闭一小时的日期时间。
代码中没有提及,但最终目标是将这些时间转换回 UTC,这就是为什么了解时区很重要。
#!/usr/bin/python
import datetime
from pytz.gae import pytz
hobart_tz = pytz.timezone('Australia/Hobart')
utc_dt = pytz.utc.localize(datetime.datetime.utcnow())
hobart_dt = utc_dt.astimezone(hobart_tz)
# create a new datetime for the start of the day and add a day to it to get tomorrow.
today_start = datetime.datetime(hobart_dt.year, hobart_dt.month, hobart_dt.day)
today_start = hobart_tz.localize(today_start)
today_end = hobart_tz.normalize(today_start + datetime.timedelta(days=1))
print 'today:', today_start
print ' next:', today_end
print
# gives:
# today: 2011-08-28 00:00:00+10:00
# next: 2011-08-29 00:00:00+10:00
# but say today is a daylight savings changeover.
# after normalisation, we are off by an hour.
dst_finish_2011 = datetime.datetime(2011, 4, 3) # this would come from hobart_dt
dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
next = hobart_tz.normalize(dst_finish_2011 + datetime.timedelta(days=1))
print '2011-04-03:', dst_finish_2011
print '2011-04-04:', next # expect 2011-04-04 00:00:00+10:00
print
# gives
# 2011-04-03: 2011-04-03 00:00:00+11:00
# 2011-04-04: 2011-04-03 23:00:00+10:00 (wrong)
dst_start_2011 = datetime.datetime(2011, 10, 2) # this would come from hobart_dt
dst_start_2011 = hobart_tz.localize(dst_start_2011)
next = hobart_tz.normalize(dst_start_2011 + datetime.timedelta(days=1))
print '2011-10-02:', dst_start_2011
print '2011-10-03:', next # expect 2011-10-03 00:00:00+11:00
print
# gives
# 2011-10-02: 2011-10-02 00:00:00+10:00
# 2011-10-03: 2011-10-03 01:00:00+11:00 (wrong)
# I guess we could ignore the timezone and localise *after* ?
dst_finish_2011 = datetime.datetime(2011, 4, 3) # this would come from hobart_dt
next = dst_finish_2011 + datetime.timedelta(days=1)
# now localise
dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
next = hobart_tz.localize(next)
print '2011-04-03:', dst_finish_2011
print '2011-04-04:', next # expect 2011-04-04 00:00:00+10:00
print
# gives
# 2011-04-03: 2011-04-03 00:00:00+11:00
# 2011-04-04: 2011-04-04 00:00:00+10:00
I'm playing around with Google App Engine and I learned that the timezone is fixed to UTC. I want to determine the start and end time of the current day for the user's local timezone. So basically, given the current time in UTC, how do you determine the start and end time of the current day, taking into account daylight savings changeovers.
I have some clunky example code. Please note that I realise that if I'm manually specifying a date, I could specify tomorrow's date as well, but they are examples and I want to determinte it programatically. My main problem is that if I add a timedelta to a datetime with a timezone and then normalise it (like it is suggested in the pytz docs) I get a datetime which is an hour off during daylight savings switchovers.
Not mentioned in the code, but ultimately the aim is to convert these times back to UTC which is why it's important to be timezone aware.
#!/usr/bin/python
import datetime
from pytz.gae import pytz
hobart_tz = pytz.timezone('Australia/Hobart')
utc_dt = pytz.utc.localize(datetime.datetime.utcnow())
hobart_dt = utc_dt.astimezone(hobart_tz)
# create a new datetime for the start of the day and add a day to it to get tomorrow.
today_start = datetime.datetime(hobart_dt.year, hobart_dt.month, hobart_dt.day)
today_start = hobart_tz.localize(today_start)
today_end = hobart_tz.normalize(today_start + datetime.timedelta(days=1))
print 'today:', today_start
print ' next:', today_end
print
# gives:
# today: 2011-08-28 00:00:00+10:00
# next: 2011-08-29 00:00:00+10:00
# but say today is a daylight savings changeover.
# after normalisation, we are off by an hour.
dst_finish_2011 = datetime.datetime(2011, 4, 3) # this would come from hobart_dt
dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
next = hobart_tz.normalize(dst_finish_2011 + datetime.timedelta(days=1))
print '2011-04-03:', dst_finish_2011
print '2011-04-04:', next # expect 2011-04-04 00:00:00+10:00
print
# gives
# 2011-04-03: 2011-04-03 00:00:00+11:00
# 2011-04-04: 2011-04-03 23:00:00+10:00 (wrong)
dst_start_2011 = datetime.datetime(2011, 10, 2) # this would come from hobart_dt
dst_start_2011 = hobart_tz.localize(dst_start_2011)
next = hobart_tz.normalize(dst_start_2011 + datetime.timedelta(days=1))
print '2011-10-02:', dst_start_2011
print '2011-10-03:', next # expect 2011-10-03 00:00:00+11:00
print
# gives
# 2011-10-02: 2011-10-02 00:00:00+10:00
# 2011-10-03: 2011-10-03 01:00:00+11:00 (wrong)
# I guess we could ignore the timezone and localise *after* ?
dst_finish_2011 = datetime.datetime(2011, 4, 3) # this would come from hobart_dt
next = dst_finish_2011 + datetime.timedelta(days=1)
# now localise
dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
next = hobart_tz.localize(next)
print '2011-04-03:', dst_finish_2011
print '2011-04-04:', next # expect 2011-04-04 00:00:00+10:00
print
# gives
# 2011-04-03: 2011-04-03 00:00:00+11:00
# 2011-04-04: 2011-04-04 00:00:00+10:00
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
要在本地时区中找出一天的开始时间(午夜)和一天的结束时间(明天),并了解 UTC 时间:
输出
另请参阅
To find out the start time of the day (midnight) and the end time of the day (tomorrow) in the local timezone, knowing UTC time:
Output
See also,
我相信您得到这个结果是因为您添加了一天而不是 86400 秒。天和秒之间不存在统一、始终正确的等价关系。例如,如果 pytz 强制一天“实际上”为 86400 秒,则在 12 月 31 日或 6 月 30 日日期中添加一天有时会导致结果的秒字段为“一”第二关”,因为有些年份那些日子有86401秒。 (未来可能有 86402 秒甚至 86399 秒。)
因此,添加一天意味着简单地将一天增加一天,如有必要,可延续到月份和年份,但不更改一天中的时间字段。尝试添加 86400 秒,看看是否得到所需的结果。
I believe you are getting this result because you are adding one day instead of 86400 seconds. There is no uniform, always-correct equivalence between days and seconds. For example, if
pytz
were to force one day to "really" be 86400 seconds, then adding one day to a December 31 or June 30 date would sometimes cause the seconds field of the result to be "one second off", because in some years those days have had 86401 seconds. (It is possible they could have 86402 or even 86399 seconds in the future.)So adding one day is taken to mean simply incrementing the day by one, carrying over to month and year if necessary, but without changing the time-of-day fields. Try adding 86400 seconds and see if you get the desired result.
使用
arrow
,代码会很简单。Use
arrow
, code will be simple.经过一些实验和思考,我相信我有一个适合您的解决方案。正如您指出的那样,我之前的回答并不正确; days=1 的 timedelta 对象与秒=86400 的 timedelta 对象基本相同(除了涉及闰秒的地方)。
我推荐的一种方法是使用
datetime.date
对象而不是datetime.datetime
对象来增加日期而不考虑一天中的时间:然后可以添加一天中的时间以形成完整的
datetime.datetime
对象,其中您知道一天中的时间字段与原始值相比没有改变。另一种方法(我觉得使用起来很安全)是暂时使用“天真的”日期。这样,添加
timedelta
时就无需应用时区策略。我针对包含闰秒的日期(例如 2008-12-31)测试了此方法,结果为一天中的时间 00:00:00。我不确定这实际上可能是错误的,但这就是你想要的:-)
After a little experimentation and thinking I believe I have a solution for you. My previous answer was not correct as you pointed out; a timedelta object for days=1 is basically the same as for seconds=86400 (except where leap seconds are concerned).
One way, the way I would recommend, to increment the date without regards to the time of day is to use a
datetime.date
object instead of adatetime.datetime
object:The time of day could then be added to form a complete
datetime.datetime
object in which you know the time of day fields are unaltered from your original value.Another method, one which I would feel safe using, is to work with "naive" dates temporarily. This way there is no time zone policy to apply while adding the
timedelta
.I tested this method for dates that have leap seconds in them (one example is 2008-12-31) and the result has time of day 00:00:00. Which may actually be wrong, I'm not sure, but it is what you want :-)
以下代码尝试获取午夜时间;如果时区调整失败,它会使用新的时区偏移量重新调整回午夜。
The following code tries to get a time of midnight; if the time zone adjustment makes it fail, it readjusts back to midnight with the new zone offset.