2017-10-19 194 views
3

我不想使用pytz庫作爲我正在處理的項目需要文書工作來引入依賴關係。如果我能在沒有第三方圖書館的情況下實現這一目標,我會更開心。如何轉換CEST和UTC時區之間的日期時間對象

當日期處於夏時制時,我無法在CET和UTC之間轉換日期。這是不同的一個小時,我的期望:

>>> print from_cet_to_utc(year=2017, month=7, day=24, hour=10, minute=30) 
2017-07-24T09:30Z 

2017-07-24T08:30Z # expected 

CET比UTC早一個小時,在夏季領先2小時。所以我希望在仲夏的中歐10:30時間到達8:30 UTC。

功能是:

from datetime import datetime, tzinfo, timedelta 

def from_cet_to_utc(year, month, day, hour, minute): 
    cet = datetime(year, month, day, hour, minute, tzinfo=CET()) 
    utc = cet.astimezone(tz=UTC()) 
    return '{:%Y-%m-%d:T%H:%MZ}'.format(utc) 

我用兩個時區信息對象:

class CET(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(hours=1) 

    def dst(self, dt): 
     return timedelta(hours=2) 

class UTC(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(0) 

    def dst(self, dt): 
     return timedelta(0) 

回答

3

只是補充@Sergey's answer

要知道什麼時候應用DST,您應該獲得DST開始和結束時的日期/時間的歷史數據,並檢查它是否必須應用於指定的datetime。您可以從IANA database獲得這些信息,或者諮詢許多在線來源,例如timeanddate website

另一個細節是CET是一個ambiguous name,因爲它使用by more than one timezone。實時時區名稱使用the ones defined by IANA database(始終格式爲Region/City,如Europe/ParisEurope/Berlin)。


空白和重疊

我將使用Europe/Berlin timezone作爲一個例子。在今年(2017年),德國柏林DST於3月26日開始:凌晨2點,時鐘轉移1小時轉發至凌晨3點,偏移量從+01:00變爲+02:00

您也可以認爲時鐘直接從凌晨1:59跳到凌晨3點,這意味着當天凌晨2點到凌晨2點59分之間的所有當地時間在當天並不存在。這就是所謂的差距,你應該檢查這種情況(一種常見的方法是將凌晨2點調整到下一個有效時間 - 在本例中爲夏令時的3點)。

在同一時區,在2017年,DST會在10月29日第結束:在上午03時,時鐘將轉向背面 1小時至2 AM,和偏移將改變從+02:00+01:00

這意味着所有當地時間凌晨2點到上午2:59之間將存在兩次:一次在DST(+02:00)和一次在非DST(+01:00)。這就是所謂的重疊,當創建一個屬於這種情況的datetime時,您必須決定選擇哪一個創建(應該是DST中的上午2點還是非DST偏移量?您決定!)。


PS:使用CET在不同的年份現在通過了,所以如果你處理舊的約會,你必須考慮過每個歐洲國家。

另一個細節是DST是由政府定義的,並且不能保證規則將永遠是這樣的,並且您必須相應地更新規則 - 使用pytz的另一個優點是,它的更新是從IANA版本和published in PyPI(感謝@Matt Johnson的pointing this info)。

你確定文書工作比手工編寫這些規則更糟嗎?只需檢查how to read IANA tz files,也許你會改變主意。

+2

爲了回答您關於pytz更新的觀點,它們是從IANA版本生成的。 IANA 2017b => pytz 2017.2。斯圖亞特主教維護他們,並將它們發佈到pypi這裏:https://pypi.python.org/pypi/pytz#downloads –

+0

@MattJohnson我已經更新了這個信息的答案,非常感謝! – 2017-10-19 18:22:12

+1

您和Sergey的答案都非常有幫助。我認爲你已經做了最多的事情來說服我,我真的應該把它交給專家。我們的解決方案是要求UTC時間作爲輸入,然後我們不需要轉換(c: –

3

您應仔細閱讀tzinfo manual,因爲它是有解釋的。

首先,dst()通常應該返回一個timedelta(0)timedelta(hours=1),從未2小時或更長時間,並且很少在之間(例如30分鐘)的東西。這只是相對於正常TZ偏移的dst偏移,而不是dst模式中的完整TZ偏移。

其次,utcoffset()必須包含dst更正。方法本身也用於手冊中描述的某些情況(例如,如果dst有效或無效,用於檢測),但它不會自動應用於TZ偏移,除非這樣做。

您的代碼應該是這樣的:

from datetime import datetime, tzinfo, timedelta 

class CET(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(hours=1) + self.dst(dt) 

    def dst(self, dt): 
     dston = datetime(year=dt.year, month=3, day=20) 
     dstoff = datetime(year=dt.year, month=10, day=20) 
     if dston <= dt.replace(tzinfo=None) < dstoff: 
      return timedelta(hours=1) 
     else: 
      return timedelta(0) 

class UTC(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(0) 

    def dst(self, dt): 
     return timedelta(0) 

def from_cet_to_utc(year, month, day, hour, minute): 
    cet = datetime(year, month, day, hour, minute, tzinfo=CET()) 
    utc = cet.astimezone(tz=UTC()) 
    return '{:%Y-%m-%d:T%H:%MZ}'.format(utc) 


print from_cet_to_utc(year=2017, month=7, day=24, hour=10, minute=30) 
# 2017-07-24:T08:30Z 

在這裏,我大膽假設DST實際上是20.03.YYYY - 19.10.YYYY包容性,其中YYYY是日期/ DateTime對象的年份。

通常情況下,關閉日期的這種邏輯要複雜得多:這些轉換僅在週日晚上發生,有時在本月的最後一個星期日發生,並且在不同年份之間有所不同,有時在各州主權之間邊界線也會隨着時間的推移而變化),並且夏季法律和時區劃分每隔幾年就會發生一次(您好,俄羅斯)。

+3

實際上,您還應該檢查當天的時間 - 在中歐,夏令時的變化通常發生在凌晨2點到凌晨3點之間,因此檢查日期是不夠準確的。無論如何,只要日子發生變化,我就會立即投票(我今天已達到我的投票限制)。 – 2017-10-19 18:01:28

+0

目前,春天凌晨2點,秋天凌晨3點。 https://www.timeanddate.com/time/change/france/paris –

+1

@Hugo你是對的。感謝您補充我的答案。然而,即使你所描述的只是冰山一角。我故意只回答了關於python和'datetime.tzinfo'部分的問題,這個問題的作者似乎是個問題。雖然提到實際的tz計算是一個非常困難和大的話題。因爲它太大了,人們可以純粹根據日期和時間(不是開玩笑)的適當工作來寫一本書。 –

相關問題