UTC datetime dizesini yerel datetime'a dönüştürme


206

Asla UTC'ye dönüşüp UTC'yi dönüştürmek zorunda kalmadım. Son zamanlarda uygulamamın saat dilimi farkında olmasını istemiştim ve kendimi çevrelerde çalıştırıyorum. Yerel saati UTC'ye dönüştürmeyle ilgili çok fazla bilgi buldum (belki de bunu yanlış yapıyorum), ancak UTC saatini son kullanıcıların saat dilimine kolayca dönüştürmeyle ilgili herhangi bir bilgi bulamıyorum.

Özetle ve android uygulaması bana (appengine uygulaması) veri gönderir ve bu veri içinde bir zaman damgasıdır. Kullandığım utc zaman için bu zaman damgasını saklamak için:

datetime.utcfromtimestamp(timestamp)

İşe yarıyor gibi görünüyor. Uygulamam verileri sakladığında, 5 saat ileriye doğru saklanıyor (EST -5'im)

Veriler appengine'ın BigTable'ında saklanıyor ve alındığında şöyle bir dize olarak çıkıyor:

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

Bu dizeyi kullanıcıların doğru saat dilimindeki DateTime'a nasıl dönüştürebilirim?

Ayrıca, kullanıcıların saat dilimi bilgileri için önerilen depolama alanı nedir? (Genellikle tz bilgilerini nasıl saklarsınız: "-5: 00" ya da "EST" vb.) Eminim ki ilk sorumun cevabı ikinci cevabı içeren bir parametre içerebilir.



Bu cevap , bunun nasıl basit bir şekilde çözüleceğini göstermektedir.
juan

Yanıtlar:


402

Kendi tzinfonesnelerinizi vermek istemiyorsanız , python-dateutil kütüphanesine göz atın . Sağladığı tzinfobir üstündeki uygulamaları zoneinfo (Olson) veritabanının Eğer biraz kurallı isimle saat dilimi kurallarına başvurabilir şekilde.

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)

strptimeKullanımı göstermek için Genişletilmiş örneği düzenle

Daha iyi giriş noktası yöntemi göstermek için 2 Sabit API kullanımını düzenleyin

Düzenle 3 Zaman dilimleri için otomatik algılama yöntemleri dahil (Yarin)


1
Benim önceki yorum, ben ithalat başardı dateutil.tzve kullanım tz.tzutc()ve tz.tzlocal()saat dilimi aradığım nesneleri olarak. Görünüşe göre sistemimdeki saat dilimi veritabanı iyi /usr/share/zoneinfo. Ne olduđundan emin deđilim.
Ben Kreeger

1
@Benjamin Doğru yoldasınız. tzModül bu kütüphane için kullanıyor doğru giriş noktasıdır. Cevabımı bunu yansıtacak şekilde güncelledim. Daha dateutil.zoneinfoönce gösterdiğim modül dahili tzolarak modül tarafından sistemin bölge DB'sini bulamıyorsa geri dönüş olarak kullanılır . Kitaplığın içine bakarsanız, paketinde sisteminizin DB'sini bulamıyorsa kullandığı bir zoneinfo DB tarball olduğunu görürsünüz. Eski örneğim bu DB'yi doğrudan vurmaya çalışıyordu ve bu özel DB'yi yükleme konusunda sorunlar yaşadığınızı tahmin ediyorum (bir şekilde python yolunda değil mi?)
Joe Holloway


1
@JFSebastian Bu # 225
Rohmer

2
@briankip Uygulamanızın ne kadar karmaşık olması gerektiğine bağlıdır, ancak genel uygulamalarda evet, bu bir sorundur. Her şeyden önce, yılın hangi zamanına bağlı olarak, bazı zaman dilimleri DST ve diğerlerinin uymadığı için toplama / çıkarma saatleri değişebilir. İkincisi, zaman dilimi kuralları zaman içinde keyfi olarak değiştirilir, bu nedenle geçmiş bir tarih / saatle çalışıyorsanız, şu anda değil, o zaman geçerli olan kuralları uygulamanız gerekir. Bu nedenle, perde arkasında Olson TZ veritabanından yararlanan bir API kullanmak en iyisidir.
Joe Holloway

45

İşte herhangi bir harici kütüphaneye bağlı olmayan esnek bir yöntem:

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

Bu, DelboyJay örneğindeki zamanlama sorunlarını önler. Ve Erik van Oosten değişikliğinde daha az zamanlama sorunları.

İlginç bir dipnot olarak, yukarıda hesaplanan saat dilimi ofseti, muhtemelen gün ışığından yararlanma kuralı değişiklikleri nedeniyle, aşağıdaki gibi görünen ifadeden farklı olabilir:

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

Güncelleme: Bu snippet, giriş tarihinin UTC ofsetinden farklı olabilecek mevcut saatin UTC ofsetini kullanmanın zayıflığına sahiptir. Başka bir çözüm için bu yanıtın yorumlarına bakın.

Farklı zamanlarda dolaşmak için, zaman geçtikten sonra çağın zamanını kapmak. İşte yaptığım şey:

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

Bu iyi çalışır ve zaman ve datetime dışında bir şey gerektirmez.
Mike_K

6
@Mike_K: ama yanlış. Başka nedenlerle geçmişte farklı utc ofseti olan DST veya zaman dilimlerini desteklemez. pytz-Db benzeri olmayan tam destek mümkün değildir, bkz. PEP-431. Linux, OS X gibi tarihsel saat dilimi zaten olan sistemlerde bu tür durumlarda çalışan stdlib-only çözüm yazabilseniz de, cevabımı görün .
jfs

@JFSebastian: Bu kodun genel olarak çalışmadığını, aynı zamanda OS X ve Linux'ta da çalıştığını söylüyorsunuz. Bu kodun Windows üzerinde çalışmadığı anlamına mı geliyor?
David Foster

2
@DavidFoster: Linux ve OS X'te de kodunuz başarısız olabilir. Sizinki ve benimki sadece stdlib çözümleri arasındaki fark, sizinki şu anda utc ofsetinden farklı olabilecek geçerli ofseti kullanmasıdır utc_datetime.
jfs

1
İyi karar. Bu çok ince bir fark. UTC ofseti de zamanla değişebilir. iç çekiyor
David Foster

23

Tzinfo nesneleri üzerindeki datetime belgelerine bakın . Kendinizi desteklemek istediğiniz zaman dilimlerini uygulamak zorundasınız. Bunlar belgelerin altındaki örneklerdir.

İşte basit bir örnek:

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)

Çıktı

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

Kabul için teşekkürler! Belirli örnek zamanınızı çevirmek için biraz güncelledim. Zamanı ayrıştırmak, dokümanların "naif" zaman dediği şeyi oluşturur. Kullanım replaceGMT saat dilimi ayarlayın ve saat dilimi "farkında", daha sonra kullanmak yapmak için astimezonebaşka bir saat diliminde dönüştürmek.
Mark Tolonen

Joe'nun cevabını değiştirdiğim için üzgünüm. Teknik olarak cevabınız ham python ile nasıl yapılacağını tam olarak açıklıyor (ki bu iyi bir şey), ancak önerdiği kütüphane beni daha hızlı bir çözüme götürüyor.
MattoTodd

4
Yaz saatini otomatik olarak işliyor gibi görünmüyor.
Gringo Suave

@GringoSuave: amaçlanmamıştı. Bunun kuralları karmaşıktır ve sık sık değişir.
Mark Tolonen

19

Belirsiz bir yerel saate (örneğin, bir DST geçişi sırasında) karşılık gelen zaman için bile doğru sonucu almak istiyorsanız ve / veya yerel utc ofseti yerel saat diliminizde farklı zamanlarda farklıysa, zaman dilimlerini kullanın 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

Bunun dışında başka bir modül kullanmak istemiyorsanız bu yanıt size yardımcı olacaktır datetime.

datetime.utcfromtimestamp(timestamp)saf bir datetimenesne döndürür (farkında değil). Farkında olanlar saat dilimi farkındadır ve saf değildir. Zaman dilimleri arasında (ör. UTC ve yerel saat arasında) dönüştürmek istiyorsanız, farkında olmak istersiniz.

Başlangıç ​​tarihini başlatan siz değilseniz, ancak datetimeUTC saatinde hala naif bir nesne oluşturabilirsiniz , dönüştürmek için şu Python 3.x kodunu denemek isteyebilirsiniz:

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

Yanlışlıkla, saat diliminiz şu anda MDT ise, yaz saati MST yazdırdığı için yukarıdaki kodla çalışmadığını varsaymamaya dikkat edin. Ayı Ağustos olarak değiştirirseniz, MDT yazdıracağını göreceksiniz.

Bilinçli bir datetimenesne (Python 3.x'te de) elde etmenin bir başka kolay yolu , başlamak için belirtilen bir saat dilimiyle oluşturmaktır. UTC kullanarak bir örnek:

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 kullanıyorsanız, Python 2.x'te bulunmadığından, muhtemelen bunu alt sınıf datetime.tzinfoyapmanız ve farkında bir datetimenesne oluşturmanıza yardımcı olması için kullanmanız gerekir datetime.timezone.


8

Django kullanıyorsanız, timezone.localtimeyöntemi kullanabilirsiniz :

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

Oku kullanabilirsiniz

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()herhangi bir şeyle besleyebilirsiniz . zaman damgası, iso dizesi vb.


1

Bunu geleneksel olarak ön uca erteliyorum - zamanları arka uçtan UTC'de zaman damgası veya başka bir datetime formatı olarak gönderiyorum, sonra müşterinin saat dilimi ofsetini bulmasına ve bu verileri uygun saat diliminde oluşturmasına izin veriyorum.

Bir web uygulaması için, javascript'te bunu yapmak oldukça kolaydır - yerleşik yöntemleri kullanarak tarayıcının saat dilimi farkını kolayca anlayabilir ve ardından arka uçtaki verileri düzgün bir şekilde oluşturabilirsiniz.


3
Bu, görüntüleme için yerelleştirdiğiniz birçok durumda iyi çalışır. Bununla birlikte, zaman zaman, kullanıcının saat dilimine bağlı olarak bazı iş mantığı gerçekleştirmeniz ve uygulama sunucusu katmanında dönüşüm yapabilmeniz gerekir.
Joe Holloway

1

calendar.timegmUnix döneminden bu yana zamanınızı saniyeye time.localtimedönüştürmek ve geri dönüştürmek için kullanabilirsiniz :

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)

Verir Fri Jan 21 05:37:21 2011(: 00 zaman dilimi Ben UTC + 03 olduğum için).


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

örneğin, saat dilimim ' +08: 00 '. giriş utc = 2018-10-17T00: 00: 00.111Z , o zaman çıktı alacağım = 2018-10-17T08: 00: 00 + 08: 00


1
Probleminizin daha iyi bir açıklamasını vermelisiniz, burada kalan kullanıcılara ne elde etmeye çalıştığınızı ve hangi probleminiz olduğunu açıklamalısınız
m33n

1

Buradaki yanıttan , utc'den bilgisayarınızda ayarlanan yerel saate dönüştürmek için zaman modülünü kullanabilirsiniz:

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

Zaman farkını çözmek için yerel sistem ayarlarını kullanan hızlı ve kirli bir sürüm. NOT: Geçerli sisteminizin çalışmadığı bir saat dilimine dönüştürmeniz gerekirse bu çalışmaz. Bunu BST saat dilimi altında İngiltere ayarlarıyla test ettim.

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

şöyle olmalıdır datetime.fromtimestamp(ts): posix zaman damgası (float saniye) -> datetime nesnesi yerel saatte (işletim sistemi yerel saat dilimi için geçmiş utc ofsetlerini hatırlarsa iyi çalışır, yani Unix'te, ancak geçmiş tarihler için Windows'ta değil). Aksi takdirde pytz kullanılabilir.
jfs

Aşağıdakileri yapmayı önerebilir miyim: offset = datetime.utcnow().replace(minute=0, second=0, microsecond=0) - datetime.now().replace(minute=0, second=0, microsecond=0) Onsuz garip mikrosaniye farklılıkları aldım.
Erik van Oosten

@ErikvanOosten: datetime.fromtimestamp(ts)Cevap yerine denediniz mi?
jfs

0

Franksandların cevabını uygun bir yöntemde birleştirmek.

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()))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.