Pregunta Convierta la cadena de fecha y hora UTC en hora local con Python


Nunca tuve que convertir el tiempo hacia y desde UTC. Hace poco recibí una solicitud para que mi aplicación tenga en cuenta la zona horaria y me corrí en círculos. Mucha información sobre la conversión de hora local a UTC, que encontré bastante elemental (tal vez estoy haciendo eso mal también), pero no puedo encontrar ninguna información sobre cómo convertir fácilmente la hora UTC a la zona horaria de los usuarios finales.

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

datetime.utcfromtimestamp(timestamp)

Eso parece estar funcionando. Cuando mi aplicación almacena los datos, se almacenan con 5 horas de anticipación (soy EST -5)

Los datos se almacenan en la BigTable de appengine, y cuando se recuperan, salen como una cadena así:

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

¿Cómo convierto esta cadena a DateTime en la zona horaria correcta de los usuarios?

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


144
2018-01-22 20:14


origen


Respuestas:


Si no quieres proporcionar tu propio tzinfo objetos, mira el python-dateutil biblioteca. Proporciona tzinfo implementaciones en la parte superior de una base de datos zoneinfo (Olson) de modo que puede referirse a las reglas de zona horaria mediante 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

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

Editar 3 Métodos de autodetección incluidos para zonas horarias (Yarin)


274
2018-01-23 01:23



Aquí hay un método resistente 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 sincronización en el ejemplo de DelboyJay. Y los problemas de sincronización menores en la enmienda de Erik van Oosten.

Como una nota al pie interesante, la compensación de la zona horaria calculada anteriormente puede diferir de la siguiente expresión aparentemente equivalente, probablemente debido a cambios en la regla de ahorro de luz natural:

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

Actualizar: Este fragmento tiene la debilidad de utilizar el desplazamiento UTC de la hora actual, que puede diferir del desplazamiento UTC de la fecha de entrada. Ver comentarios sobre esta respuesta para otra solución.

Para sortear los diferentes momentos, tome la época del tiempo transcurrido. Esto es lo que hago:

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

25
2017-10-08 03:24



Ver el fecha y hora documentación sobre tzinfo objetos. Tienes que implementar las zonas horarias que quieras mantener tú mismo. Los son 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
2018-01-22 21:22



Si desea obtener el resultado correcto incluso para el tiempo 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 en su zona horaria local, utilice 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)

9
2017-10-02 09:57



Si usa django, puede usar el método timezone.localtime (consulte 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>)

4
2017-08-04 14:08



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

datetime.utcfromtimestamp(timestamp) devuelve una ingenua datetime objeto (no uno consciente). Los conscientes son conscientes de la zona horaria y los ingenuos no. Desea conocer si desea convertir zonas horarias (por ejemplo, entre UTC y hora local).

Si no eres el primero en crear la fecha, puedes crear una ingenua datetime objeto en tiempo UTC, es posible que desee probar este código de Python 3.x para convertirlo:

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, el horario de verano no funciona con el código anterior ya que imprime MST. Notará que si cambia el mes a agosto, imprimirá MDT.

Otra manera fácil de hacerse consciente datetime objeto (también en Python 3.x) es crearlo con una zona horaria especificada para comenzar. 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 usa Python 2.x, probablemente tendrá que subclasificar datetime.tzinfo y usa eso para ayudarte a crear un conocimiento datetime objeto, ya que datetime.timezone no existe en Python 2.x.


3
2017-09-21 08:53



Tradicionalmente difiero esto al frontend: envíe tiempos desde el backend como marcas de tiempo o algún otro formato de fecha y hora en UTC, luego deje que el cliente determine el desfase de la zona horaria y represente estos datos en la zona horaria adecuada.

Para una aplicación web, esto es bastante fácil de hacer en JavaScript: puede calcular el desplazamiento de la zona horaria del navegador con bastante facilidad usando métodos integrados y luego procesar los datos desde el servidor de forma adecuada.


1
2018-01-23 00:58



Aquí hay una versión rápida y sucia que usa la configuración de sistemas locales para calcular la diferencia horaria. NOTA: Esto no funcionará si necesita convertir a una zona horaria en la que no se está ejecutando su sistema actual. He probado esto con la configuración de Reino Unido en 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
2018-04-15 09:38



Puedes usar calendar.timegm para convertir tu tiempo a segundos desde la época de Unix y time.localtime convertir de nuevo:

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 la zona horaria UTC + 03: 00).


1
2018-06-08 15:52



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, cuerda iso, etc.


-1
2018-04-04 14:07



Preguntas populares