簡體   English   中英

Python:鑒於當前UTC時間,您如何確定特定時區內一天的開始和結束時間?

[英]Python: Given the current time in UTC, how do you determine the start and end time of the day in a particular timezone?

我正在玩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

要了解當地時區的當天(午夜)的開始時間和當天(明天)的結束時間,請了解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

也可以看看,

我相信你得到這個結果是因為你增加了一天而不是86400秒。 天與秒之間沒有統一的,始終正確的等價。 例如,如果pytz強制一天“真正”為86400秒,那么將一天添加到12月31日或6月30日有時會導致結果的秒字段“一秒鍾關閉”,因為在某些情況下那些日子已經有86401秒了。 (未來可能會有86402甚至86399秒。)

因此,添加一天意味着簡單地將日期遞增1,如果需要,可以延續到月份和年份,但不會更改時間字段。 嘗試添加86400秒,看看是否得到了想要的結果。

經過一些實驗和思考,我相信我有一個解決方案。 你指出我以前的答案是不正確的; time = 1的timedelta對象與seconds = 86400基本相同(除了涉及閏秒的情況)。

一種方式,我建議的方式,增加日期而不考慮一天中的時間是使用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM