Python標準ライブラリのみを使用してPython UTC日時をローカル日時に変換しますか?


189

datetime.utcnow()を使用して作成され、データベースに永続化されたpython datetimeインスタンスがあります。

表示のために、データベースから取得したdatetimeインスタンスを、デフォルトのローカルタイムゾーンを使用してローカルのdatetimeに変換したいと思います(つまり、datetimeがdatetime.now()を使用して作成されたかのように)。

Python標準ライブラリのみを使用して(たとえば、pytzに依存しない)、UTCの日時をローカルの日時に変換するにはどうすればよいですか?

1つの解決策はdatetime.astimezone(tz)を使用することですが、デフォルトのローカルタイムゾーンをどのように取得しますか?


時間がデータベースに保持されたのはどの形式ですか?標準形式の場合は、変換を行う必要がない可能性があります。
Apalala

1
この回答は、pytzを使用する簡単な方法を示しています。
2013

回答:


275

Python 3.3以降の場合:

from datetime import datetime, timezone

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)

Python 2/3の場合:

import calendar
from datetime import datetime, timedelta

def utc_to_local(utc_dt):
    # get integer timestamp to avoid precision lost
    timestamp = calendar.timegm(utc_dt.timetuple())
    local_dt = datetime.fromtimestamp(timestamp)
    assert utc_dt.resolution >= timedelta(microseconds=1)
    return local_dt.replace(microsecond=utc_dt.microsecond)

使用pytz(両方のPython 2/3):

import pytz

local_tz = pytz.timezone('Europe/Moscow') # use your local timezone name here
# NOTE: pytz.reference.LocalTimezone() would produce wrong result here

## You could use `tzlocal` module to get local timezone on Unix and Win32
# from tzlocal import get_localzone # $ pip install tzlocal

# # get local timezone    
# local_tz = get_localzone()

def utc_to_local(utc_dt):
    local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
    return local_tz.normalize(local_dt) # .normalize might be unnecessary

def aslocaltimestr(utc_dt):
    return utc_to_local(utc_dt).strftime('%Y-%m-%d %H:%M:%S.%f %Z%z')

print(aslocaltimestr(datetime(2010,  6, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime(2010, 12, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime.utcnow()))

出力

Python 3.3
2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.093745 MSK+0400
Python 2
2010-06-06 21:29:07.730000 
2010-12-06 20:29:07.730000 
2012-11-08 14:19:50.093911 
ピッツ
2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.146917 MSK+0400

注:DSTと、MSKタイムゾーンのutcオフセットの最近の変更が考慮されます。

pytz以外のソリューションがWindowsで機能するかどうかはわかりません。


1
「ノーマライズ」は何をしますか?
avi 2014年

4
@avi:一般的に:pytz:タイムゾーン間で変換するときに正規化が必要なのはなぜですか?。注:.normalize()のソースタイムゾーン.astimezone()はUTC であるため、現在の実装では必要ありません。
jfs 2014年

1
TypeError: replace() takes no keyword argumentsはpytzソリューションを試してみました。
クレイグ、

回答の出力が示すように、@ Craigコードはそのまま動作します。TypeErrorにつながる完全な最小限のコード例を作成し、使用しているPythonとpytzのバージョンに言及し、別のStack Overflow質問として投稿します。
jfs

Python 3.3+ソリューションのクイックヒント。反対方向(UTCに対してネイティブ)に移動するには、次のように機能します local_dt.replace(tzinfo=None).astimezone(tz=timezone.utc)
。– jasonrhaas

50

標準ライブラリにはタイムゾーンがないため、標準ライブラリだけでそれを行うことはできません。pytzまたはdateutilが必要です

>>> from datetime import datetime
>>> now = datetime.utcnow()
>>> from dateutil import tz
>>> HERE = tz.tzlocal()
>>> UTC = tz.gettz('UTC')

The Conversion:
>>> gmt = now.replace(tzinfo=UTC)
>>> gmt.astimezone(HERE)
datetime.datetime(2010, 12, 30, 15, 51, 22, 114668, tzinfo=tzlocal())

または、独自のタイムゾーンを実装することで、pytzやdateutilなしで実行できます。しかし、それはばかげたことでしょう。


おかげで、完全なソリューションが必要な場合は、手元に置いておきます。dateutilはPython 3.1をサポートしますか(Python 2.3+と言いますが、疑わしいです)?
Nitro Zark、2010


pytzは実際、DSTの切り替えをより適切に処理します。
Lennart Regebro 2012年

2
更新:pytzとdateutilはどちらも現在、Python 3をサポートしています。
Lennart Regebro、2012年

1
@JFSebastian:dateutilは、DST切り替え中に発生する2つの1:30時間を区別できません。これら2つの時間を区別できるようにする必要がある場合は、それを処理できるpytzを使用することで回避できます。
Lennart Regebro、2012年

8

標準ライブラリではできません。pytzモジュールを使用 すると、ナイーブ/対応の日付時刻オブジェクトを他のタイムゾーンに変換できます。Python 3を使用した例をいくつか見てみましょう。

クラスメソッドを通じて作成されたナイーブオブジェクト utcnow()

変換するには素朴な他の時間帯にオブジェクトを、最初にあなたがに変換する必要が認識して DateTimeオブジェクト。このreplaceメソッドを使用して、単純な datetimeオブジェクトを対応する datetimeオブジェクトに変換できます。次に、対応する datetimeオブジェクトを他のタイムゾーンに変換するには、astimezoneメソッドを使用できます。

変数pytz.all_timezonesは、pytzモジュールで使用可能なすべてのタイムゾーンのリストを提供します。

import datetime,pytz

dtobj1=datetime.datetime.utcnow()   #utcnow class method
print(dtobj1)

dtobj3=dtobj1.replace(tzinfo=pytz.UTC) #replace method

dtobj_hongkong=dtobj3.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)

クラスメソッドを通じて作成されたナイーブオブジェクト now()

nowメソッドは現在の日付と時刻を返すため、最初にdatetimeオブジェクトにタイムゾーンを認識させる必要があります。このlocalize 関数は、単純な日時オブジェクトをタイムゾーン対応の日時オブジェクトに変換します。次に、このastimezoneメソッドを使用して別のタイムゾーンに変換できます。

dtobj2=datetime.datetime.now()

mytimezone=pytz.timezone("Europe/Vienna") #my current timezone
dtobj4=mytimezone.localize(dtobj2)        #localize function

dtobj_hongkong=dtobj4.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)

6

私はそれを理解したと思います:エポックからの秒数を計算し、time.localtimeを使用してローカルtimzeoneに変換し、次に時間構造体を日付時刻に変換します...

EPOCH_DATETIME = datetime.datetime(1970,1,1)
SECONDS_PER_DAY = 24*60*60

def utc_to_local_datetime( utc_datetime ):
    delta = utc_datetime - EPOCH_DATETIME
    utc_epoch = SECONDS_PER_DAY * delta.days + delta.seconds
    time_struct = time.localtime( utc_epoch )
    dt_args = time_struct[:6] + (delta.microseconds,)
    return datetime.datetime( *dt_args )

夏/冬の夏時間を正しく適用します。

>>> utc_to_local_datetime( datetime.datetime(2010, 6, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 6, 6, 19, 29, 7, 730000)
>>> utc_to_local_datetime( datetime.datetime(2010, 12, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 12, 6, 18, 29, 7, 730000)

1
ああ。しかし、少なくともUnixでは動作するはずです。Windowsの場合、日付が変換されてから夏時間規則が変更されていると正しくない可能性があります。ライブラリを使用したくないのですか?
Lennart Regebro 2010

私はutc / local変換だけを必要とするので、そのようなライブラリをドラッグするのはやり過ぎのようです。私はこのアプリケーションの唯一のユーザーなので、いくつかの制限があります。また、小さなスクリプトで作業するよりもコストがかかる依存関係関連の問題(Python 3?、Windows?の品質をサポートする...)の管理も回避できます。
Nitro Zark、2010

1
上手。Python3が必要な場合は、運が悪いです。しかし、それ以外の場合、独自のソリューションを調査して作成することは、ライブラリを使用することに比べて過剰です。
Lennart Regebro 2010

1
+1(反対票を相殺するため:stdlibのみのソリューションを探すことは有効な要件である可能性があります)、警告@Lennartが言及されています。そのため、UNIXとWindowsの両方でdateutilが失敗する可能性があります。ところで、utctotimestamp(utc_dt) -> (seconds, microseconds)わかりやすくするためにコードから抽出することができます。Python2.6-3.xの実装を
jfs '28

1
pytz(およびdateutil)は、しばらく前からPython 3で動作するため、Python3 / Windows / Qualityの問題はもう問題ではありません。
Lennart Regebro、2012年

6

アレクセイのコメントに基づく。これはDSTでも機能するはずです。

import time
import datetime

def utc_to_local(dt):
    if time.localtime().tm_isdst:
        return dt - datetime.timedelta(seconds = time.altzone)
    else:
        return dt - datetime.timedelta(seconds = time.timezone)

4

標準のPythonライブラリには、tzinfo実装は一切含まれていません。これは、datetimeモジュールの驚くべき欠点であると常に考えていました。

tzinfoクラスドキュメントには、いくつかの便利な例が含まれています。セクションの最後にある大きなコードブロックを探します。


1
一部の国ではDSTの期間を決定するための固定されたルールがないため、タイムゾーンデータベースを定期的に更新する必要があるため、コア実装のようなものについて私の推測。私が間違っていない場合、たとえば最後のタイムゾーンデータベースを取得するためにJVMを更新する必要があります...
Nitro Zark

@Nitro Zark、少なくとも彼らはUTCのためにそれを持っていたはずです。osモジュールは、オペレーティングシステムの機能に基づいて、現地時間のための1つを提供している可能性があります。
Mark Ransom 2010

1
私は3.2の新機能のリストを確認していましたが、3.2でUTCタイムゾーンが追加されました(docs.python.org/dev/whatsnew/3.2.html#datetime)。しかし、ローカルタイムゾーンはないようです...
Nitro Zark

2

Python 3.9ではzoneinfoモジュールが追加されているため、次のように実行できます(stdlibのみ)。

from zoneinfo import ZoneInfo
from datetime import datetime

utc_unaware = datetime(2020, 10, 31, 12)  # loaded from database
utc_aware = utc_unaware.replace(tzinfo=ZoneInfo('UTC'))  # make aware
local_aware = utc_aware.astimezone(ZoneInfo('localtime'))  # convert

中央ヨーロッパはUTCより1時間または2時間進んでいるため、次のようになりlocal_awareます。

datetime.datetime(2020, 10, 31, 13, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='localtime'))

としてstr

2020-10-31 13:00:00+01:00

Windowsに システムタイムゾーンデータベースがないため、追加のパッケージが必要です。

pip install tzdata  

Python 3.6から3.8で使用できるようにするバックポートがあります。

sudo pip install backports.zoneinfo

次に:

from backports.zoneinfo import ZoneInfo

zoneinfoここでも必要ありません-jfsの回答の最初の部分(Python3.3以降の部分)を参照してください;-)
MrFuppes

@MrFuppes ZoneInfo('localtime')tz=None(結局のところ、tz=Noneタイムゾーンを削除するか、ローカルタイムではなくUTCを返すことを期待するかもしれません)よりもずっと明確だと思います。
xjcl

@MrFuppes Plus質問には、OPにWebサイトがあり、ユーザーのタイムスタンプを現地時間で表示したい場合があります。そのためには、ローカルタイムではなく、ユーザーの明示的なタイムゾーンに変換する必要があります。
xjcl

0

Python 2および3で機能するシンプルな(ただし、欠陥のある)方法。

import time
import datetime

def utc_to_local(dt):
    return dt - datetime.timedelta(seconds = time.timezone)

その利点は、逆関数を書くことは簡単です


1
time.timezone夏時間を考慮していないため、これは誤った結果をもたらします。
Guyarad

0

私が見つけた最も簡単な方法は、時間がどこのオフセットを取得することですあなたがいる、そして時間からそれを引きます。

def format_time(ts,offset):
    if not ts.hour >= offset:
        ts = ts.replace(day=ts.day-1)
        ts = ts.replace(hour=ts.hour-offset)
    else:
        ts = ts.replace(hour=ts.hour-offset)
    return ts

これは、Python 3.5.2では私にとってはうまくいきます。


0

これは、日付形式でタイムゾーンを変更する別の方法です(これでエネルギーを浪費したことはわかっていますが、このページが表示されなかったため、方法がわかりません)。と秒。私のプロジェクトでは必要ないので、

def change_time_zone(year, month, day, hour):
      hour = hour + 7 #<-- difference
      if hour >= 24:
        difference = hour - 24
        hour = difference
        day += 1
        long_months = [1, 3, 5, 7, 8, 10, 12]
        short_months = [4, 6, 9, 11]
        if month in short_months:
          if day >= 30:
            day = 1
            month += 1
            if month > 12:
              year += 1
        elif month in long_months:
          if day >= 31:
            day = 1
            month += 1
            if month > 12:
              year += 1
        elif month == 2:
          if not year%4==0:
            if day >= 29:
              day = 1
              month += 1
              if month > 12:
                year += 1
          else:
            if day >= 28:
              day = 1
              month += 1
              if month > 12:
                year += 1
      return datetime(int(year), int(month), int(day), int(hour), 00)

タイムゾーンを切り替えるには、timedeltaを使用します。必要なのは、タイムゾーン間のオフセット(時間単位)だけです。日時オブジェクトの6つの要素すべての境界をいじる必要はありません。timedeltaは、うるう年、うるう世紀なども簡単に処理します。'datetimeインポートtimedeltaから'する必要があります。次に、オフセットが時間単位の変数の場合、timeout = timein + timedelta(hours = offset)となります。timeinとtimeoutはdatetimeオブジェクトです。たとえば、timein + timedelta(hours = -8)はGMTからPSTに変換します。
Karl Rudnick

0

これはひどい方法ですが、定義の作成を回避します。これは、基本的なPython3ライブラリを使用するという要件を満たしています。

# Adjust from UST to Eastern Standard Time (dynamic)
# df.my_localtime should already be in datetime format, so just in case
df['my_localtime'] = pd.to_datetime.df['my_localtime']

df['my_localtime'] = df['my_localtime'].dt.tz_localize('UTC').dt.tz_convert('America/New_York').astype(str)
df['my_localtime'] = pd.to_datetime(df.my_localtime.str[:-6])

これは興味深く、私はこの方法を使用しますが、stdlibだけを使用するのではありません。Pandasを使用して変換を実行しています 。pandas.pydata.org/ pandas
Tim Hughes

-3

タイムゾーンを切り替えるには、timedeltaを使用します。必要なのは、タイムゾーン間のオフセット(時間単位)だけです。日時オブジェクトの6つの要素すべての境界をいじる必要はありません。timedeltaは、うるう年、うるう世紀なども簡単に処理します。最初に

from datetime import datetime, timedelta

次にoffset、タイムゾーンデルタが時間単位の場合:

timeout = timein + timedelta(hours = offset)

ここで、timeinおよびtimeoutはdatetimeオブジェクトです。例えば

timein + timedelta(hours = -8)

GMTからPSTに変換します。

だから、どのように決定するのoffsetですか?これは、タイムゾーンが「認識」されている日付時刻オブジェクトを使用せずに変換の可能性がいくつかある場合に、他のいくつかの回答がうまく機能する単純な関数です。少し手作業ですが、場合によってはわかりやすさが最適です。

def change_timezone(timein, timezone, timezone_out):
    '''
    changes timezone between predefined timezone offsets to GMT
    timein - datetime object
    timezone - 'PST', 'PDT', 'GMT' (can add more as needed)
    timezone_out - 'PST', 'PDT', 'GMT' (can add more as needed)
    ''' 
    # simple table lookup        
    tz_offset =  {'PST': {'GMT': 8, 'PDT': 1, 'PST': 0}, \
                  'GMT': {'PST': -8, 'PDT': -7, 'GMT': 0}, \
                  'PDT': {'GMT': 7, 'PST': -1, 'PDT': 0}}
    try:
        offset = tz_offset[timezone][timezone_out]
    except:
        msg = 'Input timezone=' + timezone + ' OR output time zone=' + \
            timezone_out + ' not recognized'
        raise DateTimeError(msg)

    return timein + timedelta(hours = offset)

多くの答えを見て、私が考えることができる最もタイトなコードで遊んだ後(現時点では)、時間が重要であり、混合タイムゾーンが考慮されなければならないすべてのアプリケーションは、すべての日時オブジェクトを作成するために真の努力をする必要があると思われます"気がついて"。次に、最も簡単な答えは次のようになります。

timeout = timein.astimezone(pytz.timezone("GMT"))

たとえば、GMTに変換します。もちろん、ローカルかどうかに関係なく、希望する他のタイムゾーンとの間で変換を行うには、pytzが理解する適切なタイムゾーン文字列を使用します(pytz.all_timezonesから)。その後、夏時間も考慮されます。


1
これは良い考えではありません。DSTは世界中で同じ日にシフトしません。これを行うには、日時関数を使用します。
Gabe

@gabe-テーブルは米国のタイムゾーンのみをカバーする人には適しているかもしれませんが、テーブルは良いアイデアではないことを理解してください。最後に述べたように、最善の方法は、常に日時オブジェクトを「認識」させて、日時関数を簡単に使用できるようにすることです。
Karl Rudnick
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.