¿Cómo convertir la cadena de hora local a UTC?

308

¿Cómo convierto una cadena de fecha y hora en hora local en una cadena en hora UTC ?

Estoy seguro de que he hecho esto antes, pero no puedo encontrarlo y SO espero que me ayude (y a otros) a hacerlo en el futuro.

Aclaración : Por ejemplo, si tengo 2008-09-17 14:02:00en mi zona horaria local ( +10), me gustaría generar una cadena con el equivalente UTCde tiempo: 2008-09-17 04:02:00.

Además, en http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ , tenga en cuenta que, en general, esto no es posible ya que con el horario de verano y otros problemas no hay una conversión única de la hora local a Hora UTC.

Tom
fuente
1
Tenga en cuenta que el método mktime () toma una "hora local" como entrada que puede no ser lo que se esperaba, lo usé y lo estropeó todo. Por favor, eche un vistazo a este enlace
Haifeng Zhang

Respuestas:

288

Primero, analiza la cadena en un objeto ingenuo datetime. Esta es una instancia datetime.datetimesin información de zona horaria adjunta. Consulte la documentación para datetime.strptimeobtener información sobre cómo analizar la cadena de fecha.

Use el pytzmódulo, que viene con una lista completa de zonas horarias + UTC. Averigua cuál es la zona horaria local, construye un objeto de zona horaria a partir de ella, y manipúlalo y adjúntalo a la fecha y hora ingenua.

Finalmente, use el datetime.astimezone()método para convertir la fecha y hora a UTC.

Código fuente, usando la zona horaria local "America / Los_Angeles", para la cadena "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)

Desde allí, puede usar el strftime()método para formatear la fecha y hora UTC según sea necesario:

utc_dt.strftime ("%Y-%m-%d %H:%M:%S")
John Millikin
fuente
77
Edite su respuesta con el siguiente código: [código] local.localize (ingenuo) [/ código] Vea el documento: enlace Desafortunadamente, el uso del argumento tzinfo de los constructores estándar de fecha y hora '' no funciona '' con pytz para muchas zonas horarias. >>> datetime (2002, 10, 27, 12, 0, 0, tzinfo = amsterdam) .strftime (fmt) '2002-10-27 12:00:00 AMT + 0020'
Sam Stoelinga
2
Aparentemente, el paso "averiguar cuál es la zona horaria local" resulta más difícil de lo que parece (prácticamente imposible).
Jason R. Coombs
2
Use el local.localize como lo sugiere @SamStoelinga, o de lo contrario no tendrá en cuenta el horario de verano.
Emil Stenström
Esta respuesta me ha sido muy útil, pero me gustaría señalar una trampa que encontré. Si proporciona la hora pero no la fecha, puede obtener resultados inesperados. Así que asegúrese de proporcionar una marca de tiempo completa.
Steven Mercatante
145

La función utcnow () del módulo datetime se puede usar para obtener la hora UTC actual.

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

Como dice el enlace mencionado anteriormente por Tom: http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ dice:

UTC es una zona horaria sin horario de verano y sigue siendo una zona horaria sin cambios de configuración en el pasado.

Siempre mida y almacene el tiempo en UTC .

Si necesita registrar dónde se tomó el tiempo, guárdelo por separado. ¡No almacene la información de hora local + zona horaria!

NOTA - Si alguno de sus datos se encuentra en una región que usa DST, use pytzy eche un vistazo a la respuesta de John Millikin.

Si desea obtener el tiempo UTC de una cadena determinada y tiene la suerte de estar en una región del mundo que no utiliza DST o tiene datos que solo se compensan desde UTC sin DST aplicado:

-> utilizando la hora local como base para el valor de compensación:

>>> # 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'

-> O, desde un desplazamiento conocido, usando 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'

ACTUALIZAR:

Desde python 3.2 datetime.timezoneestá disponible. Puede generar un objeto datetime con reconocimiento de zona horaria con el siguiente comando:

import datetime

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

Si estás listo para realizar conversiones de zona horaria, lee esto:

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

Monkut
fuente
14
Eso solo convierte la hora actual, necesito tomar un tiempo determinado (como una cadena) y convertir a UTC.
Tom
No creo que deba importar aquí, si está utilizando valores de desplazamiento estándar. local_time = utc_time + utc_offset AND utc_time = local_time - utc_offset.
monkut
55
Solo funciona con la hora actual porque las marcas de tiempo pasadas y futuras pueden tener un desplazamiento de UTC diferente debido al horario de verano.
Alex B
buen punto ... si tiene que lidiar con el horario de verano, probablemente debería estar usando pytz como lo menciona John Millikin.
monkut
1
Hay un delta esporádico entre llamadas a utcnow () y now (). Esa es una línea de código peligrosa que podría conducir a errores extraños más tarde, tzinfo.utcoffset() must return a whole number of minutesetc.
Pavel Vlasov
62

Gracias @rofly, la conversión completa de cadena a cadena es la siguiente:

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

Mi resumen de las funciones time/ calendar:

time.strptime
cadena -> tupla (no se aplica zona horaria, por lo que coincide con la cadena)

time.mktime
tupla de hora local -> segundos desde la época (siempre hora local)

time.gmtime
segundos desde la época -> tupla en UTC

y

calendar.timegm
tupla en UTC -> segundos desde la época

time.localtime
segundos desde la época -> tupla en la zona horaria local

Tom
fuente
44
(always local time)parece estar equivocado: la entrada a mktime () es hora local, la salida es segundos desde epoch ( 1970-01-01 00:00:00 +0000 (UTC)) que no depende de la zona horaria.
jfs
3
También hay un 50% de posibilidades de que falle durante la transición al horario de verano. Ver problemas con Localtime
jfs
38

Aquí hay un resumen de las conversiones de tiempo comunes de Python.

Algunos métodos sueltan fracciones de segundos y están marcados con (s) . En su lugar, ts = (d - epoch) / unitse puede usar una fórmula explícita como (gracias jfs).

  • struct_time (UTC) → POSIX (s) :
    calendar.timegm(struct_time)
  • Fecha y hora ingenua (local) → POSIX (s) :
    calendar.timegm(stz.localize(dt, is_dst=None).utctimetuple())
    (excepción durante las transiciones DST, ver comentario de jfs)
  • Fecha y hora ingenuas (UTC) → POSIX (s) :
    calendar.timegm(dt.utctimetuple())
  • Datetime consciente → POSIX (s) :
    calendar.timegm(dt.utctimetuple())
  • POSIX → struct_time (UTC, s ):
    time.gmtime(t)
    (ver comentario de jfs)
  • Fecha y hora ingenua (local) → struct_time (UTC, s ):
    stz.localize(dt, is_dst=None).utctimetuple()
    (excepción durante las transiciones DST, ver comentario de jfs)
  • Fecha y hora ingenuas (UTC) → struct_time (UTC, s ):
    dt.utctimetuple()
  • Datetime consciente → struct_time (UTC, s ):
    dt.utctimetuple()
  • POSIX → Fecha y hora ingenua (local):
    datetime.fromtimestamp(t, None)
    (puede fallar en ciertas condiciones, vea el comentario de jfs a continuación)
  • struct_time (UTC) → Fecha y hora ingenua (local, s ):
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
    (no puede representar segundos intercalares, ver comentario de jfs)
  • Fecha y hora ingenuas (UTC) → Fecha y hora ingenuas (local):
    dt.replace(tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
  • Datetime consciente → Datetime ingenuo (local):
    dt.astimezone(tz).replace(tzinfo=None)
  • POSIX → Fecha y hora ingenuas (UTC):
    datetime.utcfromtimestamp(t)
  • struct_time (UTC) → Fecha y hora ingenua (UTC, s ):
    datetime.datetime(*struct_time[:6])
    (no puede representar segundos intercalares, ver comentario de jfs)
  • Fecha y hora ingenuas (local) → Fecha y hora ingenuas (UTC):
    stz.localize(dt, is_dst=None).astimezone(UTC).replace(tzinfo=None)
    (excepción durante las transiciones de horario de verano, ver comentario de jfs)
  • Datetime consciente → Datetime ingenuo (UTC):
    dt.astimezone(UTC).replace(tzinfo=None)
  • POSIX → Fecha y hora conscientes:
    datetime.fromtimestamp(t, tz)
    (puede fallar para zonas horarias que no son de Pytz)
  • struct_time (UTC) → Datetime (s) conscientes :
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz)
    (no puede representar segundos bisiestos, ver comentario de jfs)
  • Fecha y hora ingenua (local) → Fecha y hora consciente:
    stz.localize(dt, is_dst=None)
    (excepción durante las transiciones DST, ver comentario de jfs)
  • Fecha y hora ingenuas (UTC) → Fecha y hora conscientes:
    dt.replace(tzinfo=UTC)

Fuente: taaviburns.ca

akaihola
fuente
1
(1) fromtimestamp(t, None)puede fallar si la zona horaria local tiene un desplazamiento utc diferente en t y la biblioteca C no tiene acceso a la base de datos tz en la plataforma dada. Puede utilizar tzlocal.get_localzone()para proporcionar tzdata de forma portátil. (2) fromtimestamp(t, tz)puede fallar para zonas horarias que no son de Pytz. (3) datetime(*struct_time[:6])te estás perdiendo *. (4) timegm(), utctimetuple(), struct_timelas soluciones basadas en caer fracciones de un segundo. Podría usar una fórmula explícita como: en su ts = (d - epoch) / unitlugar.
jfs
1
(5) stz.localize(dt, is_dst=None)plantea una excepción para tiempos locales ambiguos o inexistentes, por ejemplo, durante las transiciones DST. Para evitar la excepción, use un resultado stz.normalize(stz.localize(dt))que pueda arrojar resultados imprecisos. (6) datetime () no puede representar un segundo intercalar. Convierta struct_timeprimero a "segundos desde época" como solución alternativa. (7) a time.gmtime(t)diferencia de lo que calendar.timegm()puede esperar entrada no POSIX si se utiliza la zona horaria "correcta". Utilice la fórmula explícita si la entrada es POSIX en su lugar:gmtime = lambda t: datetime(1970,1,1, tzinfo=utc) + timedelta(seconds=t)
jfs
Desearía que esto estuviera en una tabla, más fácil de leer (está en el enlace)
MichaelChirico
1
@MichaelChirico, esta respuesta indica que "No permitimos (y no permitiremos) las <table>etiquetas. Lo sentimos. Esto es intencional y por diseño. Si necesita una" tabla "rápida y sucia, use un <pre>diseño ASCII". No creo que una tabla ASCII sea más legible que la lista anterior.
akaihola
bueno eso es lamentable. tienes mi voto positivo de todos modos ... ¿tal vez haga referencia a la tabla en el enlace dentro de su respuesta?
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)

Fuente: http://feihonghsu.blogspot.com/2008/02/converting-from-local-time-to-utc.html

Ejemplo de uso de bd808 : si su fuente es un datetime.datetimeobjeto t, llame como:

local_to_utc(t.timetuple())
Chuck Callebs
fuente
55
Si su fuente es un objeto datetime.datetime t, llame como: local_to_utc (t.timetuple ())
bd808
2
.timetuple()conjuntos de llamadas tm_isdsta -1; hay un 50% de posibilidades de que mktime()falle durante la transición al horario de verano.
jfs
1
La solución de Chuck pierde información de milisegundos.
Luca
21

Estoy teniendo buena suerte con dateutil (que se recomienda ampliamente en SO para otras preguntas relacionadas):

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

(El código se derivó de esta respuesta para convertir la cadena de fecha y hora UTC a fecha y hora local )

Yarin
fuente
dateutilpuede fallar para fechas pasadas si la zona horaria local tenía un desplazamiento utc diferente (no relacionado con las transiciones DST) en sistemas donde la implementación no utiliza una base de datos histórica de zonas horarias (especialmente Windows). pytz+ tzlocalpodría usarse en su lugar.
jfs
@ JFSebastian- No había oído hablar de eso, ¿dónde podemos obtener más información?
Yarin
tome la máquina de Windows, configure cualquier zona horaria que haya tenido una compensación de utc diferente en el pasado, por ejemplo, Europa / Moscú. Compara los resultados con pytz . Además, podría probar este error que falla incluso en Ubuntu, aunque no estoy seguro sobre el uso correcto de la API en este caso.
jfs
¿Cuál es la salida? Por favor, publique la salida.
not2qubit
18

Un ejemplo más con pytz, pero incluye localize (), que me salvó el día.

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
fuente
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
fuente
mktime(dt.timetuple())puede fallar durante la transición DST . Existe LocalTimezoneen los documentos (funciona para la definición de zona horaria más reciente pero puede fallar para fechas pasadas (no relacionadas con el horario de verano))
jfs
5

si prefiere 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")
usuario235042
fuente
1
Claro, pero no puedo ver por qué prefieres eso. Requiere una importación adicional y 3 llamadas a funciones más que mi versión ...
Tom
% Z y% z imprime espacios en blanco todavía.
Pavel Vlasov
2
es incorrecto Falla si la zona horaria local no es UTC. mktime()espera la hora local como entrada. fromtimestamp()devuelve la hora local, no utc. Si lo soluciona, vea estos problemas adicionales (por ejemplo dateutil, la solución solo stdlib puede fallar)
jfs
5

Sencillo

Lo hice así:

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

Implementación de lujo

Si quieres ponerte elegante, puedes convertir esto en un functor:

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

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

Resultado:

>>> utc_converter = to_utc()
>>> print(utc_converter(datetime(2008, 9, 17, 14, 2, 0)))
2008-09-17 19:01:59.999996
uclatommy
fuente
55
esto no funciona para el horario de verano, por ejemplo, si actualmente es verano y la fecha de conversión es en invierno. y la pregunta era sobre la conversión de fechas almacenadas como cadenas ...
Tom
1
FYI. El mayor problema con este método (además del horario de verano) son los pocos milisegundos por los que el delta estará desactivado. Todas mis invitaciones de calendario se muestran apagadas en 1 minuto.
Nostalg.io
4

Puedes hacerlo con:

>>> from time import strftime, gmtime, localtime
>>> strftime('%H:%M:%S', gmtime()) #UTC time
>>> strftime('%H:%M:%S', localtime()) # localtime
Cristian Salamea
fuente
3

Qué tal si -

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

si son segundos None, convierte la hora local en hora UTC; de lo contrario, convierte la hora pasada en UTC.

Tom
fuente
2

Para moverse por el horario de verano, etc.

Ninguna de las respuestas anteriores me ayudó particularmente. El siguiente código funciona para 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
fuente
2

Usando 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') 

Esta biblioteca hace la vida fácil :)

Yash
fuente
arrow is awesome: arrow.get (datetime.now (), 'local'). to ('utc'). naive
SteveJ
1

Encontré la mejor respuesta en otra pregunta aquí . Solo usa bibliotecas integradas de Python y no requiere que ingrese su zona horaria local (un requisito en mi caso)

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)

Estoy volviendo a publicar la respuesta aquí, ya que esta pregunta aparece en Google en lugar de la pregunta vinculada, dependiendo de las palabras clave de búsqueda.

franksands
fuente
1

Tengo este código en uno de mis proyectos:

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
fuente
0

En python3:

pip install python-dateutil

from dateutil.parser import tz

mydt.astimezone(tz.gettz('UTC')).replace(tzinfo=None) 
Spedy
fuente
esto no parece funcionar, genera una excepción ValueError: ValueError: astimezone () no se puede aplicar a una fecha
Stormsson
Debería ser: from dateutil import tz
Javier
-3

Qué tal si -

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

si son segundos None, convierte la hora local en hora UTC; de lo contrario, convierte la hora pasada en UTC.

Mohammad Efazati
fuente
1
Esto no ayuda, la pregunta era convertir una cadena de tiempo local dada en una cadena utc.
Tom