San Francisco Time ??
Beijing Time ??
把一个时区的时间,比如湾区9:00am,转换成另一个时区的时间,比如北京/香港/新加坡时间,应该是很简单的问题。用Python做时区转换好像挺麻烦的,容易出错。Google出来的前几条结果都不是很让人满意,本文总结我测试过可靠可行不太复杂的方法,以及一些容易出错的地方。
先上可行的Python例子,把三藩(SF)湾区时间9am, 转换成对应的北京时间,用了datetime
和pytz
。先构造一个湾区时间的对象,然后转化成北京/香港/新加坡时间。
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
参考
- Python3中datetime时区转换介绍与踩坑
- python datetime时区转换
- 技巧:用datetime模块处理时区转换,不要用time模块
- 将UTC、EST时区的时间转化成北京时间(python)
- pytz库时区的坑(转)
- The Solution to the Problem of Pyrtz Formatting Beijing Time Over 6 Minutes
- Python 时间处理 时区的转换 时间的计算
- 【Python】python获取国内时间及其时区 pytz模块的使用
- Making sense of timezones in Python
- pytz – World Timezone Definitions for Python
- List of time zones – countryinfo.py on github
- List All TimeZones in Python
- “how to convert time from one timezone to another in python” Code Answer’s. 这个只有now到其他时区时间,没有任意时间