Pygal图涉及日期时间抛出错误与1970年之前的日期

时间:2018-03-21 14:29:53

标签: pygal

在涉及日期时间或日期的pygal中试用sample code的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。) 也许是千禧年&#39;正在返回意外的负数?

(编辑) 我使用了&#34; date&#34;下的代码,在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()

......当我改变&#39; 2013&#39;至1969年,&#39;我得到了上面显示的Traceback。

1 个答案:

答案 0 :(得分:1)

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

docs for datetime.utcfromtimestamp提到了以下限制:

  

如果时间戳不在平台C gmtime()函数支持的值范围内,并且在gmtime()失败时出现OSError,则可能会引发OverflowError。通常将其限制在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

这几乎是从source code for DateTimeLine复制而来的。唯一的变化是向dt赋值的行。

我们现在可以使用此类代替DateTimeLine来绘制2013年或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")

Plotting dates in 2013 Plotting dates in 1969