我正在玩谷歌应用程序引擎,我了解到时区是固定到UTC。我想确定用户的本地时区当前一天的开始和结束时间。因此,基本上,给定UTC的当前时间,如何确定当前一天的开始和结束时间,同时考虑到夏令时的转换。
我有一些笨拙的示例代码。请注意,我意识到,如果我手动指定日期,我也可以指定明天的日期,但它们是示例,我想以编程方式确定它。我的主要问题是,如果我在带有时区的日期时间中添加时间增量,然后对其进行规范化(就像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
在知道UTC时间的情况下,查找当地时区一天的开始时间(午夜)和一天的结束时间(明天):
#!/usr/bin/env python
from datetime import datetime, time, timedelta
import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal
tz = get_localzone() # get the local timezone as pytz.timezone
now = datetime.now(pytz.utc) # some UTC time
dt = now.astimezone(tz) # the same time in the local timezone
today = dt.date() # today in the local timezone (naive date object)
midnight = datetime.combine(today, time()) # midnight in the local timezone
aware_midnight = tz.localize(midnight, is_dst=None) # raise exception
# for ambiguous or
# non-existing
# times
tomorrow = midnight + timedelta(1)
aware_tomorrow = tz.localize(tomorrow, is_dst=None)
def print_time(aware_dt, fmt="%Y-%m-%d %H:%M:%S %Z%z"):
print(aware_dt.strftime(fmt))
utc_dt = aware_dt.astimezone(pytz.utc) # the same time in UTC
print(utc_dt.strftime(fmt))
print_time(aware_midnight)
print_time(aware_tomorrow)
输出2014-09-01 00:00:00 EST+1000
2014-08-31 14:00:00 UTC+0000
2014-09-02 00:00:00 EST+1000
2014-09-01 14:00:00 UTC+0000
参见
- 如何获得给定时区的"午夜"的UTC时间?——只有午夜(不是明天)
- 如何从python日期中减去一天?——关于一天前和昨天的差异
- python - datetime与timezone到epoch——演示如何容易产生错误的答案(对于某些日期,时区)。
我相信你得到这个结果是因为你增加了一天而不是86400秒。日与秒之间没有统一的、永远正确的等价物。例如,如果pytz
要强制一天"实际"为86400秒,那么在12月31日或6月30日的日期上添加一天有时会导致结果的秒字段"差一秒",因为在某些年份,这些日子有86401秒。(将来可能会有86402秒,甚至86399秒)
因此,添加一天的意思是简单地将一天增加1,如果需要的话,可以转到月和年,但不更改时间字段。尝试添加86400秒,看看是否能得到期望的结果。
经过一些实验和思考,我相信我有一个解决方案。我之前的回答不像你指出的那样正确;days=1的timedelta对象与seconds=86400的timedelta对象基本相同(除了涉及闰秒)。
一种方法,我推荐的方法,增加日期而不考虑一天中的时间是使用datetime.date
对象而不是datetime.datetime
对象:
>>> oneday = datetime.timedelta(days=1)
>>> d = datetime.date(2011,4,3)
>>> str(d + oneday)
'2011-04-04'
可以添加一天的时间,形成一个完整的datetime.datetime
对象,其中您知道一天的时间字段与原始值相比没有改变。
另一种我觉得比较安全的方法是暂时处理"天真"的约会对象。这样,在添加timedelta
时就不需要应用时区策略了。
>>> hob = pytz.timezone('Australia/Hobart')
>>> dstlast = datetime.datetime(2011,4,3)
>>> str(dstlast)
'2011-04-03 00:00:00'
>>> dstlasthob = hob.localize(dstlast)
>>> str(dstlasthob)
'2011-04-03 00:00:00+11:00'
>>> oneday = datetime.timedelta(days=1)
>>> str(hob.normalize(dstlasthob + oneday))
'2011-04-03 23:00:00+10:00'
>>> nextday = hob.localize(dstlasthob.replace(tzinfo=None) + oneday)
>>> str(nextday)
'2011-04-04 00:00:00+10:00'
我对包含闰秒的日期(一个例子是2008-12-31)测试了这个方法,结果是一天的时间为00:00:00。这实际上可能是错误的,我不确定,但这是你想要的:-)
下面的代码尝试获取午夜的时间;如果时区调整使其失败,它将重新调整回带有新区域偏移量的午夜。
def DayStartEnd(localized_dt):
tz = localized_dt.tzinfo
start = tz.normalize(datetime.datetime(localized_dt.year,localized_dt.month,localized_dt.day,0,0,0,0,tz))
after_midnight = start.hour*60*60 + start.minute*60 + start.second
if start.day != localized_dt.day:
start += datetime.timedelta(seconds = 24*60*60 - after_midnight)
elif after_midnight != 0:
start -= datetime.timedelta(seconds = after_midnight)
end = tz.normalize(start + datetime.timedelta(hours=24))
after_midnight = end.hour*60*60 + end.minute*60 + end.second
if end.day == localized_dt.day:
end += datetime.timedelta(seconds = 24*60*60 - after_midnight)
elif after_midnight != 0:
end -= datetime.timedelta(seconds = after_midnight)
return start,end
>>> hobart_tz = pytz.timezone('Australia/Hobart')
>>> dst_finish_2011 = datetime.datetime(2011, 4, 3)
>>> dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
>>> start,end = DayStartEnd(dst_finish_2011)
>>> print start,end
2011-04-03 00:00:00+11:00 2011-04-04 00:00:00+10:00
>>> dst_start_2011 = datetime.datetime(2011, 10, 2)
>>> dst_start_2011 = hobart_tz.localize(dst_start_2011)
>>> start,end = DayStartEnd(dst_start_2011)
>>> print start,end
2011-10-02 00:00:00+10:00 2011-10-03 00:00:00+11:00