python - 将具有 UTC 偏移量的 Unix 时间戳转换为不同时区的 Python 日期时间?
问题描述
如果我运行以下 git log 命令(在此 repo 中:https ://github.com/rubyaustralia/rubyconfau-2013-cfp ):
$ git --no-pager log --reverse --date=raw --pretty='%ad %h'
1344507869 -0700 314b3d4
1344508222 +1000 dffde53
1344510528 +1000 17e7d3b
...
...我得到一个列表,其中对于每个提交,我都有 Unix 时间戳(自纪元以来的秒数)和 UTC 偏移量。我想做的是获得一个时区感知日期时间,这将:
- 显示提交作者当时看到的日期/时间(根据记录的 UTC 时间)
- 显示我在本地时区看到的日期/时间
在第一种情况下,我所拥有的只是一个 UTC 偏移量,而不是作者的时区——因此我没有关于可能的夏令时更改的信息。
在第二种情况下,我的操作系统很可能会设置为特定的语言环境,包括(地理)时区,它会知道 DST 的变化;说 CET 时区在冬季的 UTC 偏移量为 +0100,但在夏季夏令时,它的 UTC 偏移量为 +0200(然后称为 CEST)
无论如何,我想从 UTC 时间戳开始,因为纪元秒数的“1344508222”计数与时区无关;偏移量 +1000 只会帮助我们看到作者看到的人类可读的输出。
我需要为 Python 2.7 项目执行此操作,并且我搜索了大量资源(SO 帖子), - 我想出了以下示例(它试图解析上述代码段中的第二行,“ 1344508222 +1000 dffde53
”)。但是,我真的不确定它是否正确;所以最终,我的问题是 - 这样做的正确方法是什么?
前言:
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import datetime
import pytz
import dateutil.tz
import time
def getUtcOffsetFromString(in_offset_str): # SO:1101508
offset = int(in_offset_str[-4:-2])*60 + int(in_offset_str[-2:])
if in_offset_str[0] == "-":
offset = -offset
return offset
class FixedOffset(datetime.tzinfo): # SO:1101508
"""Fixed offset in minutes: `time = utc_time + utc_offset`."""
def __init__(self, offset):
self.__offset = datetime.timedelta(minutes=offset)
hours, minutes = divmod(offset, 60)
#NOTE: the last part is to remind about deprecated POSIX GMT+h timezones
# that have the opposite sign in the name;
# the corresponding numeric value is not used e.g., no minutes
self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours)
def utcoffset(self, dt=None):
return self.__offset
def tzname(self, dt=None):
return self.__name
def dst(self, dt=None):
return datetime.timedelta(0)
def __repr__(self):
return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)
解析开始:
tstr = "1344508222 +1000 dffde53"
tstra = tstr.split(" ")
unixepochsecs = int(tstra[0])
utcoffsetstr = tstra[1]
print(unixepochsecs, utcoffsetstr) # (1344508222, '+1000')
1528917616 +0000
获取 UTC 时间戳 -首先我尝试解析字符串dateutil.parser.parse
:
justthetstz = " ".join(tstra[:2])
print(justthetstz) # '1344508222 +1000'
#print(dateutil.parser.parse(justthets)) # ValueError: Unknown string format
...但不幸的是失败了。
这有助于获得 UTC 时间戳:
# SO:12978391: "datetime.fromtimestamp(self.epoch) returns localtime that shouldn't be used with an arbitrary timezone.localize(); you need utcfromtimestamp() to get datetime in UTC and then convert it to a desired timezone"
dtstamp = datetime.datetime.utcfromtimestamp(unixepochsecs).replace(tzinfo=pytz.utc)
print(dtstamp) # 2012-08-09 10:30:22+00:00
print(dtstamp.isoformat()) # 2012-08-09T10:30:22+00:00 # ISO 8601
好的,到目前为止一切顺利 - 这个 UTC 时间戳看起来很合理。
现在,尝试获取作者的 UTC 偏移量中的日期 - 显然这里需要一个自定义类:
utcoffset = getUtcOffsetFromString(utcoffsetstr)
fixedtz = FixedOffset(utcoffset)
print(utcoffset, fixedtz) # (600, FixedOffset(600))
dtstampftz = dtstamp.astimezone(fixedtz)
print(dtstampftz) # 2012-08-09 20:30:22+10:00
print(dtstampftz.isoformat()) # 2012-08-09T20:30:22+10:00
这看起来也很合理,UTC 的 10:30 将是 +1000 的 20:30;再说一次,偏移量是偏移量,这里没有歧义。
现在我正在尝试在我的本地时区导出日期时间 - 首先,看起来我不应该使用该.replace
方法:
print(time.tzname[0]) # CET
tzlocal = dateutil.tz.tzlocal()
print(tzlocal) # tzlocal()
dtstamplocrep = dtstamp.replace(tzinfo=tzlocal)
print(dtstamp) # 2012-08-09 10:30:22+00:00
print(dtstamplocrep) # 2012-08-09 10:30:22+02:00 # not right!
这看起来不对,我得到了完全相同的“时钟字符串”和不同的偏移量。
但是,.astimezone
似乎有效:
dtstamploc = dtstamp.astimezone(dateutil.tz.tzlocal())
print(dtstamp) # 2012-08-09 10:30:22+00:00
print(dtstamploc) # 2012-08-09 12:30:22+02:00 # was August -> summer -> CEST: UTC+2h
我对命名的结果相同pytz.timezone
:
cphtz = pytz.timezone('Europe/Copenhagen')
dtstamploc = dtstamp.astimezone(cphtz)
print(dtstamp) # 2012-08-09 10:30:22+00:00
print(dtstamploc) # 2012-08-09 12:30:22+02:00 # is August -> summer -> CEST: UTC+2h
...但是,我不能.localize
在这里使用,因为我的输入dtstamp
已经有一个与之关联的时区,因此不再“幼稚”:
# dtstamploc = cphtz.localize(dtstamp, is_dst=True) # ValueError: Not naive datetime (tzinfo is already set)
最终,到目前为止,这看起来是正确的,但我真的不确定 - 特别是因为我看到了这个:
您不能在 datetime 构造函数中分配时区,因为它没有给时区对象一个调整夏令时的机会 - 它无法访问日期。这会给世界某些地区带来更多问题,这些地区的时区名称和偏移量多年来一直在变化。
从 pytz 文档:
不幸的是,对于许多时区,使用标准日期时间构造函数的 tzinfo 参数对 pytz “不起作用”。
改为使用带有天真的日期时间的 localize 方法。
...最终让我感到困惑:说我想这样做,并且我已经有一个正确的时区时间戳, - 我将如何为它推导出一个“天真的”日期时间?只是摆脱时区信息?还是从以 UTC 表示的时间戳版本派生的正确“天真”日期时间(例如2012-08-09 20:30:22+10:00
-> 2012-08-09 10:30:22+00:00
,因此正确的“天真”日期时间将是2012-08-09 10:30:22
)?
解决方案
推荐阅读
- c# - x86 时,继承的控件未显示在设计器视图中
- ios - 尝试在 Swift 5 中解析 JSON 文件(“预期解码数组
但找到了一本字典。”) - asp.net-mvc - DotNet Core 3.1 身份验证:oauth 状态丢失或无效
- java - 试图添加到 fechaProceso(localDate) 他们不在周末的日子 Java
- html - 添加absoule属性后如何阻止我的页面滚动
- import - Mariadb 无法通过“Load data infile”导入数据的问题
- android - Android Instrumental Test 未从 Startup 库开始
- openiddict - 使用 openiddict 的外部身份提供者示例
- sql - Presto SQL - 两个完全不同的表连接平行而不是垂直
- iis - 如何配置我的 web.config 以访问公共/子目录中托管在 IIS 上的 Symfony API 的文件?