web-dev-qa-db-fra.com

Comment obtenir le réglage du fuseau horaire du système et le transmettre à pytz.timezone?

time.tzname permet d’obtenir un nom de fuseau horaire local, mais ce nom n’est pas compatible avec pytz.timezone.

En fait, le nom renvoyé par time.tzname est ambigu. Cette méthode renvoie ('CST', 'CST') dans mon système, mais 'CST' peut indiquer quatre fuseaux horaires:

  • Fuseau horaire central (Amérique du Nord) - observé dans le fuseau horaire central de l'Amérique du Nord
  • Heure normale de Chine
  • Heure normale de Chungyuan - le terme "heure standard de Chungyuan" est maintenant rarement utilisé à Taiwan
  • Heure centrale australienne (ACST)
44
user805627

Une méthode très simple pour résoudre cette question:

import time

def localTzname():
    offsetHour = time.timezone / 3600
    return 'Etc/GMT%+d' % offsetHour

Mise à jour : @MartijnPieters a déclaré: «Cela ne fonctionnera pas avec l'heure d'été/heure d'été. Alors, qu'en est-il de cette version?

import time

def localTzname():
    if time.daylight:
        offsetHour = time.altzone / 3600
    else:
        offsetHour = time.timezone / 3600
    return 'Etc/GMT%+d' % offsetHour
9
user805627

tzlocal module retourne l'objet de pytz tzinfo correspondant au fuseau horaire local:

import time
from datetime import datetime

import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal

# get local timezone    
local_tz = get_localzone() 

# test it
# utc_now, now = datetime.utcnow(), datetime.now()
ts = time.time()
utc_now, now = datetime.utcfromtimestamp(ts), datetime.fromtimestamp(ts)

local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now

Il fonctionne même pendant les transitions à l'heure avancée lorsque l'heure locale peut être ambiguë.

local_tz fonctionne également pour les dates antérieures même si le décalage utc pour le fuseau horaire local était différent à l'époque. La solution basée sur dateutil.tz.tzlocal()- échoue dans ce cas, par exemple, dans le fuseau horaire Europe/Moscou (exemple à partir de 2013):

>>> import os, time
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> time.tzset()
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from tzlocal import get_localzone
>>> dateutil_tz = tzlocal()
>>> tzlocal_tz = get_localzone()
>>> datetime.fromtimestamp(0, dateutil_tz)                              
datetime.datetime(1970, 1, 1, 4, 0, tzinfo=tzlocal())
>>> datetime.fromtimestamp(0, tzlocal_tz)
datetime.datetime(1970, 1, 1, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+3:00:00 STD>)

dateutil renvoie le décalage faux UTC + 4 au lieu du UTC + 3 correct le 1970-01-01.

_ {Pour ceux qui se heurtent à cela en 2017} _ dateutil.tz.tzlocal() est toujours cassé. L'exemple ci-dessus fonctionne maintenant car l'offset utf actuel est UTC + 3 à Moscou (accidentellement égal à l'offset utc de 1970). Pour démontrer l'erreur, nous pouvons choisir une date à laquelle utc offset est UTC + 4:

>>> import os, time
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> time.tzset()
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from tzlocal import get_localzone
>>> dateutil_tz = tzlocal()
>>> tzlocal_tz = get_localzone()
>>> ts = datetime(2014, 6,1).timestamp() # get date in 2014 when gmtoff=14400 in Moscow
>>> datetime.fromtimestamp(ts, dateutil_tz)
datetime.datetime(2014, 5, 31, 23, 0, tzinfo=tzlocal())
>>> datetime.fromtimestamp(ts, tzlocal_tz)
datetime.datetime(2014, 6, 1, 0, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+4:00:00 STD>)

dateutil renvoie faux décalage UTC + 3 au lieu du correct UTC + 4 le 2014-06-01.

59
jfs

Utilisez la fonction tzlocal du paquet python-dateutil :

from dateutil.tz import tzlocal

localtimezone = tzlocal()

En interne, il s'agit d'une classe qui utilise time.timezone et time.altzone (commutation basée sur time.daylight), mais crée un objet fuseau horaire approprié à partir de cela.

Vous utilisez ce à la place d'un fuseau horaire pytz.

L'alternative consiste à lire le fuseau horaire actuellement configuré à partir du système d'exploitation, mais cela diffère grandement d'un système d'exploitation à l'autre. Sous Mac OS X, vous devez lire le résultat de systemsetup -gettimezone:

$ systemsetup -gettimezone
Time Zone: Europe/Copenhagen

Sur les systèmes Debian et Ubuntu, vous pouvez lire /etc/timezone:

$ cat /etc/timezone
Europe/Oslo

Sur les systèmes RedHat et direved, vous devrez le lire à partir de /etc/sysconfig/clock:

$ grep ZONE /etc/sysconfig/clock
ZONE="Europe/Oslo"
31
Martijn Pieters

Depuis Python 3.6, vous pouvez simplement exécuter naive_datetime.astimezone() et le fuseau horaire du système sera ajouté à l’objet naive_datetime.

Si appelé sans arguments (ou avec tz = None), le fuseau horaire local du système est supposé pour le fuseau horaire cible. L'attribut .tzinfo de l'instance datetime convertie sera défini sur une instance de fuseau horaire avec le nom de la zone et le décalage obtenus à partir du système d'exploitation.

https://docs.python.org/3/library/datetime.html#datetime.datetime.astimezone

Exemple:

>>> import datetime
>>> datetime.datetime.now().astimezone().isoformat(timespec='minutes')
'2018-10-02T13:09+03:00'
3
sirex

je ne sais pas si cela vous est utile ou non, mais je pense que cela répond à votre problème plus général:

si vous avez une date qui se trouve dans un fuseau horaire ambigu, comme CST, simple-date (désolé python 3.2+ seulement), vous pouvez automatiser la recherche et vous permettre de faire des choses comme préférer certains pays.

par exemple:

>>> SimpleDate('2013-07-04 18:53 CST')
Traceback [...
simpledate.AmbiguousTimezone: 3 distinct timezones found: <DstTzInfo 'Australia/Broken_Hill' CST+9:30:00 STD>; <DstTzInfo 'America/Regina' LMT-1 day, 17:01:00 STD>; <DstTzInfo 'Asia/Harbin' LMT+8:27:00 STD> (timezones=('CST',), datetime=datetime.datetime(2013, 7, 4, 18, 53), is_dst=False, country=None, unsafe=False)
>>> SimpleDate('2013-07-04 18:53 CST', country='CN')
SimpleDate('2013-07-04 18:53 CST')
>>> SimpleDate('2013-07-04 18:53 CST', country='CN').utc
SimpleDate('2013-07-04 10:53 UTC', tz='UTC')

remarquez comment, en spécifiant un pays, vous réduisez suffisamment la plage de valeurs possibles pour permettre la conversion en UTC.

il est implémenté en faisant une recherche sur les fuseaux horaires dans PyTZ:

>>> SimpleDate('2013-07-04 18:53 CST', country='CN', debug=True)
...
PyTzFactory: Have country code CN
PyTzFactory: Country code CN has 5 timezones
PyTzFactory: Expanded country codes to 5 timezones
PyTzFactory: Expanding ('CST',)
PyTzFactory: Name lookup failed for CST
PyTzFactory: Found CST using Asia/Shanghai
PyTzFactory: Found CST using Asia/Harbin
PyTzFactory: Found CST using Asia/Chongqing
PyTzFactory: Found CST using Asia/Urumqi
PyTzFactory: Found CST using Asia/Kashgar
PyTzFactory: Expanded timezone to 5 timezones
PyTzFactory: New offset 8:00:00 for Asia/Shanghai
PyTzFactory: Known offset 8:00:00 for Asia/Harbin
PyTzFactory: Known offset 8:00:00 for Asia/Chongqing
PyTzFactory: Known offset 8:00:00 for Asia/Urumqi
PyTzFactory: Known offset 8:00:00 for Asia/Kashgar
PyTzFactory: Have 1 distinct timezone(s)
PyTzFactory: Found Asia/Shanghai
...
SimpleDate('2013-07-04 18:53 CST')

enfin, pour répondre directement à la question posée, tzlocal est également enveloppé, comme indiqué dans une autre réponse, et fera automatiquement ce que vous attendez si vous ne définissez pas de fuseau horaire. par exemple, je vis au Chili, donc

>>> SimpleDate()
SimpleDate('2013-07-04 19:21:25.757222 CLT', tz='America/Santiago')
>>> SimpleDate().tzinfo
<DstTzInfo 'America/Santiago' CLT-1 day, 20:00:00 STD>

donne le fuseau horaire de mes paramètres régionaux (ambigu ou non).

1
andrew cooke