Python: averiguar la zona horaria local

Quiero comparar las marcas de tiempo UTC de un archivo de registro con las marcas de tiempo locales. Al crear el objeto local de datetime , uso algo como:

 >>> local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0, tzinfo=pytz.timezone('Israel')) 

Quiero encontrar una herramienta automática que reemplace tzinfo=pytz.timezone('Israel') con la zona horaria local actual.

¿Algunas ideas?

Prueba dateutil , que tiene un tipo tzlocal que hace lo que necesitas.

En Python 3.x, la zona horaria local se puede calcular de la siguiente manera:

 import datetime LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo 

Es un uso complicado del código de datetime .

para comparar las marcas de tiempo UTC de un archivo de registro con las marcas de tiempo locales.

Es difícil averiguar el nombre de Olson TZ para una zona horaria local de manera portátil. Afortunadamente, no es necesario para realizar la comparación.

tzlocal módulo tzlocal devuelve una zona horaria pytz correspondiente a la zona horaria local:

 from datetime import datetime import pytz # $ pip install pytz from tzlocal import get_localzone # $ pip install tzlocal tz = get_localzone() local_dt = tz.localize(datetime(2010, 4, 27, 12, 0, 0, 0), is_dst=None) utc_dt = local_dt.astimezone(pytz.utc) #NOTE: utc.normalize() is unnecessary here 

A diferencia de otras soluciones presentadas hasta ahora, el código anterior evita los siguientes problemas:

  • la hora local puede ser ambigua , es decir, una comparación precisa podría ser imposible para algunas horas locales
  • El desplazamiento de utc puede ser diferente para el mismo nombre de zona horaria local para las fechas en el pasado. Algunas bibliotecas que admiten objetos de fecha y hora compatibles con la zona horaria (por ejemplo, dateutil ) no lo tienen en cuenta

Nota: para obtener el objeto de fecha y hora de la zona horaria de un objeto de fecha y hora ingenuo, debe usar * :

 local_dt = tz.localize(datetime(2010, 4, 27, 12, 0, 0, 0), is_dst=None) 

en lugar de:

 #XXX fails for some timezones local_dt = datetime(2010, 4, 27, 12, 0, 0, 0, tzinfo=tz) 

* is_dst=None fuerza una excepción si la hora local dada es ambigua o inexistente.

Si está seguro de que todas las marcas de tiempo locales utilizan el mismo desplazamiento utc (actual) para la zona horaria local, entonces podría realizar la comparación utilizando solo stdlib:

 # convert a naive datetime object that represents time in local timezone to epoch time timestamp1 = (datetime(2010, 4, 27, 12, 0, 0, 0) - datetime.fromtimestamp(0)).total_seconds() # convert a naive datetime object that represents time in UTC to epoch time timestamp2 = (datetime(2010, 4, 27, 9, 0) - datetime.utcfromtimestamp(0)).total_seconds() 

timestamp1 y timestamp2 se pueden comparar directamente.

Nota:

  • timestamp1 fórmula de timestamp1 funciona solo si el desplazamiento UTC en la época ( datetime.fromtimestamp(0) ) es el mismo que ahora
  • fromtimestamp() crea un objeto de fecha y hora ingenuo en la zona horaria local actual
  • utcfromtimestamp() crea un objeto de fecha y hora ingenuo en UTC.

Me preguntaba lo mismo a mí mismo, y encontré la respuesta en 1:

Eche un vistazo a la sección 8.1.7: el formato “% z” (en minúsculas, la mayúscula Z también devuelve la zona horaria, pero no en el formato de 4 dígitos, sino en forma de abreviaturas de zona horaria, como en [3]) of strftime devuelve el formulario “+/- 4DIGIT” que es estándar en los encabezados de correo electrónico (consulte la sección 3.3 de RFC 2822, vea [2], que obsoleta las otras formas de especificar la zona horaria para los encabezados de correo electrónico).

Entonces, si quieres tu zona horaria en este formato, usa:

 time.strftime("%z") 

[1] http://docs.python.org/2/library/datetime.html

[2] http://tools.ietf.org/html/rfc2822#section-3.3

[3] Abreviaturas de la zona horaria: http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations , solo como referencia.

Primero obtén los módulos pytz y tzlocal.

 pip install pytz tzlocal 

entonces

 from tzlocal import get_localzone local = get_localzone() 

entonces puedes hacer cosas como

 from datetime import datetime print(datetime.now(local)) 

Aquí hay una forma de obtener la zona horaria local utilizando solo la biblioteca estándar, (solo funciona en un entorno * nix):

 >>> '/'.join(os.path.realpath('/etc/localtime').split('/')[-2:]) 'Australia/Sydney' 

Puedes usar esto para crear una pytz horaria de pytz :

 >>> import pytz >>> my_tz_name = '/'.join(os.path.realpath('/etc/localtime').split('/')[-2:]) >>> my_tz = pytz.timezone(my_tz_name) >>> my_tz  

… que luego puede aplicar a una datetime y datetime :

 >>> import datetime >>> now = datetime.datetime.now() >>> now datetime.datetime(2014, 9, 3, 9, 23, 24, 139059) >>> now.replace(tzinfo=my_tz) >>> now datetime.datetime(2014, 9, 3, 9, 23, 24, 139059, tzinfo=) 

Basado en la respuesta de JF Sebastian , puedes hacer esto con la biblioteca estándar:

 import time, datetime local_timezone = datetime.timezone(datetime.timedelta(seconds=-time.timezone)) 

Probado en 3.4, debería funcionar en 3.4+

Aquí hay una versión un poco más concisa de la solución de @ vbem:

 from datetime import datetime as dt dt.utcnow().astimezone().tzinfo 

La única diferencia sustancial es que reemplacé datetime.datetime.now(datetime.timezone.utc) con datetime.datetime.utcnow() . Para mayor brevedad, también puse alias datetime.datetime como dt .

Para mis propósitos, quiero el desplazamiento UTC en segundos. Esto es lo que parece:

 dt.utcnow().astimezone().utcoffset().total_seconds() 

Evitar el módulo no estándar (parece ser un método faltante del módulo datetime):

 from datetime import datetime utcOffset_min = int(round((datetime.now() - datetime.utcnow()).total_seconds())) / 60 # round for taking time twice utcOffset_h = utcOffset_min / 60 assert(utcOffset_min == utcOffset_h * 60) # we do not handle 1/2 h timezone offsets print 'Local time offset is %ih to UTC.' % (utcOffset_h) 

Basado en la respuesta de Thoku anterior, aquí hay una respuesta que resuelve la zona horaria a la media hora más cercana (que es relevante para algunas zonas horarias, por ejemplo, el sur de Australia):

 from datetime import datetime round((round((datetime.now()-datetime.utcnow()).total_seconds())/1800)/2) 

Para cosas simples, se puede usar la siguiente implementación tzinfo , que consulta al sistema operativo para obtener compensaciones de zona horaria:

 import datetime import time class LocalTZ(datetime.tzinfo): _unixEpochOrdinal = datetime.datetime.utcfromtimestamp(0).toordinal() def dst(self, dt): return datetime.timedelta(0) def utcoffset(self, dt): t = (dt.toordinal() - self._unixEpochOrdinal)*86400 + dt.hour*3600 + dt.minute*60 + dt.second + time.timezone utc = datetime.datetime(*time.gmtime(t)[:6]) local = datetime.datetime(*time.localtime(t)[:6]) return local - utc print datetime.datetime.now(LocalTZ()) print datetime.datetime(2010, 4, 27, 12, 0, 0, tzinfo=LocalTZ()) # If you're in the EU, the following datetimes are right on the DST change. print datetime.datetime(2013, 3, 31, 0, 59, 59, tzinfo=LocalTZ()) print datetime.datetime(2013, 3, 31, 1, 0, 0, tzinfo=LocalTZ()) print datetime.datetime(2013, 3, 31, 1, 59, 59, tzinfo=LocalTZ()) # The following datetime is invalid, as the clock moves directly from # 01:59:59 standard time to 03:00:00 daylight savings time. print datetime.datetime(2013, 3, 31, 2, 0, 0, tzinfo=LocalTZ()) print datetime.datetime(2013, 10, 27, 0, 59, 59, tzinfo=LocalTZ()) print datetime.datetime(2013, 10, 27, 1, 0, 0, tzinfo=LocalTZ()) print datetime.datetime(2013, 10, 27, 1, 59, 59, tzinfo=LocalTZ()) # The following datetime is ambigous, as 02:00 can be either DST or standard # time. (It is interpreted as standard time.) print datetime.datetime(2013, 10, 27, 2, 0, 0, tzinfo=LocalTZ()) 
 now_dt = datetime.datetime.now() utc_now = datetime.datetime.utcnow() now_ts, utc_ts = map(time.mktime, map(datetime.datetime.timetuple, (now_dt, utc_now))) offset = int((now_ts - utc_ts) / 3600) 

Espero que esto te ayudará.

Primero, tenga en cuenta que la pregunta presenta una inicialización incorrecta de un objeto de fecha y hora consciente:

 >>> local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0, ... tzinfo=pytz.timezone('Israel')) 

crea una instancia inválida Uno puede ver el problema al calcular el desplazamiento UTC del objeto resultante:

 >>> print(local_time.utcoffset()) 2:21:00 

(Note el resultado que es una fracción impar de una hora).

Para inicializar correctamente una fecha y hora correcta usando pytz, se debe usar el método localize() siguiente manera:

 >>> local_time=pytz.timezone('Israel').localize(datetime.datetime(2010, 4, 27, 12)) >>> print(local_time.utcoffset()) 3:00:00 

Ahora, si necesita una zona horaria local de pytz como el nuevo tzinfo, debe usar el paquete tzlocal como otros lo han explicado, pero si todo lo que necesita es una instancia con un desplazamiento de zona horaria local y una abreviatura correctos y luego comenzar con Python 3.3, llame al método astimezone() sin argumentos para convertir una instancia de datetime para su zona horaria local:

 >>> local_time.astimezone().strftime('%Y-%m-%d %H:%M %Z %z') '2010-04-27 05:00 EDT -0400' 

Puede ser feliz con el péndulo

 >>> pendulum.datetime(2015, 2, 5, tz='local').timezone.name 'Israel' 

El péndulo tiene una API bien diseñada para manipular fechas. Todo es consciente de TZ.

tzlocal desde dateutil .

Código de ejemplo que sigue. Última cadena adecuada para su uso en nombres de archivos.

 >>> from datetime import datetime >>> from dateutil.tz import tzlocal >>> str(datetime.now(tzlocal())) '2015-04-01 11:19:47.980883-07:00' >>> str(datetime.now(tzlocal())).replace(' ','-').replace(':','').replace('.','-') '2015-04-01-111947-981879-0700' >>>