Python: выяснить местный часовой пояс

Я хочу сравнить метки времени UTC из файла журнала с местными метками времени. При создании местного datetime объект, я использую что-то вроде:

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

Я хочу найти автоматический инструмент, который заменитtzinfo=pytz.timezone('Israel') с текущим местным часовым поясом.

Есть идеи?

19 ответов

Решение

Попробуйте dateutil, который имеет тип tzlocal, который делает то, что вам нужно.

В Python 3.x местный часовой пояс может быть определен так:

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

Это хитрое использование datetime код.

сравнить временные метки UTC из файла журнала с локальными временными метками.

Трудно найти название Olson TZ для местного часового пояса в портативном виде. К счастью, он не нужен для сравнения.

tzlocal модуль возвращает часовой пояс pytz, соответствующий местному часовому поясу:

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

В отличие от других решений, представленных до сих пор, приведенный выше код позволяет избежать следующих проблем:

  • местное время может быть неоднозначным, т. е. точное сравнение может быть невозможно для некоторых локальных времен
  • Смещение utc может отличаться для одного и того же имени местного часового пояса для дат в прошлом. Некоторые библиотеки, которые поддерживают объекты datetime с учетом часового пояса (например, dateutilне принять это во внимание

Примечание: чтобы получить объект datetime с учетом часового пояса из наивного объекта datetime, вы должны использовать*:

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

вместо:

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

*is_dst=None вызывает исключение, если заданное местное время неоднозначно или отсутствует.

Если вы уверены, что все локальные временные метки используют одинаковое (текущее) смещение utc для локального часового пояса, тогда вы можете выполнить сравнение, используя только 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 а также timestamp2 можно сравнить напрямую.

Замечания:

  • timestamp1 формула работает только если смещение UTC в эпоху (datetime.fromtimestamp(0)) такой же как сейчас
  • fromtimestamp() создает наивный объект datetime в текущем местном часовом поясе
  • utcfromtimestamp() создает наивный объект datetime в UTC.

Я спрашивал про себя то же самое и нашел ответ в 1:

Взгляните на раздел 8.1.7: формат "%z" (строчные буквы, прописные буквы Z возвращают также часовой пояс, но не в 4-значном формате, а в виде сокращений часовых поясов, как в [3]) of strftime возвращает форму "+/- 4DIGIT", стандартную для заголовков электронной почты (см. раздел 3.3 RFC 2822, см. [2], в котором устарели другие способы указания часового пояса для заголовков электронной почты).

Итак, если вы хотите, чтобы ваш часовой пояс в этом формате, используйте:

time.strftime("%z")

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

[2] http://tools.ietf.org/html/rfc2822

[3] Сокращения часовых поясов: http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations, только для справки.

Следующее работает для версии 3.7+, используя стандартные библиотеки:

from datetime import timedelta
from datetime import timezone
import time

def currenttz():
    if time.daylight:
        return timezone(timedelta(seconds=-time.altzone),time.tzname[1])
    else:
        return timezone(timedelta(seconds=-time.timezone),time.tzname[0])

Сначала получите pytz и tzlocal модули

pip install pytz tzlocal

затем

from tzlocal import get_localzone
local = get_localzone()

тогда вы можете делать такие вещи, как

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

Чтобы создать строку в формате ISO, которая включает представление ISO вашего местного часового пояса в Израиле ( +04:00):

на сервере в Израиле:

      >>> datetime.now(datetime.now().astimezone().tzinfo).isoformat()
'2021-09-07T01:02.030042+04:00'

Это создаст объект даты с учетом часового пояса, который будет соответствующим образом сравниваться с любым другим объектом даты и времени в формате UTC или по местному времени. Но ISO-представление часового пояса (и сама строка даты/времени) изменится, если вы запустите это на сервере в Сан-Франциско в то же время, что и я:

на сервере в Сан-Франциско, Калифорния, США (Тихий океан):

      >>> datetime.now(datetime.now().astimezone().tzinfo).isoformat()
'2021-09-06T14:01:02.030042-07:00'

The datetimeобъекты в обоих случаях будут совместимы друг с другом. Итак, если вы вычтете их, вы получите дельту времени, равную 0:

На сервере где угодно в Python3.6+:

      >>> (datetime.fromisoformat('2021-09-06T14:01:02.030042-07:00') -
...  datetime.fromisoformat('2021-09-07T01:01:02.030042+04:00'))
datetime.timedelta(0)

Вот несколько более краткая версия решения @vbem:

from datetime import datetime as dt

dt.utcnow().astimezone().tzinfo

Единственное существенное отличие состоит в том, что я заменил datetime.datetime.now(datetime.timezone.utc) с datetime.datetime.utcnow(), Для краткости я тоже псевдоним datetime.datetime как dt,

Для моих целей я хочу смещение UTC в секундах. Вот как это выглядит:

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

Вот способ получить местный часовой пояс, используя только стандартную библиотеку (работает только в среде *nix):

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

Вы можете использовать это для создания pytz часовой пояс:

>>> import pytz
>>> my_tz_name = '/'.join(os.path.realpath('/etc/localtime').split('/')[-2:])
>>> my_tz = pytz.timezone(my_tz_name)
>>> my_tz
<DstTzInfo 'Australia/Sydney' LMT+10:05:00 STD>

... который вы можете затем применить к 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=<DstTzInfo 'Australia/Sydney' LMT+10:05:00 STD>)

Я также искал простой способ прочитать конфигурацию локального хоста и получить информацию о часовом поясе local_time на ее основе. Что касается python 3.6+, самый простой подход - использовать dateutil.tz, который будет читать/etc/localtime и помочь в получении объекта datetime с учетом часового пояса.

Подробнее об этом: https://dateutil.readthedocs.io/en/stable/tz.html.

Реализация для достижения того, что вы ищете, выглядит следующим образом:

from datetime import datetime
from dateutil import tz
local_time = datetime.now(tz.gettz())

Это предоставит вам следующее local_time:

2019-10-18 13:41:06.624536-05:00

Дополнительные ресурсы, которые я использовал при исследовании этой темы: Презентация Пола Ганссла о часовых поясах: https://www.youtube.com/watch?v=l4UCKCo9FWY

pytz: Самый быстрый Footgun на Западе https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html

Основываясь на ответе Дж. Ф. Себастьяна, вы можете сделать это с помощью стандартной библиотеки:

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

Протестировано в 3.4, должно работать на 3.4+

Как избежать нестандартного модуля (кажется, отсутствует метод модуля 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 %i h to UTC.' % (utcOffset_h)

Вы можете быть счастливы с маятником

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

Маятник имеет хорошо разработанный API для манипулирования датами. Все в курсе ТЗ.

Основываясь на ответе Thoku выше, вот ответ, который разрешает часовой пояс с точностью до получаса (что актуально для некоторых часовых поясов, например, Южной Австралии):

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

Я хочу сравнить временные метки UTC из файла журнала с локальными временными метками

Если это ваше намерение, я бы не стал беспокоиться об указании конкретных параметров tzinfo или каких-либо дополнительных внешних библиотек. Начиная с Python 3.5, встроенный модуль datetime - это все, что вам нужно для автоматического создания UTC и локальной временной метки.

import datetime
f = "%a %b %d %H:%M:%S %Z %Y"         # Full format with timezone

# tzinfo=None
cdatetime = datetime.datetime(2010, 4, 27, 12, 0, 0, 0)  # 1. Your example from log
cdatetime = datetime.datetime.now()   # 2. Basic date creation (default: local time)
print(cdatetime.strftime(f))          # no timezone printed
# Tue Apr 27 12:00:00  2010

utctimestamp = cdatetime.astimezone(tz=datetime.timezone.utc)  # 1. convert to UTC
utctimestamp = datetime.datetime.now(tz=datetime.timezone.utc) # 2. create in UTC
print(utctimestamp.strftime(f))
# Tue Apr 27 17:00:00 UTC 2010

localtimestamp = cdatetime.astimezone()               # 1. convert to local [default]
localtimestamp = datetime.datetime.now().astimezone()  # 2. create with local timezone
print(localtimestamp.strftime(f))
# Tue Apr 27 12:00:00 CDT 2010

Параметр "%Z" функции datetime.strftime() печатает аббревиатуру часового пояса в метке времени, которую люди могут прочитать.

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)

Надеюсь, что это поможет вам.

Цлокал от датыутил.

Пример кода приведен ниже. Последняя строка подходит для использования в именах файлов.

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

Во-первых, обратите внимание, что вопрос представляет неправильную инициализацию осведомленного объекта datetime:

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

создает недействительный экземпляр. Можно увидеть проблему, рассчитав смещение UTC полученного объекта:

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

(Обратите внимание на результат, который составляет нечетную долю часа.)

Чтобы правильно инициализировать время и дату, используя pytz, следует использовать localize() метод следующим образом:

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

Теперь, если вам требуется локальный часовой пояс pytz в качестве новой tzinfo, вы должны использовать пакет tzlocal, как объяснили другие, но если все, что вам нужно, это экземпляр с правильным смещением и сокращением локального часового пояса, а затем перейти к Python 3.3, вы можете позвонить astimezone() метод без аргументов для преобразования в курсе datetime Например, ваш местный часовой пояс:

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

Для простых вещей, следующее tzinfo Можно использовать реализацию, которая запрашивает у ОС смещение часового пояса:

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())
Другие вопросы по тегам