Python中是否有类似strftime函数的方法可以处理1900年以前的日期?

20

我之前没有意识到,但显然Python的strftime函数不支持1900年之前的日期:

>>> from datetime import datetime
>>> d = datetime(1899, 1, 1)
>>> d.strftime('%Y-%m-%d')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: year=1899 is before 1900; the datetime strftime() methods require year >= 1900

我确定我可以自己破解一些东西来完成这个任务,但我认为 strftime 函数是有原因存在的(也有一个不能支持 1900 年之前日期的原因)。我需要支持 1900 年之前的日期。我只想使用 str,但变化太多了。换句话说,它可能具有微秒或时区,也可能没有。是否有任何解决方案?

如果有所不同,我正在这样做是为了将数据写入文本文件,并使用 Oracle SQL*Loader 将其加载到数据库中。

实质上,我最终采用了 Alex Martelli 的答案。 这里是更完整的实现:

>>> from datetime import datetime
>>> d = datetime.now()
>>> d = d.replace(microsecond=0, tzinfo=None)
>>> str(d)
'2009-10-29 11:27:27'
唯一的区别在于str(d)等价于d.isoformat(' ')
5个回答

15

isoformat可以对任意范围内的 datetime 实例进行操作:

>>> import datetime
>>> x=datetime.datetime(1865, 7, 2, 9, 30, 21)
>>> x.isoformat()
'1865-07-02T09:30:21'
如果您需要不同格式的字符串,从isoformat获取的字符串很容易被切分、拼接和重新组合,这个字符串非常一致(YYYY-MM-DDTHH:MM:SS.mmmmmm,如果毫秒为零,则省略点和后面的微秒)。

这基本上就是我最终所做的。 - Jason Baker

11

文档对此非常清晰:

strftime()适用的确切年份范围也因平台而异。无论平台如何,都不能使用1900年之前的年份。

因此,没有使用strftime()的解决方案。幸运的是,手动完成这个任务也很简单:

>>> "%02d-%02d-%02d %02d:%02d" % (d.year,d.month,d.day,d.hour,d.minute)
'1899-01-01 00:00'

1
已经发布了一些示例代码,展示了为什么您实际上不需要使用strftime()。 - David Webb

4

mxDateTime 可以处理任意日期。Python的 timedatetime 模块在内部使用UNIX时间戳,这就是它们具有有限范围的原因。

In [5]: mx.DateTime.DateTime(1899)
Out[5]: <mx.DateTime.DateTime object for '1899-01-01 00:00:00.00' at 154a960>

In [6]: DateTime.DateTime(1899).Format('%Y-%m-%d')
Out[6]: 1899-01-01

唉...就在我认为我们不再使用mxODBC时,我以为我能够将mx*作为依赖项移除。 :-( - Jason Baker

3
这是来自于 matplotlib源码。它可以为你自己的滚动提供一个良好的起点。
def strftime(self, dt, fmt):
    fmt = self.illegal_s.sub(r"\1", fmt)
    fmt = fmt.replace("%s", "s")
    if dt.year > 1900:
        return cbook.unicode_safe(dt.strftime(fmt))

    year = dt.year
    # For every non-leap year century, advance by
    # 6 years to get into the 28-year repeat cycle
    delta = 2000 - year
    off = 6*(delta // 100 + delta // 400)
    year = year + off

    # Move to around the year 2000
    year = year + ((2000 - year)//28)*28
    timetuple = dt.timetuple()
    s1 = time.strftime(fmt, (year,) + timetuple[1:])
    sites1 = self._findall(s1, str(year))

    s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
    sites2 = self._findall(s2, str(year+28))

    sites = []
    for site in sites1:
        if site in sites2:
        sites.append(site)

    s = s1
    syear = "%4d" % (dt.year,)
    for site in sites:
        s = s[:site] + syear + s[site+4:]

    return cbook.unicode_safe(s)

2
啊,这源于我对matplotlib的贡献。抽泣 :) - Andrew Dalke
@dalke,太棒了。在Stack Overflow上这里真是个小世界。 - Mark

2
这是 ctime 库(UTF)的“特性”。 此外,您可能会在2038年之后遇到问题。

我明白了。所以问题在于日期不能超出正负32位吗? - Jason Baker
1
确切地说,没有直截了当的解决方案。 - bua

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接