旧金山湾区时间与北京时间转换,用python实现时区转换例子

San Francisco Time ??

Beijing Time ??

把一个时区的时间,比如湾区9:00am,转换成另一个时区的时间,比如北京/香港/新加坡时间,应该是很简单的问题。用Python做时区转换好像挺麻烦的,容易出错。Google出来的前几条结果都不是很让人满意,本文总结我测试过可靠可行不太复杂的方法,以及一些容易出错的地方。

先上可行的Python例子,把三藩(SF)湾区时间9am, 转换成对应的北京时间,用了datetimepytz。先构造一个湾区时间的对象,然后转化成北京/香港/新加坡时间。

from pytz import timezone
from datetime import datetime

bjtz = timezone('Asia/Harbin')
sftz = timezone('US/Pacific')

dt9amsf = sftz.localize(datetime(2022, 7, 11, 9, 0))
dt9amsf2bj = dt9amsf.astimezone(bjtz)

加上注解和debug信息。

from pytz import timezone
from datetime import datetime

format = '%Y-%m-%d %H:%M:%S %Z %z'

bjtz = timezone('Asia/Harbin')
sftz = timezone('US/Pacific')

# construct SF 9am datetime object
dt9amsf = sftz.localize(datetime(2022, 7, 11, 9, 0))

# for debug
print(dt9amsf.strftime(format)) # 2022-07-11 09:00:00, 09:00AM PDT -0700
print(dt9amsf.timestamp()) #1657555200.0

# Convert to Beijing time
dt9amsf2bj = dt9amsf.astimezone(bjtz)
# debug
print(dt9amsf2bj.strftime(format)) #2022-07-12 00:00:00, 12:00AM CST +0800

可以看到建立的湾区时间时区为-0700而北京时间为0800,可以验证湾区和中国时差为15小时。

时区行话解释

全球标准时间用的是位于英国的格林威治时间(GMT, Greenwich Mean Time)。因为以前格林威治天文台是世界中心,本初子午线或者0度经线就定义为穿过它的经线。然后又有了一个叫世界协调时间的东西(UTC, Coordinated Universal Time)。这个和GMT基本一致,大概就是北京话和普通话的区别吧。北京在Greenwich的东边,时间领先GMT/UTC 8小时,所以叫东八区,代号0800。而湾区在格林威治的西边,时间滞后7个小时(美国国会通过了永久使用夏令时法案),所以叫西7区,代号-0700。而两地时差为7+9=15,北京时间比湾区时间领先15个小时。

同理,日本和韩国位于东9区,代号为0900,那么时间就比为与东八区的中国早1小时。

当地真实时间和约定标准时间

一个地方的真实时间只取决于太阳相对当地的位置,也就是取决于当地经度。但是为了方便,每个国家可以人为规定用什么时间。比如整个中国虽然跨了几个纬度,确人为规定了各地都使用北京时间。新加坡真实时间应该为0655,确规定使用0800,也许是和大中华区保持一致比较方便。

回到Python上,这也正是pytz包的一些坑的来源。pytz用的是地理意义上的真实时间,而人们真正使用的是当地法律约定的时间,这两者是有差别的。

Python datetime不设时区是什么时间?

如果新建一个datetime对象,不设时区的话,会是当地时间。

from datetime import datetime

dt = datetime(2022, 7, 1)
format = '%Y-%m-%d %H:%M:%S %Z %z'
print(dt.strftime(format)) # => '2022-07-01 00:00:00  ' time zone missing
print(dt.timestamp()) # => 1656658800.0

epochconverter转换1656658800.0,正是湾区时间2022-07-01 00:00:00

如果replace了timezone,就把底下的timestamp变了,而不是保持timestamp而改变时间表示形式(日期,小时/分/秒)。所以replace(tzinfo=…)并不是做时区变换。而且注意下面例子里上海时间是0806,并不是0800,应该用了地理意义上的真实时间而不是法定时间。这个地理时间在生活中基本没用,这个pytz的坑之一。

bjdt = dt.replace(tzinfo=bjtz) # bjdt is datetime.datetime(2022, 7, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Harbin' LMT+8:06:00 STD>), dt itself is not changed
print(bjdt.strftime(format)) # => '2022-07-01 00:00:00 LMT +0806'
bjdt.timestamp() # => 1656604440.0

参考

Leave a Comment

Your email address will not be published.