UTC日時文字列をローカル日時に変換する


206

UTCとの間で時間を変換する必要はありませんでした。最近、自分のアプリにタイムゾーンを認識させるようリクエストがあり、私は自分自身をサークルで実行しています。現地時間をUTCに変換することについての情報はたくさんありますが、かなり基本的なものだと思います(たぶん私もそれを間違っています)。しかし、UTC時間をエンドユーザーのタイムゾーンに簡単に変換することについての情報は見つかりません。

簡単に言うと、Androidアプリは私(appengineアプリ)のデータを送信し、そのデータ内にはタイムスタンプがあります。そのタイムスタンプを使用しているutc時間に保存するには:

datetime.utcfromtimestamp(timestamp)

それは機能しているようです。アプリがデータを保存すると、5時間先に保存されます(EST -5です)

データはappengineのBigTableに保存されており、取得すると次のような文字列として出力されます。

"2011-01-21 02:37:21"

ユーザーの正しいタイムゾーンでこの文字列をDateTimeに変換するにはどうすればよいですか?

また、ユーザーのタイムゾーン情報に推奨されるストレージは何ですか?(通常、どのようにtz情報を格納しますか(例: "-5:00"または "EST"など))最初の質問への回答に2番目の回答へのパラメータが含まれていると確信しています。



この回答は、これを簡単な方法で解決する方法を示しています。
2013

回答:


402

独自のtzinfoオブジェクトを提供したくない場合は、python-dateutilライブラリを確認してください。これはtzinfo、ゾーン情報(Olson)データベースの上に実装を提供し、タイムゾーンルールをやや正規の名前で参照できるようにします。

from datetime import datetime
from dateutil import tz

# METHOD 1: Hardcode zones:
from_zone = tz.gettz('UTC')
to_zone = tz.gettz('America/New_York')

# METHOD 2: Auto-detect zones:
from_zone = tz.tzutc()
to_zone = tz.tzlocal()

# utc = datetime.utcnow()
utc = datetime.strptime('2011-01-21 02:37:21', '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in UTC time zone since 
# datetime objects are 'naive' by default
utc = utc.replace(tzinfo=from_zone)

# Convert time zone
central = utc.astimezone(to_zone)

展開された例を編集してstrptime使用法を表示する

Edit 2の修正されたAPIの使用法により、より適切なエントリポイントメソッドを表示

Edit 3タイムゾーンの自動検出方法(Yarin)


1
私の以前のコメントで、私はインポートすることができたdateutil.tzし、使用tz.tzutc()してtz.tzlocal()タイムゾーンをオブジェクトとして私が探していました。私のシステムのタイムゾーンデータベースは良好であるようです(私はチェックインしました/usr/share/zoneinfo)。何が起きたのかわかりません。
ベンクリーガー、2011年

1
@ベンジャミンあなたは正しい軌道に乗っています。tzモジュールは、このライブラリーのために使用するための正しいエントリポイントです。これを反映するように回答を更新しました。dateutil.zoneinfo私は以前に示したモジュールは、内部で使用されtz、それがシステムのzoneinfoのDBを見つけることができない場合はフォールバックとしてモジュール。ライブラリの内部を見ると、パッケージにzoneinfo DB tarballがあり、システムのDBが見つからない場合に使用されることがわかります。私の古い例は、そのDBを直接ヒットしようとしていましたが、そのプライベートDBのロードで問題が発生していると思います(Pythonパス上に何とかありませんか?)
Joe Holloway


1
@JFSebastianこれは#225で
Rohmer

2
@briankipそれはあなたのアプリケーションがどれほど洗練されている必要があるかに依存しますが、一般的なアプリケーションでは、はい、それは問題です。まず最初に、それが何時であるかに応じて、加算または減算する時間数は、一部のタイムゾーンはDSTを尊重し、他のタイムゾーンは承諾しないため、変更される可能性があります。次に、タイムゾーンルールは時間の経過とともに任意に変更されるため、過去の日付/時刻で作業している場合は、現時点ではなく、その時点で有効だったルールを適用する必要があります。そのため、舞台裏でOlson TZデータベースを活用するAPIを使用するのが最善です。
Joe Holloway、

45

外部ライブラリに依存しない回復力のあるメソッドは次のとおりです。

from datetime import datetime
import time

def datetime_from_utc_to_local(utc_datetime):
    now_timestamp = time.time()
    offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp)
    return utc_datetime + offset

これにより、DelboyJayの例でのタイミングの問題が回避されます。そして、Erik van Oostenの修正でタイミングの問題が少なくなりました。

興味深い脚注として、上記で計算されたタイムゾーンオフセットは、おそらく夏時間のルールの変更が原因で、以下の同等に見える式とは異なる場合があります。

offset = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # NO!

更新:このスニペットには、現在の時刻のUTCオフセットを使用するという弱点があります。これは、入力日時のUTCオフセットとは異なる場合があります。別の解決策については、この回答に関するコメントを参照してください。

別の時間を回避するには、渡された時間からエポック時間を取得します。これが私が行うことです。

def utc2local (utc):
    epoch = time.mktime(utc.timetuple())
    offset = datetime.fromtimestamp (epoch) - datetime.utcfromtimestamp (epoch)
    return utc + offset

これはうまく機能し、時刻と日付時刻以外に何も必要としません。
Mike_K 2013年

6
@Mike_K:しかし、それは正しくありません。他の理由により、過去に異なるUTCオフセットがあったDSTまたはタイムゾーンはサポートされません。pytz-like db なしでの完全なサポートは不可能です。PEP-431を参照してください。Linux、OS Xなどのタイムゾーンデータベースの履歴がすでにあるシステムでこのような場合に機能するstdlibのみのソリューションを作成できますが、私の回答を参照しください。
jfs

@JFSebastian:このコードは一般的には機能しないが、OS XとLinuxでも機能するとします。このコードはWindowsでは機能しないのですか?
デビッドフォスター

2
@DavidFoster:LinuxおよびOS Xでもコードが失敗する可能性があります。あなたと私の標準ライブラリのみのソリューションの違いは、あなたのソリューション現在のオフセットを使用するということですutc_datetime
jfs

1
よかった。それは非常に微妙な違いです。UTCオフセットも時間とともに変化します。ため息
デビッドフォスター

23

tzinfoオブジェクトに関する日時のドキュメントを参照してください。自分でサポートしたいタイムゾーンを実装する必要があります。ドキュメントの下部にある例です。

以下に簡単な例を示します。

from datetime import datetime,tzinfo,timedelta

class Zone(tzinfo):
    def __init__(self,offset,isdst,name):
        self.offset = offset
        self.isdst = isdst
        self.name = name
    def utcoffset(self, dt):
        return timedelta(hours=self.offset) + self.dst(dt)
    def dst(self, dt):
            return timedelta(hours=1) if self.isdst else timedelta(0)
    def tzname(self,dt):
         return self.name

GMT = Zone(0,False,'GMT')
EST = Zone(-5,False,'EST')

print datetime.utcnow().strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(GMT).strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(EST).strftime('%m/%d/%Y %H:%M:%S %Z')

t = datetime.strptime('2011-01-21 02:37:21','%Y-%m-%d %H:%M:%S')
t = t.replace(tzinfo=GMT)
print t
print t.astimezone(EST)

出力

01/22/2011 21:52:09 
01/22/2011 21:52:09 GMT
01/22/2011 16:52:09 EST
2011-01-21 02:37:21+00:00
2011-01-20 21:37:21-05:00a

受け入れてくれてありがとう!特定の例の時間を翻訳するために少し更新しました。時間を解析すると、ドキュメントが「単純な」時間と呼ぶものを作成します。使用はreplaceGMTにタイムゾーンを設定し、それがタイムゾーン「意識」、そして使用するようにするastimezone別のタイムゾーンに変換します。
Mark Tolonen、2011年

ジョーの答えを交換してすみません。技術的にあなたの答えは生のpythonでそれを行う方法を正確に説明します(これは知っておくと良いことです)が、彼が提案したライブラリは私をはるかに迅速に解決に導きます。
MattoTodd、2011年

4
自動的に夏時間を処理するようには見えません。
Gringo Suave 2012年

@GringoSuave:それは意図されていませんでした。そのためのルールは複雑で、頻繁に変更されます。
Mark Tolonen

19

あいまいなローカル時間に対応する時間(DST移行中など)でも正しい結果を取得したい場合、および/またはローカルutcオフセットがローカルタイムゾーンの異なる時間で異なる場合は、タイムゾーンを使用しますpytz

#!/usr/bin/env python
from datetime import datetime
import pytz    # $ pip install pytz
import tzlocal # $ pip install tzlocal

local_timezone = tzlocal.get_localzone() # get pytz tzinfo
utc_time = datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S")
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(local_timezone)

10

この回答は、以外のモジュールを使用したくない場合に役立ちますdatetime

datetime.utcfromtimestamp(timestamp)ナイーブdatetimeオブジェクト(認識されていないオブジェクト)を返します。認識しているものはタイムゾーンを認識し、ナイーブは認識していません。タイムゾーン間(たとえば、UTCと現地時間の間)に変換する場合は、注意が必要です。

開始する日付をインスタンス化する人ではないが、それでもdatetimeUTC時間で単純なオブジェクトを作成できる場合は、次のPython 3.xコードを試して変換することをお勧めします。

import datetime

d=datetime.datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S") #Get your naive datetime object
d=d.replace(tzinfo=datetime.timezone.utc) #Convert it to an aware datetime object in UTC time.
d=d.astimezone() #Convert it to your local timezone (still aware)
print(d.strftime("%d %b %Y (%I:%M:%S:%f %p) %Z")) #Print it with a directive of choice

タイムゾーンが現在MDTである場合、夏時間はMSTを出力するため、上記のコードでは機能しないと誤って想定しないように注意してください。月を8月に変更すると、MDTが出力されることに注意してください。

アウェアdatetimeオブジェクト(Python 3.xでも同じ)を取得する別の簡単な方法は、最初にタイムゾーンを指定して作成することです。次に、UTCを使用した例を示します。

import datetime, sys

aware_utc_dt_obj=datetime.datetime.now(datetime.timezone.utc) #create an aware datetime object
dt_obj_local=aware_utc_dt_obj.astimezone() #convert it to local time

#The following section is just code for a directive I made that I liked.
if sys.platform=="win32":
    directive="%#d %b %Y (%#I:%M:%S:%f %p) %Z"
else:
    directive="%-d %b %Y (%-I:%M:%S:%f %p) %Z"

print(dt_obj_local.strftime(directive))

Python 2.xはPython 2.xには存在しないため、Python 2.x を使用する場合は、サブクラスdatetime.tzinfoを作成し、それを使用してアウェアdatetimeオブジェクトを作成する必要がありますdatetime.timezone


8

Djangoを使用している場合は、次のtimezone.localtimeメソッドを使用できます。

from django.utils import timezone
date 
# datetime.datetime(2014, 8, 1, 20, 15, 0, 513000, tzinfo=<UTC>)

timezone.localtime(date)
# datetime.datetime(2014, 8, 1, 16, 15, 0, 513000, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)

2

矢印が使えます

from datetime import datetime
import arrow

now = datetime.utcnow()

print(arrow.get(now).to('local').format())
# '2018-04-04 15:59:24+02:00'

arrow.get()何でも食べられます タイムスタンプ、ISO文字列など


1

私はこれを伝統的にフロントエンドに任せています-バックエンドから時間をタイムスタンプまたはUTCの他の日時形式として送信し、クライアントにタイムゾーンオフセットを計算させて、このデータを適切なタイムゾーンでレンダリングします。

Webアプリの場合、これはJavaScriptでかなり簡単です。組み込みメソッドを使用してブラウザーのタイムゾーンオフセットをかなり簡単に把握し、バックエンドからのデータを適切にレンダリングできます。


3
これは、単に表示用にローカライズしている多くの場合にうまく機能します。ただし、場合によっては、ユーザーのタイムゾーンに基づいてビジネスロジックを実行する必要があり、アプリケーションサーバー層の変換を実行できるようにする必要があります。
Joe Holloway

1

を使用calendar.timegmして、Unixエポック以降の時間を秒time.localtimeに変換し、変換して戻すことができます。

import calendar
import time

time_tuple = time.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S")
t = calendar.timegm(time_tuple)

print time.ctime(t)

与えるFri Jan 21 05:37:21 2011(私はUTC + 03:00タイムゾーンにいるため)。


1
import datetime

def utc_str_to_local_str(utc_str: str, utc_format: str, local_format: str):
    """
    :param utc_str: UTC time string
    :param utc_format: format of UTC time string
    :param local_format: format of local time string
    :return: local time string
    """
    temp1 = datetime.datetime.strptime(utc_str, utc_format)
    temp2 = temp1.replace(tzinfo=datetime.timezone.utc)
    local_time = temp2.astimezone()
    return local_time.strftime(local_format)

utc = '2018-10-17T00:00:00.111Z'
utc_fmt = '%Y-%m-%dT%H:%M:%S.%fZ'
local_fmt = '%Y-%m-%dT%H:%M:%S+08:00'
local_string = utc_str_to_local_str(utc, utc_fmt, local_fmt)
print(local_string)   # 2018-10-17T08:00:00+08:00

たとえば、私のタイムゾーンは「+08:00」です。input utc = 2018-10-17T00:00:00.111Z、そしてoutput = 2018-10-17T08:00:00 + 08:00


1
あなたは、あなたの問題であり、あなたが達成しようとしているものをユーザーの残りの部分を説明するプレーンテキスト、中にあなたの問題のより良い説明を与えるとされなければならない
m33n

1

ここでの答えから、時間モジュールを使用して、utcからコンピューターに設定されている現地時間に変換できます。

utc_time = time.strptime("2018-12-13T10:32:00.000", "%Y-%m-%dT%H:%M:%S.%f")
utc_seconds = calendar.timegm(utc_time)
local_time = time.localtime(utc_seconds)

0

これは、ローカルシステムの設定を使用して時間差を解決する迅速でダーティなバージョンです。注:これは、現在のシステムが実行されていないタイムゾーンに変換する必要がある場合は機能しません。BSTタイムゾーンでUK設定を使用してこれをテストしました

from datetime import datetime
def ConvertP4DateTimeToLocal(timestampValue):
   assert isinstance(timestampValue, int)

   # get the UTC time from the timestamp integer value.
   d = datetime.utcfromtimestamp( timestampValue )

   # calculate time difference from utcnow and the local system time reported by OS
   offset = datetime.now() - datetime.utcnow()

   # Add offset to UTC time and return it
   return d + offset

それがあるべきdatetime.fromtimestamp(ts):POSIXタイムスタンプ(フロート秒) -現地時間で>日時オブジェクト(OS、すなわちローカルタイムゾーンのために過去のUTCオフセットを覚えているならば、それは過去の日付のためのUnix上ではなく、Windows上で、うまく動作します))。それ以外の場合は、pytzを使用できます。
jfs

次のことを実行することをお勧めします offset = datetime.utcnow().replace(minute=0, second=0, microsecond=0) - datetime.now().replace(minute=0, second=0, microsecond=0) 。それなしで奇妙なマイクロ秒の違いが出ました。
エリックファンオステン2013

@ErikvanOosten:datetime.fromtimestamp(ts)答えの代わりに試しましたか?
jfs

0

フランクサンドからの回答を便利な方法に統合する。

import calendar
import datetime

def to_local_datetime(utc_dt):
    """
    convert from utc datetime to a locally aware datetime according to the host timezone

    :param utc_dt: utc datetime
    :return: local timezone datetime
    """
    return datetime.datetime.fromtimestamp(calendar.timegm(utc_dt.timetuple()))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.