Python:找出本地时区


72

我想将日志文件中的UTC时间戳与本地时间戳进行比较。创建本地datetime对象时,我使用类似以下内容的方法:

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

我想找到一个自动工具,将其替换tzinfo=pytz.timezone('Israel')为当前本地时区。

有任何想法吗?


1
首先,您的问题值得怀疑。根据您的操作,很有可能会产生接近夏令时转换(或当地时区更改的任何其他情况)的竞争条件。
凯文

如果以本地时间表示您的操作系统设置,则可以简单地将其作为tz知道的日期时间对象来获取local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0).astimezone()(如Python> = 3.6)。[[文档]](docs.python.org/3/library/...
MrFuppes

Answers:


40

尝试使用dateutil,它具有tzlocal类型,可以满足您的需求。


23
这不是一个非常标准的软件包...还有更多规范的解决方案吗?
gatoatigrado 2012年

2
dateutil在某些过去日期的时区中失败。对于确实可行的情况,您可以使用纯stdlib解决方案
jfs


4
from dateutil.tz import tzlocal
安迪·海登

2
@Jthorpedateutil并不确定,并且正在积极开发中。
tacaswell '18

105

在Python 3.x中,本地时区可以像这样计算出来:

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

这是一个棘手的使用datetime代码

对于python> = 3.6,您需要

import datetime
LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone(datetime.timedelta(0))).astimezone().tzinfo

20
根本datetime.datetime.now().astimezone().tzinfo应该已经可以了。
波兰'18

3
@Polvdatetime.datetime.utcnow().astimezone().tzinfo似乎也是正确的,对我来说更容易阅读,因为它明确指出它将以UTC开头。由于datetime最初没有绑定到任何时区,因此在内部不应该有所作为。
sjngm

8
@Polv @sjngm不完全是因为它仅适用于Python> = 3.6。您不能astimezone()在3.6之前的原始日期时间调用。
Fynn Becker

1
聪明,但是如果您的程序可以长期运行(例如,DST更改),它将起作用吗?
威尔默

这行代码在我的python终端(在Docker容器中?)上返回UTC,但是我在GMT + 4中
Jose Georges

31

将日志文件中的UTC时间戳与本地时间戳进行比较。

这是很难找出奥尔森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偏移可以不同。一些支持时区感知的日期时间对象(例如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()

timestamp1timestamp2可以直接进行比较。

注意:

  • timestamp1仅当epoch(datetime.fromtimestamp(0))处的UTC偏移量与现在相同时,该公式才起作用
  • fromtimestamp() 在当前本地时区创建一个朴素的datetime对象
  • utcfromtimestamp() 在UTC中创建一个简单的datetime对象。

正是我所需要的!
nagylzs 17-10-22

我不明白为什么获取本地时区不包含在Python核心模块之一中……
Rfraile

@Rfraile:如果我们排除了需要pytz时区的极端情况,那么Python永远都会在stdlib中使用本地时区。2- PEP 615- Python 3.9接受了对标准库中IANA时区数据库的支持(它提供与tzdata相同的访问权限pytz
jfs

@jfs我想说支持Olson时区,对不起。很
高兴

19

我对自己也问过同样的问题,我在1中找到了答案:

看一下8.1.7节:格式“%z”(小写,Z大写也返回时区,但不是4位格式,而是时区缩写形式,如[3]中所示) strftime的返回形式为电子邮件标头中的标准形式“ +/- 4DIGIT”(请参阅​​RFC 2822的3.3节,请参见[2],该方法淘汰了为电子邮件标头指定时区的其他方式)。

因此,如果您希望时区采用这种格式,请使用:

time.strftime("%z")

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

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

[3]时区缩写:http : //en.wikipedia.org/wiki/List_of_time_zone_abbreviations,仅供参考。


2
问题是要找到tzinfo与本地时区相对应的对象,而不是作为字符串的当前utc偏移量。time.timezone.altzone为您提供当前的utc偏移量。时区偏移量或缩写不明确。要获得可用于过去,现在和不久的将来的日期的本地时区并非易事。查看tzlocal模块的源代码,以查看如何完成此示例。
jfs 2013年

简单而有效
穆罕默德Banisaeid

6

首先获取pytz和tzlocal模块

pip install pytz tzlocal

然后

from tzlocal import get_localzone
local = get_localzone()

然后你可以做类似的事情

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

5

这是一种仅使用标准库获取本地时区的方法(仅在* 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>)

为了避免发明方形车轮,请查看tzlocal源代码
jfs

这破坏了使用TZ变量的环境时区覆盖。
卡尔斯滕

MacOS上是否存在“ / etc / localtime”?
卢卡斯

还是在Windows上?;-)
–quant_dev

5

使用标准库,以下内容似乎适用于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])

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)

2
如果您过去不关心DST或utc偏移量(如您的解决方案所示);您可以使用-time.timezone
jfs

4

根据JF Sebastian的回答,您可以使用标准库执行此操作:

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

在3.4中测试,应在3.4+上运行


我认为在实行夏令时时,这可能会产生错误的结果。
普雷斯顿·兰德斯

3

这是@vbem解决方案的更简洁的版本:

from datetime import datetime as dt

dt.utcnow().astimezone().tzinfo

唯一的实质区别是,我datetime.datetime.now(datetime.timezone.utc)datetime.datetime.utcnow()。为了简洁起见,我还别名datetime.datetimedt

出于我的目的,我希望以秒为单位的UTC偏移量。看起来像这样:

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

1
ValueError:astimezone()无法应用于原始日期时间(python 3.4)
pbarill

1
是的,在我使用该解决方案发布了一些代码之后,使用linux的用户也报告了该错误。如果我没记错的话,根本原因是操作系统时间未配置为时区(即“天真的” UTC),这会破坏代码。除了将代码包装在try / except块中之外,没有其他解决方案。
大卫·马克思

好,谢谢。从那以后,我放弃了纯stdlib解决方案的想法,而选择了pytzand tzlocal。当python已经附带了很多东西时,我很谨慎地添加第三方依赖,但是当处理时区时,显然您需要外部帮助。
pbarill

2

根据上述Thoku的答案,以下是将时区解析为最近的半小时的答案(这与某些时区有关,例如南澳大利亚州):

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

Python 3版本:round((round((datetime.datetime.now()-datetime.datetime.utcnow()).total_seconds())/1800)/2)
Dmitry

(1)如果本地utc偏移过去不同(在许多时round()区),这是错误的。(2)在这里(某些时区)可能会产生错误的utc偏移,这是不必要的。要了解如何获取精确的utc偏移量,请参见使用Python获取计算机的UTC偏移量
jfs 2015年

2

我也一直在寻找一种简单的方法来读取本地主机配置,并基于它获取时区感知的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

我在研究此主题时使用的其他资源:Paul Ganssle有关时区的演示: https //www.youtube.com/watch?v=

pytz:西方最快的步枪 https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html


1

为了简单起见,tzinfo可以使用以下实现,该实现向OS查询时区偏移量:

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())

(1)如果它不能工作time模块具有一给定平台上没有访问时区信息数据库(注意:pytz在质询提供了一种在便携式方式这样的访问)(2)t式是不正确如果UTC偏差对应于dt-time.timezone因此,即使time模块知道正确的信息localtime(t),您的代码也会要求输入错误的值(请考虑DST转换周围的时间)。
jfs

1
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)

希望这会帮助你。



1

我想将日志文件中的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

datetime.strftime()的'%Z'参数将时区首字母缩写打印到时间戳中,以供人类阅读。


0

首先,请注意,该问题显示了已知的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'

(1)不要将pytz时区datetime()直接传递给构造函数-如果您知道不应该这样做;为什么呢?(2)如果已pytz安装;为什么astimezone()在给定平台上使用可能无法访问tz数据库的非便携式产品?您可以获得纯Pythontzlocal模块,该模块可以pytz在Windows和Unix(/etc/localtime/usr/local/etc/localtime)上为本地时区找到tzinfo对象。
jfs

(1)因为这是OP提出他的问题的方式;(2)astimezone()具有尽可能的可移植性,可以在无需安装其他软件包的情况下使用。
亚历山大·贝洛波尔斯基

(1)问题中可能包含无效的代码;答案不应该(没有充分的理由,例如在代码块本身中有明确的指示)。(2)该代码已使用pytz。正确答案应使用tz数据库。
jfs 2015年

tzlocal不是pytz的一部分。如果它在用户系统上可用-使用它是一个很好的解决方案。如果不是,则astimezone()是Python 3.3及更高版本的推荐解决方案。“正确”的解决方案是否应使用tz数据库取决于用户需求。希望对于大多数用户而言,结果将是相同的。请注意,虽然不建议将pytz时区与dateutil混合使用,但使用标准库datetime.timezone的datetime实例可与使用pytz的实例完全互操作。
亚历山大·贝洛波尔斯基

(1)该问题可能包含无效的代码;答案不应该... 这是一个很好的建议。我改写了我的答案。谢谢。
亚历山大·贝洛波尔斯基

-1

dateutil中tzlocal

代码示例如下。适用于文件名的最后一个字符串。

>>> 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'
>>> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.