web-dev-qa-db-ja.com

特定のタイムゾーンの「真夜中」の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)を取得し、それを他のタイムゾーンに変換し、時刻を午前0時に設定してから、UTCに戻します。

ユーザーのタイムゾーンではなくサーバーのタイムゾーンを使用するため、私はnow()またはlocaltime()を使用しているだけではありません。

私は何か、何かアイデアを逃していると感じざるを得ませんか?

39
Tom

次のようにすれば、いくつかのメソッド呼び出しを削ることができると思います。

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

しかし、コードには美的問題よりも大きな問題があります。夏時間への切り替えまたは夏時間からの切り替えの日に誤った結果をもたらします。

これは、日時コンストラクターも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()のドキュメントには次のように記載されています。

このメソッドは、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年より前の日付は保証されません。

61
user3850

@ hopの回答 夏時間(DST)からの移行日(たとえば、2012年4月1日)は誤りです。修正するには 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)
24
jfs

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)

各タイムゾーンには数値があります(例:US/Central = -6)。これは、UTCからの時間単位のオフセットとして定義されます。 0000は午前0時なので、このオフセットを使用して、UTCの午前0時の任意のタイムゾーンの時刻を検索できます。これにアクセスするには、あなたが使用できると思います

 time.timezone

The Python Docs によると、time.timezoneは実際にはこの数値の負の値を与えます:

time.timezone

UTCの西の秒単位のローカル(非DST)タイムゾーンのオフセット(ほとんどの西ヨーロッパでは負、米国では正、英国ではゼロ)。

そのため、正の場合(つまり、シカゴの真夜中(タイムゾーン値が+6の場合)の場合は、時間単位の時間にその数値を使用するだけで、6000 = 6am UTC)になります。

数値が負の場合は、24から減算します。たとえば、ベルリンでは-1となるため、24-1 => 2300 = 11pmとなります。

0
Colin

これはpytzよりdateutil.tzの方が簡単です。

>>>import datetime
>>>import dateutil.tz
>>>midnight=(datetime.datetime
             .now(dateutil.tz.gettz('Australia/Melbourne'))
             .replace(hour=0, minute=0, second=0, microsecond=0)
             .astimezone(dateutil.tz.tzutc()))
>>>print(midnight)
2019-04-26 14:00:00+00:00

tzinfoドキュメント は、Python 3.6以降、dateutil.tzを推奨しています。 dateutil.tzのtzinfoオブジェクトは、pytzのローカライズ機能を必要とせずに、DSTなどの異常に問題がありません。 user3850の例を使用します。

>>> now = (datetime.datetime(2012, 4, 1, 5,  
...         tzinfo = dateutil.tz.gettz('Australia/Melbourne'))) 
>>> print(now.replace(hour = 0).astimezone(dateutil.tz.tzutc()))
2012-03-31 13:00:00+00:00
0
Noyer282