2008-12-17 101 views
32

最好我可以想出現在是這個畸形:如何獲得給定時區的「午夜」的UTC時間?

>>> datetime.utcnow() \ 
... .replace(tzinfo=pytz.UTC) \ 
... .astimezone(pytz.timezone("Australia/Melbourne")) \ 
... .replace(hour=0,minute=0,second=0,microsecond=0) \ 
... .astimezone(pytz.UTC) \ 
... .replace(tzinfo=None) 
datetime.datetime(2008, 12, 16, 13, 0) 

也就是說,在英國,獲取當前時間(UTC),將其轉換爲其他時區,設置時間爲午夜然後,轉換回UTC。

我不只是使用now()或localtime(),因爲它會使用服務器的時區,而不是用戶的時區。

我不禁感到我錯過了什麼,有什麼想法?

回答

46

我覺得你可以刮掉一些方法調用,將在這一天錯誤的結果切換到夏令時或從夏令時開始。

原因是,datetime構造函數和replace()都沒有考慮到DST更改。

例如:

>>> now = datetime(2012, 4, 1, 5, 0, 0, 0, tzinfo=pytz.timezone("Australia/Melbourne")) 
>>> print now 
2012-04-01 05:00:00+10:00 
>>> print now.replace(hour=0) 
2012-04-01 00:00:00+10:00 # wrong! midnight was at 2012-04-01 00:00:00+11:00 
>>> print datetime(2012, 3, 1, 0, 0, 0, 0, tzinfo=tz) 
2012-03-01 00:00:00+10:00 # wrong again! 

然而,對於tz.localize()狀態的文檔:

此方法應被用於構建localtimes,而 不是傳遞一個tzinfo參數爲日期時間的構造。

因此,你的問題就解決了,像這樣:

>>> import pytz 
>>> from datetime import datetime, date, time 

>>> tz = pytz.timezone("Australia/Melbourne") 
>>> the_date = date(2012, 4, 1) # use date.today() here 

>>> midnight_without_tzinfo = datetime.combine(the_date, time()) 
>>> print midnight_without_tzinfo 
2012-04-01 00:00:00 

>>> midnight_with_tzinfo = tz.localize(midnight_without_tzinfo) 
>>> print midnight_with_tzinfo 
2012-04-01 00:00:00+11:00 

>>> print midnight_with_tzinfo.astimezone(pytz.utc) 
2012-03-31 13:00:00+00:00 

的日期不能保證1582之前,雖然。

0

設置TZ環境變量會修改Python的日期和時間函數使用的時區。

>>> time.gmtime() 
(2008, 12, 17, 1, 16, 46, 2, 352, 0) 
>>> time.localtime() 
(2008, 12, 16, 20, 16, 47, 1, 351, 0) 
>>> os.environ['TZ']='Australia/Melbourne' 
>>> time.localtime() 
(2008, 12, 17, 12, 16, 53, 2, 352, 1) 
+0

除了不想使用TZ變量來控制這個,實際上並沒有告訴我如何找到午夜,只是當前時間。 – Tom 2008-12-17 01:38:49

0

每個時區都有一個數字,例如US/Central = -6。這被定義爲距UTC的時間偏移量。由於0000是午夜,因此您可以簡單地使用此偏移量在UTC時間午夜時分查找任何時區中的時間。要訪問,我相信你可以使用

 time.timezone

The Python Docs,time.timezone實際上給這個數的負值:

time.timezone

的偏移當地(非DST)時區,以UTC爲單位的秒數(西歐大部分地區爲負值,美國爲正值,英國爲零)。

所以,你會簡單地使用數以小時爲單位的時間,如果是積極的(即,如果它在芝加哥(其中有一個+6區值午夜),那麼它的6000 =上午6時UTC) 。

如果數字爲負值,則從24中減去。例如,柏林將給出-1,因此24-1 => 2300 = 11pm。

>>> from datetime import datetime 
>>> datetime.now(pytz.timezone("Australia/Melbourne")) \ 
      .replace(hour=0, minute=0, second=0, microsecond=0) \ 
      .astimezone(pytz.utc) 

但是...有比你的代碼美學一個更大的問題:如果你不喜歡這樣

+0

我認爲你在正確的軌道上,但你怎麼知道什麼日期開始?即幾個小時前,它在墨爾本的第17位,而它仍然是UTC的第16位。 – Tom 2008-12-17 01:40:20

+0

問題是關於當地的午夜。日間關係由時區的UTC偏移修正 - 在當地午夜。 – 2008-12-17 03:06:38

+0

手動添加/減少tz差異可能會在從DST到DST的切換周圍出現問題 – hop 2008-12-18 01:40:41

22

@hop's answer是錯誤的過渡,從夏令時(DST),例如,4月1日,2012年的一天要修復它tz.localize()可用於:

tz = pytz.timezone("Australia/Melbourne") 
today = datetime.now(tz).date() 
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None) 
utc_dt = midnight.astimezone(pytz.utc)   

同樣與評論:

#!/usr/bin/env python 
from datetime import datetime, time 
import pytz # pip instal pytz 

tz = pytz.timezone("Australia/Melbourne") # choose timezone 

# 1. get correct date for the midnight using given timezone. 
today = datetime.now(tz).date() 

# 2. get midnight in the correct timezone (taking into account DST) 
#NOTE: tzinfo=None and tz.localize() 
# assert that there is no dst transition at midnight (`is_dst=None`) 
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None) 

# 3. convert to UTC (no need to call `utc.normalize()` due to UTC has no 
# DST transitions) 
fmt = '%Y-%m-%d %H:%M:%S %Z%z' 
print midnight.astimezone(pytz.utc).strftime(fmt)