1

尝试pygal 中涉及日期时间或日期的 XY 图的示例代码,1970 年之前的任何日期都会导致此回溯:

Traceback (most recent call last):
  File "C:/Users/***/dt_test.py", line 30, in <module>
    datetimeline.render()
  File "C:\Python\Python36\lib\site-packages\pygal\graph\public.py", line 52, 
in render
    self.setup(**kwargs)
  File "C:\Python\Python36\lib\site-packages\pygal\graph\base.py", line 217, 
in setup
self._draw()
  File "C:\Python\Python36\lib\site-packages\pygal\graph\graph.py", line 924, 
in _draw
    self._compute_x_labels()
  File "C:\Python\Python36\lib\site-packages\pygal\graph\dual.py", line 61, 
in _compute_x_labels
    self._x_labels = list(zip(map(self._x_format, x_pos), x_pos))
  File "C:\Python\Python36\lib\site-packages\pygal\graph\time.py", line 103, 
in datetime_to_str
    dt = datetime.utcfromtimestamp(x)
OSError: [Errno 22] Invalid argument

有没有其他人得到这种行为?(我正在使用 PyCharm。)也许“千年”正在返回一个意外的负数?

(编辑)我使用了“日期”下的代码,在 PyCharm 中运行:

from datetime import datetime
datetimeline = pygal.DateTimeLine(
    x_label_rotation=35, truncate_label=-1,
    x_value_formatter=lambda dt: dt.strftime('%d, %b %Y at %I:%M:%S %p'))
datetimeline.add("Serie", [
    (datetime(2013, 1, 2, 12, 0), 300),
    (datetime(2013, 1, 12, 14, 30, 45), 412),
    (datetime(2013, 2, 2, 6), 823),
    (datetime(2013, 2, 22, 9, 45), 672)
])
datetimeline.render()

...当我将“2013”​​更改为“1969”时,我得到了上面显示的 Traceback。

4

1 回答 1

1

这是由 Python 用于某些日期和时间处理的底层 C 函数的限制引起的。这些功能在不同平台上的实现方式不同,这就是为什么它不会影响每个人(我不得不借用一个 Windows 盒子来复制错误)。

提及这些限制的文档:datetime.utcfromtimestamp

如果时间戳超出平台 C gmtime() 函数支持的值范围,这可能会引发 OverflowError,并且在 gmtime() 失败时出现 OSError。这通常仅限于 1970 年到 2038 年的年份。

幸运的是,相同的文档提出了一种解决方法,并且由于DateTimeLine包含麻烦行的类中的函数非常短,我们可以轻松地创建自己的类来继承DateTimeLine并覆盖该函数。

import pygal
from datetime import datetime, timedelta, timezone

class MyDateTimeLine(pygal.DateTimeLine):

    @property
    def _x_format(self):
        def datetime_to_str(x):
            dt = datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(seconds=x)
            return self.x_value_formatter(dt)
        return datetime_to_str

这几乎只是DateTimeLine. 唯一的变化是对给 赋值的行dt

我们现在可以使用这个类作为DateTimeLine2013 年或 1969 年的绘图日期的替代品:

formatter = lambda dt: dt.strftime("%d, %b %Y at %I:%M:%S %p")

chart = MyDateTimeLine(x_label_rotation=35, truncate_label=-1,
                       x_value_formatter=formatter, width=640, height=300)
chart.add("2013", [(datetime(2013, 1, 2, 12, 0), 300),
                   (datetime(2013, 1, 12, 14, 30, 45), 412),
                   (datetime(2013, 2, 2, 6), 823),
                   (datetime(2013, 2, 22, 9, 45), 672)])
chart.render_to_png("chart2013.png")

chart = MyDateTimeLine(x_label_rotation=35, truncate_label=-1,
                       x_value_formatter=formatter, width=640, height=300)
chart.add("1969", [(datetime(1969, 1, 2, 12, 0), 300),
                   (datetime(1969, 1, 12, 14, 30, 45), 412),
                   (datetime(1969, 2, 2, 6), 823),
                   (datetime(1969, 2, 22, 9, 45), 672)])
chart.render_to_png("chart1969.png")

2013 年的绘图日期 1969 年的绘图日期

于 2018-06-22T19:41:33.243 回答