Convertir cadena de fecha y hora UTC a fecha y hora local con Python


Nunca he tenido que convertir el tiempo hacia y desde UTC. Recientemente tuve una solicitud para que mi aplicación sea consciente de la zona horaria, y he estado corriendo en círculos. Mucha información sobre la conversión de la hora local a UTC, que me pareció bastante elemental (tal vez lo estoy haciendo mal también), pero no puedo encontrar ninguna información sobre la conversión fácil de la hora UTC a la zona horaria de los usuarios finales.

En pocas palabras, y la aplicación Android me envía datos (appengine app) y dentro de esos datos hay una marca de tiempo. Para almacenar eso marca de tiempo a la hora utc que estoy usando:

datetime.utcfromtimestamp(timestamp)

Eso parece estar funcionando. Cuando mi aplicación almacena los datos, se está almacenando como 5 horas antes (estoy EST -5)

Los datos se almacenan en BigTable de appengine, y cuando se recuperan, aparecen como una cadena como esta:

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

¿Cómo convertir esta cadena a una fecha y hora en la zona horaria correcta de los usuarios?

Además, ¿cuál es el almacenamiento recomendado para una información de zona horaria de los usuarios? (¿ Cómo se suele almacenar tz info ie: "-5: 00" o "EST" etc etc? Estoy seguro de que la respuesta a mi primera pregunta podría contener un parámetro las respuestas a la segunda.

Author: Martin Thoma, 2011-01-22

10 answers

Si no desea proporcionar sus propios objetos tzinfo, consulte la biblioteca python-dateutil. Proporciona tzinfo implementaciones sobre una base de datos zoneinfo (Olson) de modo que puede referirse a las reglas de zona horaria con un nombre algo canónico.

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)

Editar Ejemplo expandido para mostrar strptime uso

Edit 2 Se corrigió el uso de la API para mostrar un mejor método de punto de entrada

Edit 3 Métodos de detección automática incluidos para zonas horarias (Yarin)

 288
Author: Joe Holloway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-19 17:52:33

Aquí hay un método resiliente que no depende de ninguna biblioteca externa:

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

Esto evita los problemas de tiempo en el ejemplo de DelboyJay. Y los problemas de menor tiempo en la enmienda de Erik van Oosten.

Como nota al pie interesante, el desplazamiento de zona horaria calculado anteriormente puede diferir de la siguiente expresión aparentemente equivalente, probablemente debido a los cambios en las reglas de ahorro de luz diurna:

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

Actualización: Este fragmento tiene la debilidad de usar el desplazamiento UTC de la tiempo presente, que puede diferir del desplazamiento UTC de la fecha y hora de entrada. Ver comentarios sobre esta respuesta para otra solución.

Para moverse por los diferentes tiempos, tome el tiempo de la época del tiempo pasado. Esto es lo que hago:

def utc2local (utc):
    epoch = time.mktime(utc.timetuple())
    offset = datetime.fromtimestamp (epoch) - datetime.utcfromtimestamp (epoch)
    return utc + offset
 28
Author: David Foster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-28 20:49:20

Vea la documentación datetimesobre los objetos tzinfo. Tienes que implementar las zonas horarias que quieres mantener a ti mismo. Los ejemplos en la parte inferior de la documentación.

Aquí hay un ejemplo simple:

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)

Salida

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
 17
Author: Mark Tolonen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-22 21:52:16

Si desea obtener el resultado correcto incluso para la hora que corresponde a una hora local ambigua (por ejemplo, durante una transición DST) y / o el desplazamiento utc local es diferente en diferentes momentos de su zona horaria local, use pytz zonas horarias:

#!/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)
 11
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-03 00:35:58

Si usa django, puede usar la zona horaria.método localtime (ver https://docs.djangoproject.com/en/dev/topics/i18n/timezones/).

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>)
 5
Author: frmdstryr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-04 14:08:23

Esta respuesta debería ser útil si no desea utilizar ningún otro módulo además de datetime.

datetime.utcfromtimestamp(timestamp) devuelve un objeto naive datetime (no uno consciente). Los conscientes son conscientes de la zona horaria, y los ingenuos no lo son. Si desea realizar una conversión entre zonas horarias (por ejemplo, entre UTC y hora local), debe utilizar una de ellas.

Si usted no es el que crea una instancia de la fecha para comenzar, pero aún puede crear un objeto datetime ingenuo en hora UTC, es posible que desee probar este Python 3.código x a convertir it:

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

Tenga cuidado de no asumir erróneamente que si su zona horaria es actualmente MDT que el horario de verano no funciona con el código anterior, ya que imprime MST. Notarás que si cambias el mes a agosto, se imprimirá MDT.

Otra forma fácil de obtener un objeto aware datetime (también en Python 3.x) es crearlo con una zona horaria especificada para empezar. Aquí hay un ejemplo, usando 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))

Si utiliza Python 2.x, probablemente tendrás que subclase datetime.tzinfo y úsalo para ayudarte a crear un objeto aware datetime, ya que datetime.timezone no existe en Python 2.x.

 3
Author: Shule,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-21 10:54:34

Tradicionalmente defiendo esto al frontend send envío tiempos desde el backend como marcas de tiempo o algún otro formato de fecha y hora en UTC, luego dejo que el cliente calcule el desplazamiento de la zona horaria y renderice estos datos en la zona horaria adecuada.

Para una aplicación web, esto es bastante fácil de hacer en javascript you puede averiguar el desplazamiento de la zona horaria del navegador con bastante facilidad utilizando métodos incorporados y luego renderizar los datos del backend correctamente.

 1
Author: Matt Billenstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-23 00:58:06

Aquí hay una versión rápida y sucia que usa la configuración del sistema local para calcular la diferencia horaria. NOTA: Esto no funcionará si necesita convertir a una zona horaria en la que su sistema actual no se esté ejecutando. He probado esto con la configuración del Reino Unido bajo la zona horaria BST

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
 1
Author: DelboyJay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-15 09:38:26

Puede usar calendar.timegm para convertir su tiempo a segundos desde Unix epoch y time.localtime para volver a convertir:

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)

Da Fri Jan 21 05:37:21 2011 (porque estoy en UTC+03:00 zona horaria).

 1
Author: myaut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-08 15:52:15

Puedes usar flecha

from datetime import datetime
import arrow

now = datetime.utcnow()

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

Puedes alimentar arrow.get() con cualquier cosa. marca de tiempo, cadena iso etc

 -2
Author: d21d3q,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-04 14:07:13