CyanideCN/PyCINRAD

使用datetime 处理数据时间时最好带上时区信息

Anebrithien opened this issue · 3 comments

CinradReaderscantime 是没有指定 tzinfo 属性的 datetime.datetime 对象,在对这个对象调用 timestamp() 等一些与操作系统所在时区有关的函数时,会出现意料之外的情况。

观察代码发现,认定了雷达数据里的日期时间是UTC+8的,所以做了减去8小时的操作,手动将它变成UTC日期时间。

代码片段:

utc_offset = datetime.timedelta(hours=8)

self.scantime = (
datetime.datetime(
header["ucEYear1"][0] * 100 + header["ucEYear2"][0],
header["ucEMonth"][0],
header["ucEDay"][0],
header["ucEHour"][0],
header["ucEMinute"][0],
header["ucESecond"][0],
)
- utc_offset
)

下面是一个简单示例:

>>> import datetime
>>> original_time = datetime.datetime(2014, 6, 11, 0, 3, 47)
>>> time_offset = datetime.timedelta(hours=8)
>>> scan_time = original_time - time_offset
>>> scan_time.timestamp()
1402387427.0
  • 如果只是把 scan_time 格式化为字符串,那确实看起来是正确的UTC日期时间。
  • 而在时区是UTC+8的系统上,scan_time.timestamp() 得到结果1402387427.0
    再把这个时间戳转换成UTC日期时间,是 2014-06-10 08:03:47 ,比正确的UTC日期时间又少了8小时,这是错误的。

暂未得知其他使用了 datetime.datetime 的地方是否有类似的隐患

欢迎pr

关于这处的代码,我有一点疑问

start = datetime.datetime(1969, 12, 31)
deltday = datetime.timedelta(days=int(data["day"][0]))
deltsec = datetime.timedelta(milliseconds=int(data["time"][0]))
self.scantime = start + deltday + deltsec

这里为什么是1970-01-01 的前一天呢?
如果是这样的话,datetime.datetime.utcfromtimestamp() 就需要减去1天才行了

示例:

>>> datetime.datetime(1969, 12, 31) + datetime.timedelta(days=23333) + datetime.timedelta(milliseconds=23132)
datetime.datetime(2033, 11, 18, 0, 0, 23, 132000)
>>> epoch_seconds = (datetime.timedelta(days=23333) + datetime.timedelta(milliseconds=23132)).total_seconds()
>>> epoch_seconds
2015971223.132
>>> datetime.datetime.utcfromtimestamp(epoch_seconds).replace(tzinfo=datetime.timezone.utc)
datetime.datetime(2033, 11, 19, 0, 0, 23, 132000, tzinfo=datetime.timezone.utc)
>>> datetime.datetime.utcfromtimestamp(epoch_seconds).replace(tzinfo=datetime.timezone.utc).timestamp()
2015971223.132

关于这处的代码,我有一点疑问

start = datetime.datetime(1969, 12, 31)
deltday = datetime.timedelta(days=int(data["day"][0]))
deltsec = datetime.timedelta(milliseconds=int(data["time"][0]))
self.scantime = start + deltday + deltsec

这里为什么是1970-01-01 的前一天呢? 如果是这样的话,datetime.datetime.utcfromtimestamp() 就需要减去1天才行了

示例:

>>> datetime.datetime(1969, 12, 31) + datetime.timedelta(days=23333) + datetime.timedelta(milliseconds=23132)
datetime.datetime(2033, 11, 18, 0, 0, 23, 132000)
>>> epoch_seconds = (datetime.timedelta(days=23333) + datetime.timedelta(milliseconds=23132)).total_seconds()
>>> epoch_seconds
2015971223.132
>>> datetime.datetime.utcfromtimestamp(epoch_seconds).replace(tzinfo=datetime.timezone.utc)
datetime.datetime(2033, 11, 19, 0, 0, 23, 132000, tzinfo=datetime.timezone.utc)
>>> datetime.datetime.utcfromtimestamp(epoch_seconds).replace(tzinfo=datetime.timezone.utc).timestamp()
2015971223.132

这是当时写的时候试出来的,如果是按文档里的1970-01-01就会比实际上的日期多了一天,我也不知道为什么