Jak przekonwertować ciąg czasu lokalnego na UTC?

307

Jak przekonwertować ciąg datetime w czasie lokalnym na ciąg w czasie UTC ?

Jestem pewien, że już to zrobiłem, ale nie mogę tego znaleźć, więc mam nadzieję, że pomoże mi (i innym) zrobić to w przyszłości.

Wyjaśnienie : Na przykład, jeśli mam 2008-09-17 14:02:00w mojej lokalnej strefy czasowej ( +10), chciałbym aby wygenerować ciąg z równoważnego UTCczasu: 2008-09-17 04:02:00.

Ponadto z http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ zauważ, że ogólnie nie jest to możliwe, ponieważ w przypadku DST i innych problemów nie ma unikalnej konwersji z czasu lokalnego na Czas UTC.

Tomek
źródło
1
Należy pamiętać, że metoda mktime () zajmuje „czas lokalny” jako dane wejściowe, które mogą nie być zgodne z oczekiwaniami, użyłem jej i wszystko popsuło. Rzuć okiem na ten link
Haifeng Zhang

Odpowiedzi:

287

Najpierw przeanalizuj ciąg znaków w naiwny obiekt typu data-godzina. Jest to przykład datetime.datetimebez dołączonej informacji o strefie czasowej. datetime.strptimeInformacje na temat analizowania ciągu daty można znaleźć w dokumentacji .

Użyj pytzmodułu, który zawiera pełną listę stref czasowych + UTC. Dowiedz się, czym jest lokalna strefa czasowa, zbuduj z niej obiekt strefy czasowej oraz manipuluj nim i dołącz go do naiwnej daty i godziny.

Na koniec użyj datetime.astimezone()metody do konwersji daty i godziny na UTC.

Kod źródłowy, przy użyciu lokalnej strefy czasowej „America / Los_Angeles”, dla ciągu „2001-2-3 10:11:12”:

import pytz, datetime
local = pytz.timezone ("America/Los_Angeles")
naive = datetime.datetime.strptime ("2001-2-3 10:11:12", "%Y-%m-%d %H:%M:%S")
local_dt = local.localize(naive, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)

Stamtąd możesz użyć tej strftime()metody, aby sformatować datę / godzinę UTC zgodnie z potrzebami:

utc_dt.strftime ("%Y-%m-%d %H:%M:%S")
John Millikin
źródło
7
Edytuj swoją odpowiedź za pomocą następującego kodu: [code] local.localize (naive) [/ code] Zobacz dokument: link Niestety użycie argumentu tzinfo standardowych konstruktorów datetime „nie działa” z pytzem w wielu strefach czasowych. >>> datetime (2002, 10, 27, 12, 0, 0, tzinfo = amsterdam) .strftime (fmt) '2002-10-27 12:00:00 AMT + 0020'
Sam Stoelinga
2
Najwyraźniej krok „dowiedzieć się, jaka jest lokalna strefa czasowa” okazuje się trudniejszy niż się wydaje (praktycznie niemożliwe).
Jason R. Coombs
2
Użyj local.localize zgodnie z sugestią @SamStoelinga, w przeciwnym razie nie weźmiesz pod uwagę czasu letniego.
Emil Stenström,
Ta odpowiedź była dla mnie bardzo pomocna, ale chciałbym wskazać napotkany problem. Jeśli podasz godzinę, ale nie datę, możesz uzyskać nieoczekiwane wyniki. Upewnij się, że podałeś pełny znacznik czasu.
Steven Mercatante
144

Za pomocą funkcji utcnow () modułu datetime można uzyskać bieżący czas UTC.

>>> import datetime
>>> utc_datetime = datetime.datetime.utcnow()
>>> utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2010-02-01 06:59:19'

Jak wspomniany wyżej link przez Toma: http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ mówi:

UTC to strefa czasowa bez czasu letniego i wciąż strefa czasowa bez zmian konfiguracji w przeszłości.

Zawsze mierz i przechowuj czas w UTC .

Jeśli chcesz nagrać, gdzie został pobrany czas, przechowuj go osobno. Nie przechowuj informacji o czasie lokalnym + strefie czasowej!

UWAGA - Jeśli któreś z twoich danych znajdują się w regionie, który korzysta z DST, skorzystaj pytzi spójrz na odpowiedź Johna Millikina.

Jeśli chcesz uzyskać czas UTC z danego ciągu i masz szczęście, że jesteś w regionie świata, który albo nie używa DST, albo masz dane, które są przesunięte tylko w stosunku do UTC bez zastosowania DST:

-> wykorzystując czas lokalny jako podstawę wartości przesunięcia:

>>> # Obtain the UTC Offset for the current system:
>>> UTC_OFFSET_TIMEDELTA = datetime.datetime.utcnow() - datetime.datetime.now()
>>> local_datetime = datetime.datetime.strptime("2008-09-17 14:04:00", "%Y-%m-%d %H:%M:%S")
>>> result_utc_datetime = local_datetime + UTC_OFFSET_TIMEDELTA
>>> result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2008-09-17 04:04:00'

-> Lub, ze znanego przesunięcia, za pomocą datetime.timedelta ():

>>> UTC_OFFSET = 10
>>> result_utc_datetime = local_datetime - datetime.timedelta(hours=UTC_OFFSET)
>>> result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2008-09-17 04:04:00'

AKTUALIZACJA:

Ponieważ Python 3.2 datetime.timezonejest dostępny. Możesz wygenerować obiekt datetime rozpoznający strefę czasową za pomocą poniższego polecenia:

import datetime

timezone_aware_dt = datetime.datetime.now(datetime.timezone.utc)

Jeśli jesteś gotowy na konwersje w strefie czasowej, przeczytaj to:

https://medium.com/@eleroy/10-things-you-need-to-know-about-date-and-time-in-python-with-datetime-pytz-dateutil-timedelta-309bfbafb3f7

monkut
źródło
14
To tylko konwertuje aktualny czas, muszę wziąć dowolny czas (jako ciąg) i przekonwertować na UTC.
Tom
Nie sądzę, żeby to miało znaczenie, jeśli używasz standardowych wartości przesunięcia. local_time = utc_time + utc_offset AND utc_time = local_time - utc_offset.
monkut,
5
Działa tylko z bieżącym czasem, ponieważ znaczniki czasu przeszłego i przyszłego mogą mieć różne przesunięcie UTC z powodu czasu letniego.
Alex B
dobra uwaga ... jeśli masz do czynienia z DST, prawdopodobnie powinieneś używać pytza, o którym wspomniał John Millikin.
monkut
1
Sporadycznie występuje delta między wywołaniami funkcji utcnow () i now (). Jest to niebezpieczna linia kodu, która może później prowadzić do dziwnych błędów tzinfo.utcoffset() must return a whole number of minutes.
Pavel Vlasov
62

Dzięki @rofly, pełna konwersja z łańcucha na łańcuch jest następująca:

time.strftime("%Y-%m-%d %H:%M:%S", 
              time.gmtime(time.mktime(time.strptime("2008-09-17 14:04:00", 
                                                    "%Y-%m-%d %H:%M:%S"))))

Moje podsumowanie funkcji time/ calendar:

time.strptime
string -> krotka (nie zastosowano strefy czasowej, więc dopasowuje ciąg)

time.mktime
krotka czasu lokalnego -> sekund od epoki (zawsze czas lokalny)

time.gmtime
sekund od epoki -> krotka w UTC

i

calendar.timegm
krotka w UTC -> sekund od epoki

time.localtime
sekund od epoki -> krotka w lokalnej strefie czasowej

Tomek
źródło
4
(always local time)wydaje się być niepoprawny: wejściem do mktime () jest czas lokalny, wyjściem jest sekunda od epoki ( 1970-01-01 00:00:00 +0000 (UTC)), która nie zależy od strefy czasowej.
jfs,
3
Istnieje również 50% szansa, że ​​zawiedzie podczas przejścia DST. Zobacz problemy z czasem lokalnym
jfs
38

Oto podsumowanie typowych konwersji czasu w języku Python.

Niektóre metody upuszczają ułamki sekund i są oznaczone (s) . ts = (d - epoch) / unitZamiast tego można użyć jawnej formuły, takiej jak (dzięki jfs).

  • struct_time (UTC) → POSIX (s) :
    calendar.timegm(struct_time)
  • Naiwny datetime (lokalny) → POSIX (s) :
    calendar.timegm(stz.localize(dt, is_dst=None).utctimetuple())
    (wyjątek podczas przejść DST, patrz komentarz od jfs)
  • Naiwny datetime (UTC) → POSIX (s) :
    calendar.timegm(dt.utctimetuple())
  • Świadomy datetime → POSIX (S) :
    calendar.timegm(dt.utctimetuple())
  • POSIX → struct_time (UTC, s ):
    time.gmtime(t)
    (patrz komentarz od jfs)
  • Naiwny datetime (lokalny) → struct_time (UTC, s ):
    stz.localize(dt, is_dst=None).utctimetuple()
    (wyjątek podczas przejść DST, patrz komentarz JFS)
  • Naiwny datetime (UTC) → struct_time (UTC, s ):
    dt.utctimetuple()
  • Świadomy datetime → struct_time (UTC, s ):
    dt.utctimetuple()
  • POSIX → Naïve datetime (lokalny):
    datetime.fromtimestamp(t, None)
    (może się nie powieść w niektórych warunkach, patrz komentarz JFS poniżej)
  • struct_time (UTC) → Naïve datetime (local, s ):
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
    (nie może reprezentować sekund przestępnych, patrz komentarz od jfs)
  • Naiwna data / godzina (UTC) → Naiwna data / godzina (lokalna):
    dt.replace(tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
  • Świadomy datetime → Naïve datetime (lokalny):
    dt.astimezone(tz).replace(tzinfo=None)
  • POSIX → Naïve datetime (UTC):
    datetime.utcfromtimestamp(t)
  • struct_time (UTC) → Naïve datetime (UTC, s ):
    datetime.datetime(*struct_time[:6])
    (nie może reprezentować sekund przestępnych, patrz komentarz JFS)
  • Naiwny datetime (lokalny) → Naiwny datetime (UTC):
    stz.localize(dt, is_dst=None).astimezone(UTC).replace(tzinfo=None)
    (wyjątek podczas przejść DST, patrz komentarz JFS)
  • Świadomy datetime → Naïve datetime (UTC):
    dt.astimezone(UTC).replace(tzinfo=None)
  • POSIX → Świadomy datetime:
    datetime.fromtimestamp(t, tz)
    (może się nie powieść w strefach czasowych innych niż pytz)
  • struct_time (UTC) → Aware datetime (s) :
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz)
    (nie może reprezentować sekund przestępnych, patrz komentarz od jfs)
  • Naiwny datetime (lokalny) → Świadomy datetime:
    stz.localize(dt, is_dst=None)
    (wyjątek podczas przejść DST, patrz komentarz od jfs)
  • Naïve datetime (UTC) → Aware datetime:
    dt.replace(tzinfo=UTC)

Źródło: taaviburns.ca

akaihola
źródło
1
(1) fromtimestamp(t, None)może się nie powieść, jeśli lokalna strefa czasowa miała inne przesunięcie utc w, t a biblioteka C nie ma dostępu do bazy danych tz na danej platformie. Możesz użyć, tzlocal.get_localzone()aby dostarczyć tzdata w przenośny sposób. (2) fromtimestamp(t, tz)może się nie powieść w strefach czasowych niebędących pytzami. (3) datetime(*struct_time[:6])brakuje *. (4) timegm(), utctimetuple(), struct_timeopartych roztwór kropli ułamki sekundy. Można użyć wyraźnego wzoru, takie jak: ts = (d - epoch) / unitzamiast.
jfs
1
(5) stz.localize(dt, is_dst=None)podnosi wyjątek dla niejednoznacznych lub nieistniejących czasów lokalnych, np. Podczas przejść DST. Aby uniknąć wyjątku, użyj, stz.normalize(stz.localize(dt))który może zwrócić nieprecyzyjne wyniki. (6) datetime () nie może reprezentować sekundy przestępnej. Najpierw zamień struct_timena „sekundy od epoki” jako obejście. (7) w time.gmtime(t)odróżnieniu od tych, które calendar.timegm()mogą oczekiwać danych innych niż POSIX, jeśli używana jest „właściwa” strefa czasowa. Zamiast tego użyj jawnej formuły, jeśli dane wejściowe to POSIX:gmtime = lambda t: datetime(1970,1,1, tzinfo=utc) + timedelta(seconds=t)
jfs
chciałbym, żeby to było w tabeli, łatwiejsze do odczytania (jest w linku)
MichaelChirico
1
@MichaelChirico, ta odpowiedź stwierdza, że ​​„Nie zezwalamy (i nie zezwalamy) na <table>tagi. Przepraszamy. Jest to celowe i zgodne z projektem. Jeśli potrzebujesz szybkiej i brudnej„ tabeli ”, użyj <pre>i układu ASCII.” Nie sądzę, aby tabela ASCII była bardziej czytelna niż powyższa lista.
akaihola
to niefortunne. i tak masz moje poparcie ... może odwołać się do tabeli w linku w odpowiedzi?
MichaelChirico
25
def local_to_utc(t):
    secs = time.mktime(t)
    return time.gmtime(secs)

def utc_to_local(t):
    secs = calendar.timegm(t)
    return time.localtime(secs)

Źródło: http://feihonghsu.blogspot.com/2008/02/converting-from-local-time-to-utc.html

Przykładowe użycie z bd808 : Jeśli źródłem jest datetime.datetimeobiekt t, wywołaj jako:

local_to_utc(t.timetuple())
Chuck Callebs
źródło
5
Jeśli źródłem jest obiekt datetime.datetime t, wywołaj jako: local_to_utc (t.timetuple ())
bd808,
2
.timetuple()zestawy połączeń tm_isdstdo -1; istnieje 50% szans na mktime()niepowodzenie podczas przejścia DST.
jfs
1
Rozwiązanie Chucka traci informacje milisekundowe.
Luca,
21

Mam szczęście z dateutil (który jest powszechnie zalecany w SO w przypadku innych powiązanych pytań):

from datetime import *
from dateutil import *
from dateutil.tz import *

# METHOD 1: Hardcode zones:
utc_zone = tz.gettz('UTC')
local_zone = tz.gettz('America/Chicago')
# METHOD 2: Auto-detect zones:
utc_zone = tz.tzutc()
local_zone = tz.tzlocal()

# Convert time string to datetime
local_time = datetime.strptime("2008-09-17 14:02:00", '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in local time zone since 
# datetime objects are 'naive' by default
local_time = local_time.replace(tzinfo=local_zone)
# Convert time to UTC
utc_time = local_time.astimezone(utc_zone)
# Generate UTC time string
utc_string = utc_time.strftime('%Y-%m-%d %H:%M:%S')

(Kod powstał na podstawie tej odpowiedzi do Konwertuj ciąg danych UTC na lokalny czas danych )

Yarin
źródło
dateutilmoże się nie powieść w przeszłości, jeśli lokalna strefa czasowa miała inne przesunięcie utc niż (niezwiązane z przejściami DST) w systemach, w których implementacja nie korzysta z historycznej bazy danych stref czasowych (zwłaszcza Windows). pytz+ tzlocalmożna zamiast tego użyć.
jfs
@ JFSebastian - Nie słyszałem o tym - gdzie możemy uzyskać więcej informacji?
Yarin,
weź maszynę z Windows, ustaw dowolną strefę czasową, która miała inne przesunięcie utc w przeszłości, np. Europa / Moskwa. Porównaj wyniki z pytz . Ponadto możesz przetestować ten błąd, który nie działa nawet w systemie Ubuntu, chociaż nie jestem pewien, czy w tym przypadku jest prawidłowe użycie interfejsu API.
jfs
Jaka jest wydajność? Proszę opublikować wynik.
not2qubit
18

Jeszcze jeden przykład z pytzem, ale zawiera localize (), który uratował mi dzień.

import pytz, datetime
utc = pytz.utc
fmt = '%Y-%m-%d %H:%M:%S'
amsterdam = pytz.timezone('Europe/Amsterdam')

dt = datetime.datetime.strptime("2012-04-06 10:00:00", fmt)
am_dt = amsterdam.localize(dt)
print am_dt.astimezone(utc).strftime(fmt)
'2012-04-06 08:00:00'
Paulius Sladkevičius
źródło
12

Miałem największy sukces z python-dateutil :

from dateutil import tz

def datetime_to_utc(date):
    """Returns date in UTC w/o tzinfo"""
    return date.astimezone(tz.gettz('UTC')).replace(tzinfo=None) if date.tzinfo else date
Shu Wu
źródło
7
import time

import datetime

def Local2UTC(LocalTime):

    EpochSecond = time.mktime(LocalTime.timetuple())
    utcTime = datetime.datetime.utcfromtimestamp(EpochSecond)

    return utcTime

>>> LocalTime = datetime.datetime.now()

>>> UTCTime = Local2UTC(LocalTime)

>>> LocalTime.ctime()

'Thu Feb  3 22:33:46 2011'

>>> UTCTime.ctime()

'Fri Feb  4 05:33:46 2011'
Scipythonee
źródło
mktime(dt.timetuple())może zawieść podczas przejścia DST . Jest LocalTimezonew dokumentach (działa dla najnowszej definicji strefy czasowej, ale może się nie powieść w przypadku przeszłych dat (niezwiązanych z DST))
jfs
5

jeśli wolisz datetime.datetime:

dt = datetime.strptime("2008-09-17 14:04:00","%Y-%m-%d %H:%M:%S")
utc_struct_time = time.gmtime(time.mktime(dt.timetuple()))
utc_dt = datetime.fromtimestamp(time.mktime(utc_struct_time))
print dt.strftime("%Y-%m-%d %H:%M:%S")
użytkownik235042
źródło
1
Jasne, ale nie rozumiem, dlaczego tak wolisz. Wymaga dodatkowego importu i 3 dodatkowych wywołań funkcji niż moja wersja ...
Tom
% Z i% z nadal drukują białe spacje.
Pavel Vlasov
2
to jest niepoprawne. Nie powiedzie się, jeśli lokalna strefa czasowa nie jest UTC. mktime()oczekuje czasu lokalnego jako danych wejściowych. fromtimestamp()zwraca czas lokalny, nie utc. Jeśli to naprawisz, zobacz te dodatkowe problemy (na przykład dateutilrozwiązanie tylko dla stdlib może się nie powieść)
jfs
5

Prosty

Zrobiłem tak:

>>> utc_delta = datetime.utcnow()-datetime.now()
>>> utc_time = datetime(2008, 9, 17, 14, 2, 0) + utc_delta
>>> print(utc_time)
2008-09-17 19:01:59.999996

Fantazyjne wdrożenie

Jeśli chcesz się podoba, możesz zmienić to w funktor:

class to_utc():
    utc_delta = datetime.utcnow() - datetime.now()

    def __call__(cls, t):
        return t + cls.utc_delta

Wynik:

>>> utc_converter = to_utc()
>>> print(utc_converter(datetime(2008, 9, 17, 14, 2, 0)))
2008-09-17 19:01:59.999996
uclatommy
źródło
5
nie działa to na czas letni - np. jeśli obecnie jest lato, a data konwersji to zima. a pytanie dotyczyło konwersji dat zapisanych jako ciągi ...
Tom
1
Do Twojej wiadomości Największym problemem związanym z tą metodą (oprócz oszczędności w świetle dziennym) jest kilka milisekund, o które delta się wyłączy. Wszystkie moje zaproszenia z kalendarza są wyświetlane jako wyłączone o 1 minutę.
Nostalg.io
4

Możesz to zrobić za pomocą:

>>> from time import strftime, gmtime, localtime
>>> strftime('%H:%M:%S', gmtime()) #UTC time
>>> strftime('%H:%M:%S', localtime()) # localtime
Cristian Salamea
źródło
3

Co powiesz na -

time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(seconds))

jeśli jest sekunda, Noneto konwertuje czas lokalny na czas UTC, w przeciwnym razie konwertuje upływ czasu na UTC.

Tomek
źródło
2

Do poruszania się przy świetle dziennym itp.

Żadna z powyższych odpowiedzi nie pomogła mi szczególnie. Poniższy kod działa dla GMT.

def get_utc_from_local(date_time, local_tz=None):
    assert date_time.__class__.__name__ == 'datetime'
    if local_tz is None:
        local_tz = pytz.timezone(settings.TIME_ZONE) # Django eg, "Europe/London"
    local_time = local_tz.normalize(local_tz.localize(date_time))
    return local_time.astimezone(pytz.utc)

import pytz
from datetime import datetime

summer_11_am = datetime(2011, 7, 1, 11)
get_utc_from_local(summer_11_am)
>>>datetime.datetime(2011, 7, 1, 10, 0, tzinfo=<UTC>)

winter_11_am = datetime(2011, 11, 11, 11)
get_utc_from_local(winter_11_am)
>>>datetime.datetime(2011, 11, 11, 11, 0, tzinfo=<UTC>)
Dantalion
źródło
2

Korzystanie z http://crsmithdev.com/arrow/

arrowObj = arrow.Arrow.strptime('2017-02-20 10:00:00', '%Y-%m-%d %H:%M:%S' , 'US/Eastern')

arrowObj.to('UTC') or arrowObj.to('local') 

Ta biblioteka ułatwia życie :)

Yash
źródło
arrow is awesome: arrow.get (datetime.now (), 'local'). to ('utc'). naiwny
SteveJ
1

Tutaj znalazłem najlepszą odpowiedź na inne pytanie . Używa tylko bibliotek wbudowanych w Pythona i nie wymaga wprowadzania lokalnej strefy czasowej (w moim przypadku wymaganie)

import time
import calendar

local_time = time.strptime("2018-12-13T09:32:00.000", "%Y-%m-%dT%H:%M:%S.%f")
local_seconds = time.mktime(local_time)
utc_time = time.gmtime(local_seconds)

Ponownie zamieszczam odpowiedź tutaj, ponieważ to pytanie pojawia się w Google zamiast połączonego pytania, w zależności od słów kluczowych wyszukiwania.

frankands
źródło
1

Mam ten kod w jednym z moich projektów:

from datetime import datetime
## datetime.timezone works in newer versions of python
try:
    from datetime import timezone
    utc_tz = timezone.utc
except:
    import pytz
    utc_tz = pytz.utc

def _to_utc_date_string(ts):
    # type (Union[date,datetime]]) -> str
    """coerce datetimes to UTC (assume localtime if nothing is given)"""
    if (isinstance(ts, datetime)):
        try:
            ## in python 3.6 and higher, ts.astimezone() will assume a
            ## naive timestamp is localtime (and so do we)
            ts = ts.astimezone(utc_tz)
        except:
            ## in python 2.7 and 3.5, ts.astimezone() will fail on
            ## naive timestamps, but we'd like to assume they are
            ## localtime
            import tzlocal
            ts = tzlocal.get_localzone().localize(ts).astimezone(utc_tz)
    return ts.strftime("%Y%m%dT%H%M%SZ")
tobixen
źródło
0

W python3:

pip install python-dateutil

from dateutil.parser import tz

mydt.astimezone(tz.gettz('UTC')).replace(tzinfo=None) 
spedy
źródło
wydaje się, że to nie działa, powoduje wyjątek ValueError: ValueError: astimezone () nie można zastosować do naiwnej
daty
Powinno to być: from dateutil import tz
Javier
-3

Co powiesz na -

time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(seconds))

jeśli jest sekunda, Noneto konwertuje czas lokalny na czas UTC, w przeciwnym razie konwertuje upływ czasu na UTC.

Mohammad Efazati
źródło
1
To nie pomaga, pytanie polegało na konwersji danego ciągu czasu lokalnego na ciąg utc.
Tom