Python UTC tarih saatini yalnızca python standart kitaplığını kullanarak yerel bir tarih saatine dönüştürmek mi istiyorsunuz?


189

Ben datetime.utcnow () kullanılarak oluşturulan ve veritabanında kalıcı bir python datetime örneği var.

Görüntüleme için, varsayılan yerel saat dilimini (yani datetime datetime.now () kullanılarak oluşturulmuş gibi) kullanarak veritabanından alınan datetime örneğini yerel datetime dönüştürmek istiyorum.

UTC tarih saatini yalnızca python standart kitaplığını kullanarak yerel bir tarih saatine nasıl dönüştürebilirim (ör. Pytz bağımlılığı yok)?

Bir çözüm datetime.astimezone (tz) kullanmak olabilir, ancak varsayılan yerel saat dilimini nasıl elde edersiniz?


Veritabanında zaman hangi formatta kaldı? Standart bir biçimse, herhangi bir dönüşüm yapmanız gerekmeyebilir.
Apalala

1
Bu cevap pytz kullanmanın basit bir yolunu gösterir .
juan

Yanıtlar:


275

Python 3.3 ve üzeri sürümlerde:

from datetime import datetime, timezone

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

Python 2/3'te:

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)

Kullanarak pytz(her ikisi de 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

Misal

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()))

Çıktı

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 
pytz
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

Not: DST'yi ve MSK saat dilimi için son utc ofset değişikliğini dikkate alır.

Pytz olmayan çözümlerin Windows üzerinde çalışıp çalışmadığını bilmiyorum.


1
'normalleştirmek' ne yapar?
avi

4
@avi: genel olarak: pytz: Zaman dilimleri arasında dönüştürme yaparken normalleştirme neden gereklidir? . Not: için geçerli .normalize()saat diliminde gerekli değildir çünkü kaynak saat dilimi .astimezone()UTC'dir.
jfs

1
TypeError: replace() takes no keyword argumentsPytz çözümünü denerken anladım .
Craig

@ Yanıttaki çıkışın gösterdiği gibi, kodu olduğu gibi çalışır. TypeError'a yol açan tam bir minimal kod örneği oluşturun, Python'unuzdan, kullandığınız pytz sürümlerinden bahsedin ve ayrı bir Yığın Taşması sorusu olarak gönderin.
jfs

Python 3.3+ çözümü için hızlı ipucu. Ters yöne gitmek için (UTC'ye local_dt.replace(tzinfo=None).astimezone(tz=timezone.utc)
özgü

50

Standart kitaplığın herhangi bir zaman dilimi olmadığından bunu yalnızca standart kitaplıkla yapamazsınız. İhtiyacınız pytz veya 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())

Ya da kendi zaman dilimlerinizi uygulayarak pytz veya dateutil olmadan yapabilirsiniz. Ama bu aptalca olurdu.


Tam bir çözüme ihtiyacım olursa bunu elimde tutacağım. Dateutil Python 3.1'i destekliyor mu (Python 2.3+ diyor ama şüpheli)?
Nitro Zark


pytz aslında DST değişimlerini daha iyi idare ediyor.
Lennart Regebro

2
Güncelleme: pytz ve dateutil, günümüzde Python 3'ü desteklemektedir.
Lennart Regebro

1
@JFSebastian: dateutil, bir DST değişimi sırasında meydana gelen iki 1:30 arasındaki farkı ayırt edemez. Bu iki kez ayırt edebilmeniz gerekiyorsa, pytz'ı kullanmak için geçici çözüm.
Lennart Regebro

8

Standart kütüphane ile yapamazsınız. Pytz modülünü kullanarak , herhangi bir naif / farkında datetime nesnesini başka bir saat dilimine dönüştürebilirsiniz. Python 3 kullanarak bazı örneklere bakalım.

Sınıf yöntemi ile oluşturulan naif nesneler utcnow()

Bir naif nesneyi başka herhangi bir saat dilimine dönüştürmek için önce onu farkında olan datetime nesnesine dönüştürmeniz gerekir . replaceBir naif datetime nesnesini bilinçli bir datetime nesnesine dönüştürmek için yöntemi kullanabilirsiniz . Daha sonra bilinçli bir datetime nesnesini başka bir saat dilimine dönüştürmek için astimezoneyöntem kullanabilirsiniz .

Değişken pytz.all_timezones, pytz modülündeki tüm kullanılabilir saat dilimlerinin listesini verir.

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)

Sınıf yöntemi ile oluşturulan naif nesneler now()

Çünkü nowbu metot bize geçerli tarih ve saat, bu nedenle datetime nesnesi saat dilimi farkında ilk yapmak zorunda. localize İşlevi dönüştürür naif bir zaman dilimi farkında datetime nesnesine datetime nesnesi. Ardından astimezoneyöntemi başka bir saat dilimine dönüştürmek için kullanabilirsiniz .

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

Sanırım anladım: Dönemden bu yana geçen saniye sayısını hesaplar, sonra time.localtime kullanarak yerel bir timzeone'ya dönüştürür ve sonra zaman yapısını bir tarih saatine dönüştürür ...

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 )

Yaz / kış DST'sini doğru şekilde uygular:

>>> 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
Ugh. Ama en azından Unix üzerinde çalışmalı. Windows için, gün ışığından yararlanma kurallarınız dönüştürüldüğünden bu yana değiştiyse yanlış olabilir. Neden kütüphane kullanmak istemiyorsun?
Lennart Regebro

Ben sadece utc / yerel dönüşüm gerekir, bu yüzden böyle bir kütüphane sürükleyerek overkill gibi görünüyor. Uygulamanın tek kullanıcısı olduğum için bazı sınırlamalarla yaşayabilirim. Ayrıca, küçük komut dosyamda çalışmaktan daha maliyetli olabilecek bağımlılık ile ilgili sorunları (Python 3 ?, Windows? Kalite ...) desteklemekten kaçının.
Nitro Zark

1
İyi. Python3'e ihtiyacınız varsa, şansınız kalmaz. Ancak, aksi takdirde kendi çözümünüzü araştırmak ve yapmak bir kütüphane kullanmaya kıyasla aşırıdır.
Lennart Regebro

1
+1 (downvote'u dengelemek için: belirtilen stdlib-sadece çözümler aramak geçerli bir gereklilik olabilir) @Lennart uyarısı ile. Göz önüne alındığında dateutil başarısız olabilir Unix hem de Windows üzerinde. btw, utctotimestamp(utc_dt) -> (seconds, microseconds)netlik için kodunuzdan ayıklayabilirsiniz , bkz. Python 2.6-3.x uygulaması
jfs

1
Bir pytz (ve dateutil) bir süredir Python 3 üzerinde çalışıyor, bu yüzden Python3 / Windows / Kalite sorunları artık sorun değil.
Lennart Regebro

6

Alexei'nin yorumu üzerine inşa. Bu DST için de çalışmalıdır.

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

Standart Python kitaplığı hiç bir tzinfouygulama ile gelmez . Bunu her zaman datetime modülünün şaşırtıcı bir eksikliği olarak gördüm.

Tzinfo sınıf için dokümantasyon bazı yararlı örneklerle geliyor. Bölümün sonunda büyük kod bloğuna bakın.


1
Temel uygulama gibi tahminim, bazı ülkelerin DST sürelerini belirlemek için sabit kurallara sahip olmamaları nedeniyle zaman dilimi veritabanının düzenli olarak güncellenmesi gerektiğidir. Yanılmıyorsam, örneğin JVM'nin son saat dilimi veritabanını almak için güncellenmesi gerekiyor ...
JVM'nin

@Nitro Zark, en azından UTC için bir tane olmalıydı. osModül işletim sistemi fonksiyonlarına dayalı yerel saat bir donatıldığı olabilirdi.
Mark Ransom

1
3.2'deki yeni özelliklerin listesini kontrol ediyordum ve 3.2'de UTC saat dilimini eklediler ( eklediler docs.python.org/dev/whatsnew/3.2.html#datetime ). Yerel bir saat dilimi gibi görünmüyor ...
Nitro Zark

2

Python 3.9 zoneinfomodülü ekler, böylece şimdi aşağıdaki gibi yapılabilir (sadece 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

Orta Avrupa UTC'den 1 veya 2 saat ileridedir, yani local_aware:

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

gibi str:

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

, Windows hayır vardır işte ekstra paket gereklidir, sistem saat dilimi veritabanı:

pip install tzdata  

Python 3.6 ila 3.8'de kullanıma izin veren bir backport var :

sudo pip install backports.zoneinfo

Sonra:

from backports.zoneinfo import ZoneInfo

zoneinfoburada bile ihtiyacınız yok - jfs 'cevabının ilk kısmını görün (Python3.3 + kısmı) ;-)
MrFuppes

@MrFuppes Bence ZoneInfo('localtime')çok daha net tz=None(sonuçta, bir tz=Nonezaman dilimi kaldırmak veya yerel saat yerine UTC dönmek bekleyebilirsiniz ).
xjcl

@MrFuppes Plus, soru OP'nin bir web sitesine sahip olmasını ve kullanıcılara yerel saatlerinde zaman damgalarını göstermek istemesini içerebilir. Bunun için kullanıcının yerel saat yerine açık saat dilimine dönüştürmeniz gerekir.
xjcl

0

Python 2 ve 3'te çalışan basit (ama belki de kusurlu) bir yol:

import time
import datetime

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

Avantajı, ters bir işlev yazmanın önemsiz olmasıdır


1
Bu, time.timezonegün ışığından yararlanma tasarruflarını dikkate almadığı için yanlış sonuç verir .
guyarad

0

Bulduğum en kolay yolu zaman nerede ofset elde etmektir sen , o zaman saatten bu çıkarma.

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

Bu benim için Python 3.5.2'de çalışıyor.


0

Burada saat dilimini datetime biçiminde değiştirmenin başka bir yolu var (enerjimi bu konuda boşa harcadığımı biliyorum, ancak bu sayfayı görmedim, bu yüzden nasıl bilmiyorum) min olmadan. ve sn. çünkü projem için buna ihtiyacım yok:

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)

Zaman dilimleri arasında geçiş yapmak için zaman çizelgesini kullanın. İhtiyacınız olan tek şey saat dilimleri arasındaki saat cinsinden sapmadır. Bir datetime nesnesinin 6 öğesinin tümü için sınırlarla uğraşmak zorunda değilsiniz. timedelta artık yılları, artık yüzyılları vb. kolaylıkla halleder. 'Datetime import timedelta'dan' gerekir. Sonra, uzaklık saat cinsinden bir değişkeni ise: zaman aşımı = zaman + zaman çizelgesi (saat = uzaklık), burada zaman ve zaman aşımı datetime nesneleridir. örneğin timein + timedelta (saat = -8) GMT'den PST'ye dönüşür.
Karl Rudnick

0

Bunu yapmanın korkunç bir yolu ama bir tanım yaratmaktan kaçınıyor. Temel Python3 kütüphanesine bağlı kalmayı gerektirir.

# 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])

Bu ilginç olsa da ve ben bu yöntemi kullanacağım sadece stdlib kullanmıyor. Dönüşümleri yapmak için Pandalar kullanıyor. Pandas.pydata.org/pandas-docs/version/0.25/reference/api/…
Tim Hughes

-3

Zaman dilimleri arasında geçiş yapmak için zaman çizelgesini kullanın. İhtiyacınız olan tek şey saat dilimleri arasındaki saat cinsinden sapmadır. Bir datetime nesnesinin 6 öğesinin tümü için sınırlarla uğraşmak zorunda kalmazsınız. timedelta artık yılları, artık yüzyılları vb. kolaylıkla halleder. Önce sen

from datetime import datetime, timedelta

O offsetzaman saat dilimindeki delta ise:

timeout = timein + timedelta(hours = offset)

burada timein ve timeout datetime nesneleridir. Örneğin

timein + timedelta(hours = -8)

GMT'den PST'ye dönüştürür.

Peki, nasıl belirlenir offset? Burada, diğer bazı cevapların güzel yaptığı saat dilimi "farkında" olan datetime nesnelerini kullanmadan dönüşüm için sadece birkaç olanakınız olması koşuluyla basit bir işlevdir. Biraz manuel, ancak bazen netlik en iyisidir.

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)

Sayısız yanıtı inceledikten ve aklıma gelen en sıkı kodla oynadıktan sonra (şimdilik), zamanın önemli olduğu ve karışık zaman dilimlerinin hesaba katılması gereken tüm uygulamaların tüm datetime nesnelerini yapmak için gerçek bir çaba göstermesi en iyisi gibi görünüyor. "farkında". O zaman en basit cevap şudur:

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

örneğin GMT'ye dönüştürmek. Tabii ki, yerel veya başka bir şekilde istediğiniz herhangi bir saat dilimine / dilinden dönüştürmek için sadece pytz'ın anladığı uygun saat dilimi dizesini kullanın (pytz.all_timezones'ten). Gün ışığından yararlanma saati de dikkate alınır.


1
Bu iyi bir fikir değil. DST tüm dünyada aynı tarihte değişmez. Bunu yapmak için datetime işlevlerini kullanın.
Gabe

@gabe - Tablonun iyi bir fikir olmadığını anlayın, ancak yalnızca ABD saat dilimlerini kapsayan birine uygun olabilir. Sonunda söylediğim gibi, en iyi yol her zaman datetime işlevlerini kolayca kullanabilmeniz için datetime nesnelerini "farkında" hale getirmektir.
Karl Rudnick
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.