如何得到给定时区的协调世界时“午夜”时间?

我现在能想到的最好的办法就是这个怪物:

>>> datetime.utcnow() \
...   .replace(tzinfo=pytz.UTC) \
...   .astimezone(pytz.timezone("Australia/Melbourne")) \
...   .replace(hour=0,minute=0,second=0,microsecond=0) \
...   .astimezone(pytz.UTC) \
...   .replace(tzinfo=None)
datetime.datetime(2008, 12, 16, 13, 0)

例如,在英语中,获取当前时间(以 UTC 为单位) ,将其转换为其他时区,将时间设置为午夜,然后再转换回 UTC。

我不仅仅使用 now ()或 localtime () ,因为它将使用服务器的时区,而不是用户的时区。

我总觉得我漏掉了什么,有什么想法吗?

89507 次浏览

设置 TZ 环境变量可以修改 Python 的时区日期和时间函数的工作方式。

>>> time.gmtime()
(2008, 12, 17, 1, 16, 46, 2, 352, 0)
>>> time.localtime()
(2008, 12, 16, 20, 16, 47, 1, 351, 0)
>>> os.environ['TZ']='Australia/Melbourne'
>>> time.localtime()
(2008, 12, 17, 12, 16, 53, 2, 352, 1)

每个时区都有一个数字,例如 US/Central = -6。这被定义为以小时为单位的距离 UTC 的偏移量。由于0000是午夜,您可以简单地使用这个偏移量来查找任何时区的时间,当它是午夜协调世界时。要访问它,我相信你可以使用

时间,时区

根据 巨蟒文档,time.timezone 实际上给出了这个数的负值:

时间,时区

当地(非 DST)时区的偏移量,以协调世界时以西几秒为单位(西欧大部分地区为负,美国为正,英国为零)。

因此,如果时间是正数,你可以简单地用这个数字表示以小时为单位的时间(也就是说,如果现在是芝加哥的午夜(时区值为 + 6) ,那么它就是6000 = 协调世界时上午6点)。

如果数字是负数,从24减去。例如,柏林会给出 -1,所以24-1 = > 2300 = 11 pm。

我认为你可以减少一些方法调用,如果你这样做:

>>> from datetime import datetime
>>> datetime.now(pytz.timezone("Australia/Melbourne")) \
.replace(hour=0, minute=0, second=0, microsecond=0) \
.astimezone(pytz.utc)

但是... ... 在你的代码中有一个比美学更大的问题: 它会在切换到或切换到夏时制的那一天给出错误的结果。

这样做的原因是,datetime 构造函数和 replace()都没有考虑 DST 更改。

例如:

>>> now = datetime(2012, 4, 1, 5, 0, 0, 0, tzinfo=pytz.timezone("Australia/Melbourne"))
>>> print now
2012-04-01 05:00:00+10:00
>>> print now.replace(hour=0)
2012-04-01 00:00:00+10:00 # wrong! midnight was at 2012-04-01 00:00:00+11:00
>>> print datetime(2012, 3, 1, 0, 0, 0, 0, tzinfo=tz)
2012-03-01 00:00:00+10:00 # wrong again!

然而,tz.localize()的文件规定:

此方法应该用于构造本地时间,而不是 而不是将 tzinfo 参数传递给 datetime 构造函数。

因此,你的问题是这样解决的:

>>> import pytz
>>> from datetime import datetime, date, time


>>> tz = pytz.timezone("Australia/Melbourne")
>>> the_date = date(2012, 4, 1) # use date.today() here


>>> midnight_without_tzinfo = datetime.combine(the_date, time())
>>> print midnight_without_tzinfo
2012-04-01 00:00:00


>>> midnight_with_tzinfo = tz.localize(midnight_without_tzinfo)
>>> print midnight_with_tzinfo
2012-04-01 00:00:00+11:00


>>> print midnight_with_tzinfo.astimezone(pytz.utc)
2012-03-31 13:00:00+00:00

不过1582年以前的日期不能保证。

@ hop 的回答 在从夏时制过渡的那一天是错误的,例如,2012年4月1日。为了修复它,可以使用 tz.localize():

tz = pytz.timezone("Australia/Melbourne")
today = datetime.now(tz).date()
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)
utc_dt = midnight.astimezone(pytz.utc)

评论也是如此:

#!/usr/bin/env python
from datetime import datetime, time
import pytz # pip instal pytz


tz = pytz.timezone("Australia/Melbourne") # choose timezone


# 1. get correct date for the midnight using given timezone.
today = datetime.now(tz).date()


# 2. get midnight in the correct timezone (taking into account DST)
#NOTE: tzinfo=None and tz.localize()
# assert that there is no dst transition at midnight (`is_dst=None`)
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)


# 3. convert to UTC (no need to call `utc.normalize()` due to UTC has no
#    DST transitions)
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
print midnight.astimezone(pytz.utc).strftime(fmt)

与 pytz 相比,dateutil.tz 更直接:

>>>import datetime
>>>import dateutil.tz
>>>midnight=(datetime.datetime
.now(dateutil.tz.gettz('Australia/Melbourne'))
.replace(hour=0, minute=0, second=0, microsecond=0)
.astimezone(dateutil.tz.tzutc()))
>>>print(midnight)
2019-04-26 14:00:00+00:00

从 Python 3.6开始,Tzinfo 文档就推荐 dateutil.tz。来自 dateutil.tz 的 tzinfo 对象没有像 DST 这样的异常问题,而不需要 pytz 的本地化功能。使用 user3850的例子:

>>> now = (datetime.datetime(2012, 4, 1, 5,
...         tzinfo = dateutil.tz.gettz('Australia/Melbourne')))
>>> print(now.replace(hour = 0).astimezone(dateutil.tz.tzutc()))
2012-03-31 13:00:00+00:00

值得注意的是,我们可以调整@jfs 给出的答案,找到明天的午夜或昨天的午夜,等等。诀窍在于为感知的时区增加一定数量的天数。这是因为虽然这通常会增加24小时,但有时可能会基于 DST 问题增加23或25小时。

from datetime import datetime, time, timedelta
import pytz


def midnight_UTC(offset):


# Construct a timezone object
tz = pytz.timezone('Australia/Melbourne')


# Work out today/now as a timezone-aware datetime
today = datetime.now(tz)


# Adjust by the offset. Note that that adding 1 day might actually move us 23 or 25
# hours into the future, depending on daylight savings. This works because the {today}
# variable is timezone aware
target_day = today + timedelta(days=1) * offset


# Discard hours, minutes, seconds and microseconds
midnight_aware = tz.localize(
datetime.combine(target_day, time(0, 0, 0, 0)), is_dst=None)


# Convert to UTC
midnight_UTC = midnight_aware.astimezone(pytz.utc)


return midnight_UTC


print("The UTC time of the previous midnight is:", midnight_UTC(0))
print("The UTC time of the upcoming midnight is:", midnight_UTC(1))