web-dev-qa-db-ja.com

気付かない日時のタイムゾーンをPythonで認識させる方法

私がするべきこと

私はタイムゾーンを意識しないdatetimeオブジェクトを持っています、それに他のタイムゾーンを意識したdatetimeオブジェクトと比較できるようにするためにタイムゾーンを追加する必要があります。私は自分のアプリケーション全体を、この1つの従来のケースに気付かないタイムゾーンに変換したくはありません。

試したことのあるもの

まず、問題を実証するために:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes

まず、astimezoneを試しました:

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>

実際に変換を行おうとしているので、これが失敗したことはそれほど驚くことではありません。置換は、より良い選択のように思えました( Python:datetime.today()の値を取得する方法 "timezone aware"? )。

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> 

しかし、ご覧のとおり、replaceはtzinfoを設定するように見えますが、オブジェクトを認識させません。私はそれを解析する前にタイムゾーンを持つように入力文字列を処理することにフォールバックする準備をしています(それが問題であれば、私は解析にdateutilを使用しています)。

また、私はこれをpython 2.6とpython 2.7の両方で試しましたが、同じ結果になりました。

コンテキスト

私はいくつかのデータファイルのパーサを書いています。日付文字列にタイムゾーンインジケータがない場合にサポートする必要がある古い形式があります。データソースはすでに修正されていますが、それでも従来のデータ形式をサポートする必要があります。レガシーデータのワンタイム変換は、さまざまなビジネスBSの理由から選択できません。一般的に言って、私はデフォルトのタイムゾーンをハードコーディングするという考えは好きではありませんが、この場合それは最良の選択肢のように思えます。問題となっているすべてのレガシデータがUTCにあることを私は合理的な確信を持って知っているので、この場合それに対するデフォルトのリスクを受け入れる準備をしています。

396
Mark Tozzi

一般に、単純な日時をタイムゾーン対応にするには、 localizeメソッド :を使用します。

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

UTCタイムゾーンでは、処理する夏時間の計算がないため、localizeを使用する必要はありません。

now_aware = unaware.replace(tzinfo=pytz.UTC)

動作します。 (.replaceは新しい日時を返します。unawareは変更されません。)

470
unutbu

これらの例はすべて外部モジュールを使用しますが、 this SO answer にも示されているように、datetimeモジュールだけを使用しても同じ結果を得ることができます。

from datetime import datetime
from datetime import timezone

dt = datetime.now()
dt.replace(tzinfo=timezone.utc)

print(dt.replace(tzinfo=timezone.utc).isoformat())
'2017-01-12T22:11:31+00:00'

依存性が少なく、問題はありません。

注:python3およびpython2でこれを使用したい場合は、タイムゾーンのインポートにも使用できます(UTC用にハードコードされています)。

try:
    from datetime import timezone
    utc = timezone.utc
except ImportError:
    #Hi there python2 user
    class UTC(tzinfo):
        def utcoffset(self, dt):
            return timedelta(0)
        def tzname(self, dt):
            return "UTC"
        def dst(self, dt):
            return timedelta(0)
    utc = UTC()
98
kang

Dt_awareからdt_unwareまで使用しました

dt_unaware = dt_aware.replace(tzinfo=None)

そしてdt_unwareからdt_awareへ

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)

しかし、以前に答えても良い解決策です。

68
Sérgio

私はDjangoでこの文を使用して、認識されていない時間を認識に変換します。

from Django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
36
Googol

これは@Sérgioと@ unutbuの answer を体系化したものです。それはpytz.timezoneオブジェクトか IANAタイムゾーン 文字列のどちらかで "ただ动作"します。

def make_tz_aware(dt, tz='UTC', is_dst=None):
    """Add timezone information to a datetime object, only if it is naive."""
    tz = dt.tzinfo or tz
    try:
        tz = pytz.timezone(tz)
    except AttributeError:
        pass
    return tz.localize(dt, is_dst=is_dst) 

これはdatetime.localize()(または.inform()または.awarify())が行うべきことのように思えます。tz引数に文字列とタイムゾーンオブジェクトの両方を受け入れ、タイムゾーンが指定されていない場合はデフォルトのUTCになります。

8
hobs

私は前の答えに同意します、そしてあなたがUTCで始めても大丈夫ならば大丈夫です。しかし、UTC以外のローカルタイムゾーンを持つ datetimeを持つtzを意識した値で作業するのは、一般的なシナリオでもあると思います。

名前だけで行けば、replace()が適用可能になり、日時に対応した正しいオブジェクトが生成されると推測されます。これはそうではありません。

置換(tzinfo = ...)はその振る舞いにおいてランダムに思われる 。したがってそれは無駄です。これを使わないでください。

localizeは正しい関数です。例:

localdatetime_aware = tz.localize(datetime_nonaware)

またはより完全な例:

import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())

現在の現地時間のタイムゾーンを意識した日時の値を教えてください。

datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
8
paolov

純粋に知っている2つの方法

from datetime import datetime
import pytz

naive = datetime.now()
aware = naive.replace(tzinfo=pytz.UTC)

または

aware = pytz.UTC.localize(naive)

もちろん、UTCの代わりに任意のタイムゾーンを使用できます。

2
Doom

ローカルタイムゾーンを追加するため。 (dateutilを使用)

from dateutil import tz
import datetime

dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)

dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())
2
Ahmet

Unutbuの答えの形式では。もっと直感的な構文で、このようなことを扱うユーティリティモジュールを作りました。ピップで取り付けることができます。

import datetime
import saturn

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)

now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')
0