盘一盘 Python 特别篇 18

您所在的位置:网站首页 美国夏令时和冬令时 盘一盘 Python 特别篇 18

盘一盘 Python 特别篇 18

2024-06-28 02:15| 来源: 网络整理| 查看: 265

1

时区

时间差

在每个地区,中午 12 点都对应着正午,但是每个地区的 12 点是统一时刻吗?显然不是,要不然也不会有时差概念了。下图最右边的图显示着火车穿过两个时区,那么记录的时间应该是处在时区的那个时间,因此区分时区很重要。

世界上不同地区显示的时间不同,北京时间就比美国东部时间快 13 个小时,看下图:

UTC

为了方便比较不同时区的时间,我们用协调世界时间当作基准。

协调世界时间 (Coordinated Universal Time, UTC) 是最主要的世界时间标准,在在时刻上尽量接近于格林威治标准时间 (Greenwich Mean Time, GMT)。UTC 可以视为一个世界统一的时间,其他时区的时间都是在这个基础上增加或减少的,比如

北京和新加坡的时间比 UTC 快 8 小時,可记做 UTC + 8美国东部时区时间比 UTC 慢 5 个小时,可记做 UTC - 5

这样看北京时间比美东时间快 13 个小时,因为 UTC + 8 - (UTC - 5) = 13。有了标准 UTC,时间就可以比较了。

当用 datetime() 对象创建时间式,如果不设定时区,那么这个时间被称为不考虑时区 (UTC-naive) 的日期时间;如果设定时区,那么这个时间被称为考虑时区 (UTC-aware) 的日期时间。

代码语言:javascript复制from datetime import datetime, timedelta, timezone

创建一个不考虑时区的日期时间,如果你处理的问题不需要考虑多个时区,那么这个时间可看做是你处理问题所在地区的时间;如果你处理的问题需要考虑多个时区,那么这个时间可看做是 UTC。

代码语言:javascript复制dt = datetime(2020, 6, 27, 21, 30) print(dt)代码语言:javascript复制2020-06-27 21:30:00

已知美国东部时区时间比 UTC 慢 5 个小时,因此可用 timedelta() 对象定义一个负 5 个小时的时间差,并传入 timezone() 对象中定义美东时区 ET。

代码语言:javascript复制ET = timezone(timedelta(hours=-5)) dt = datetime(2020, 6, 27, 21, 30, tzinfo=ET) print(dt)代码语言:javascript复制2020-06-27 21:30:00-05:00

打印出来的日期时间都是当地时间 (此时是美东时间),而最后有 -05:00 的字样,它叫做 UTC offset,负号代表比 UTC 慢 5 个小时。

已知北京时间比 UTC 快 8 个小时,因此可用 timedelta() 对象定义一个正 8 个小时的时间差,并传入 timezone() 对象中定义北京时区 BJ。

代码语言:javascript复制BJ = timezone(timedelta(hours=8)) dt = datetime(2020, 6, 27, 21, 30, tzinfo=BJ) print(dt)代码语言:javascript复制2020-06-27 21:30:00+08:00

astimezone()

打印出来的都是当地时间 (此时是北京时间),而最后有 +08:00 的字样,它叫做 UTC offset,正号代表比 UTC 快 8 个小时。

现在定义一个美东时间 dt 为 2020-06-27 早上 9 点 30 分,用 astimezone() 对象显示出对应的北京时间是多少,结果是 2020-06-27 晚上 22 点 30 分。但两者的绝对差异是零。这个现实被称作相同时刻,不同时间 (same moment, different time)。

代码语言:javascript复制dt = datetime(2020, 6, 27, 9, 30, tzinfo=ET) print(f'美东时间:{dt}') print(f'北京时间:{dt.astimezone(BJ)}') print(dt.astimezone(BJ)-dt)代码语言:javascript复制美东时间:2020-06-27 09:30:00-05:00 北京时间:2020-06-27 22:30:00+08:00 0:00:00

同理,定义一个北京时间 dt 为 2020-06-27 晚上 22 点 30 分,用 astimezone() 对象显示出对应的美东时间是多少,结果是 2020-06-27 早上 9 点 30 分。两者的绝对差异是零。相同时刻,不同时间。

代码语言:javascript复制dt = datetime(2020, 6, 27, 22, 30, tzinfo=BJ) print(f'北京时间:{dt}') print(f'美东时间:{dt.astimezone(ET)}') print(dt.astimezone(ET)-dt)代码语言:javascript复制北京时间:2020-06-27 22:30:00+08:00 美东时间:2020-06-27 09:30:00-05:00 0:00:00

replace()

dt.replace(some_tz) 函数返回一个具有同样值的日期,但是在不同时区,即 dt 的时区和 some_tz 时区不同,这个叫做相同时间,不同时刻 (same time, different moment)。

代码语言:javascript复制dt = datetime(2020, 6, 27, 9, 30, tzinfo=ET) dt_to_utc = dt.replace(tzinfo=timezone.utc) dt_as_utc = dt.astimezone(timezone.utc) print(dt) print(dt_to_utc) print(dt_as_utc) print(dt - dt_to_utc) print(dt - dt_as_utc)代码语言:javascript复制2020-06-27 09:30:00-04:00 2020-06-27 09:30:00+00:00 2020-06-27 13:30:00+00:00 4:00:00 0:00:00

从上面结果可看出两点:

dt_to_utc 和 dt 是相同时间 (都是 2020-06-27 09:30:00),不同时刻 (从它俩的 UTC offset 或者它俩之差 4:00:00 看出来)dt_as_utc 和 dt 是不同时间 (前者是 14:30:00 后者是 09:30:00),相同时刻 (从它俩之差是 0:00:00 看出来)

dateutil.tz

在实际操作做很难记住每个时区的时间和 UTC 差多少,幸运的是 dateutil 包里的 tz 对象可以帮我们解决这个难题。只需用 '区域/城市' 来设定时区就可以了,比如

美东时间用 'America/New_York' 来设定北京时间用 'China/Bei_Jing' 来设定

tz 是 timezone 的缩写,可看成是时区的数据库,首先从 dateutil 引入它,然后用 gettz() 函数加上设定的字符串时区来获取时区对象。

代码语言:javascript复制from dateutil import tz ET = tz.gettz('America/New_York') BJ = tz.gettz('China/Bei_Jing')

以下结果复制了上面结果,只不过现在用 tz.gettz() 加字符串来设置时区,而上面用 timezone(timedelta()) 加时间差来设置时区。对普通人来说,记住形象的字符串比记住枯燥的时差容易多了吧。

代码语言:javascript复制dt = datetime(2020, 6, 27, 9, 30, tzinfo=ET) dt_to_bj = dt.replace(tzinfo=BJ) dt_as_bj = dt.astimezone(BJ) print(dt) print(dt_to_bj) print(dt_as_bj) print(dt - dt_to_bj) print(dt - dt_as_bj)代码语言:javascript复制2020-06-27 09:30:00-04:00 2020-06-27 09:30:00+08:00 2020-06-27 21:30:00+08:00 12:00:00 0:00:00

提示:注意力放在 dt - dt_to_bj 的结果 12:00:00 上,美东时间比北京时间慢了 12 个小时。

现在突发奇想换个日期 2020-01-11 看看有什么变化?

代码语言:javascript复制dt = datetime(2020, 1, 11, 9, 30, tzinfo=ET) dt_to_bj = dt.replace(tzinfo=BJ) dt_as_bj = dt.astimezone(BJ) print(dt) print(dt_to_bj) print(dt_as_bj) print(dt - dt_to_bj) print(dt - dt_as_bj)代码语言:javascript复制2020-01-11 09:30:00-05:00 2020-01-11 09:30:00+08:00 2020-01-11 22:30:00+08:00 13:00:00 0:00:00

这时 dt - dt_to_bj 的结果是 13:00:00 上,表示美东时间比北京时间慢了 13 个小时 (之前只慢了 12 个小时)。此外更明显的是,

当日期为 2020-06-27,北京时间是 2020-06-27 21:30:00+08:00当日期为 2020-01-11,北京时间是 2020-01-11 22:30:00+08:00

为什么不同日期上同样的美东时间对应的北京时间会不同呢?时间差还能随日期变?你说对了,夏令时了解一下。

2

夏令时

夏令时 (daylight saving time, DST) 则是为了充分利用夏天日照长的特点,充分利用光照节约能源而人为调整时间的一种机制。通过在夏天将时间向前加一小时,使人们早睡早起节约能源。虽然很多西方国家都采用了DST,但是中国不采用 DST。

夏令时 (daylight saving time) 是由“创始人”本杰明·富兰克林提出,他在 1784 年发表《节约日光成本的经济工程》, 原因是注意到人们 10 点后才起床,夜生活过到深夜。所以建议大家早睡早起,每年可节省不少蜡烛。但他没能目睹这天到来。在第一次世界大战期间,美国首次实施日光节约,以此来节省燃料。1966年,约翰逊总统签署了统一时间法案,才使其成为法律。

美股开盘时间在中国的晚上,因为美国有夏令时间 , 因此夏天的交易时间与冬天相比会提前一小时:

在冬天交易时间为美国东部时间 9:30 到 16:00,对应着北京时间 22:30 到次日 5:00在夏天交易时间为美国东部时间 9:30 到 16:00,对应着北京时间 21:30 到次日 4:00

在 2020 年,夏令时是从 3 月 8 日早上 2 点开始,到 11 月 1 日早上 2 点结束。

夏令时的起点 (将表前拨)

在 3 月 8 日早上 2 点,大家把表往前调 1 个小时到早上 3 点,感觉是 2 点到 3 点这一段的时间突然没有了,如下图所示:

但是对应到 UTC 上,这段时间根本没有消失,只不过美东时间的 UTC offset 变了,由原来的 UTC-5 变成了 UTC-4,即从原来正常的慢 5 个小时到现在慢 4 个小时,如下图所示:

代码语言:javascript复制ET = tz.gettz('US/Eastern') spring_159am = datetime(2020, 3, 8, 1, 59, 59, tzinfo=ET) spring_3am = datetime(2020, 3, 8, 3, 0, 0, tzinfo=ET) print(spring_159am) print(spring_3am) (spring_3am - spring_159am).total_seconds()代码语言:javascript复制2020-03-08 01:59:59-05:00 2020-03-08 03:00:00-04:00 3601.0代码语言:javascript复制spring_159am = spring_159am.astimezone(tz.UTC) spring_3am = spring_3am.astimezone(tz.UTC) print(spring_159am) print(spring_3am) (spring_3am - spring_159am).total_seconds()代码语言:javascript复制2020-03-08 06:59:59+00:00 2020-03-08 07:00:00+00:00 1.0

夏令时的终点 (将表后拨)

在 11 月 1 日早上 2 点,大家把表往后调 1 个小时到早上 1 点,把之前“丢失的那一个小时找回来了”,回归正常。注意在调时间这个动作点 (夏令时终点) 的前后从“1 点到 2 点”的时间段有歧义,它们既可以指夏令时结束之前的时间段,也可以指夏令时结束之后的时间段。为了偏于说明,用两个时间轴来区分,如下图所示:

但是对应到 UTC 上,丢失的那一个小时找回来,使得对应的美东时间的 UTC offset 变了,由原来的 UTC-4 变成了 UTC-5,即从原来慢 4 个小时又回到正常情况的慢 5 个小时,如下图所示:

代码语言:javascript复制ET = tz.gettz('US/Eastern')

首先用 datetime_ambiugous() 函数来验证在早上 1 点到 2 点这段时间段中的时间是否有歧义:

1:00:00 有歧义1:59:59 有歧义2:00:00 无歧义代码语言:javascript复制first_1am = datetime(2020, 11, 1, 1, 0, 0, tzinfo=ET) tz.datetime_ambiguous(first_1am) first_159am = datetime(2020, 11, 1, 1, 59, 59, tzinfo=ET) tz.datetime_ambiguous(first_159am) first_2am = datetime(2020, 11, 1, 2, 2, 0, tzinfo=ET) tz.datetime_ambiguous(first_2am)代码语言:javascript复制True True False

由于 1:00:00 这个时点有歧义,我们先创建两个日期时间对象 first_1am 和 second_1am,发现两者在 ET 时区和 UTC 的时间差都为零。

代码语言:javascript复制first_1am = datetime(2020, 11, 1, 1, 0, 0, tzinfo=ET) second_1am = datetime(2020, 11, 1, 1, 0, 0, tzinfo=ET) (second_1am - first_1am).total_seconds()代码语言:javascript复制0.0代码语言:javascript复制first_1am = first_1am.astimezone(tz.UTC) second_1am = second_1am.astimezone(tz.UTC) (second_1am - first_1am).total_seconds()代码语言:javascript复制0.0

那这样就无法实现夏令时结束“时间回调”这个现象了,好在我们用 enfold() 函数,它将有歧义的时间“折叠”起来,使得转换成 UTC 时能考虑到“时间回调”。从 first_1am 和 second_1am 之间的时间差为 3600 秒可以看出 enfold() 函数的作用了。

代码语言:javascript复制first_1am = datetime(2020, 11, 1, 1, 0, 0, tzinfo=ET) second_1am = datetime(2020, 11, 1, 1, 0, 0, tzinfo=ET) second_1am = tz.enfold(second_1am) (second_1am - first_1am).total_seconds()代码语言:javascript复制0.0代码语言:javascript复制first_1am = first_1am.astimezone(tz.UTC) second_1am = second_1am.astimezone(tz.UTC) (second_1am - first_1am).total_seconds()代码语言:javascript复制3600.0

将两者表示成美东时间,发现 first_1am 是夏令时结束前的早上 1 点钟,比 UTC 慢 4 小时,而 second_1am 是夏令时结束后的早上 1 点钟,比 UTC 慢 5 小时。

代码语言:javascript复制print(first_1am.astimezone(ET)) print(second_1am.astimezone(ET))代码语言:javascript复制2020-11-01 01:00:00-04:00 2020-11-01 01:00:00-05:00

3

总结

这么清楚的帖子还需要总结吗?需要:

astimezone() 不同时间,相同时刻。replace() 相同时间,不同时刻。用 dateutil.tz 可以方便设定时区很多国家有夏令时,一年调节两次时间,先调慢再调快UTC 是标准,不管你怎么变,对应在 UTC 上的时间不会变,比较不同时区的时间最好转成 UTC 再比较


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3