如何在Python中解析多种(未知)日期格式?

17 投票
4 回答
14673 浏览
提问于 2025-04-16 23:31

我有一堆Excel文档,里面有日期信息。我想把这些日期转换成一个标准格式,这样我就可以把它们放进数据库里。有没有什么函数可以让我把这些字符串处理一下,得到一个标准格式的日期呢?以下是我数据的一小部分:

好消息是,我知道日期总是以“月/日”的形式出现。

10/02/09
07/22/09
09-08-2008
9/9/2008
11/4/2010
 03-07-2009
09/01/2010

我想把它们都转换成MM/DD/YYYY的格式。有没有办法可以做到这一点,而不需要逐个尝试不同的格式呢?

4 个回答

10

如果你不想安装像 dateutil 这样的第三方模块:

import re
from datetime import datetime
dates = ['10/02/09', '07/22/09', '09-08-2008', '9/9/2008', '11/4/2010', ' 03-07-2009', '09/01/2010']
reobj = re.compile(
    r"""\s*  # optional whitespace
    (\d+)    # Month
    [-/]     # separator
    (\d+)    # Day
    [-/]     # separator
    (?:20)?  # century (optional)
    (\d+)    # years (YY)
    \s*      # optional whitespace""", 
    re.VERBOSE)
ndates = [reobj.sub(r"\1/\2/20\3", date) for date in dates]
fdates = [datetime.strftime(datetime.strptime(date,"%m/%d/%Y"), "%m/%d/%Y")
          for date in ndates]

结果是:

['10/02/2009', '07/22/2009', '09/08/2008', '09/09/2008', '11/04/2010', '03/07/2009', '09/01/2010']
26

第三方模块 dateutil 有一个叫 parse 的功能,它的工作方式和 PHP 的 strtotime 很像:你不需要指定特定的日期格式,它会自己尝试各种格式。

>>> from dateutil.parser import parse
>>> parse("10/02/09", fuzzy=True)
datetime.datetime(2009, 10, 2, 0, 0)  # default to be in American date format

这个功能还允许你设置不同的假设:

  • dayfirst – 这个选项决定在模糊的三位数日期(比如 01/05/09)中,第一位数字是表示日期的“日”(如果设置为 True)还是“月”(如果设置为 False)。如果 yearfirst 设置为 True,这样就能区分是 YDM 还是 YMD。如果设置为 None,这个值会从当前的解析器信息对象中获取(默认是 False)。
  • yearfirst – 这个选项决定在模糊的三位数日期(比如 01/05/09)中,第一位数字是否表示“年”。如果设置为 True,第一位数字就被认为是年份;如果设置为 False,最后一位数字就被认为是年份。如果设置为 None,这个值也会从当前的解析器信息对象中获取(默认是 False)。
16
import re

ss = '''10/02/09
07/22/09
09-08-2008
9/9/2008
11/4/2010
03-07-2009
09/01/2010'''


regx = re.compile('[-/]')
for xd in ss.splitlines():
    m,d,y = regx.split(xd)
    print xd,'   ','/'.join((m.zfill(2),d.zfill(2),'20'+y.zfill(2) if len(y)==2 else y))

结果

10/02/09     10/02/2009
07/22/09     07/22/2009
09-08-2008     09/08/2008
9/9/2008     09/09/2008
11/4/2010     11/04/2010
03-07-2009     03/07/2009
09/01/2010     09/01/2010

编辑 1

还有 编辑 2:根据 JBernardo 提供的信息 '{0:0>2}'.format(day),我增加了一个第四种解决方案,看起来是最快的。

import re
from time import clock
iterat = 100

from datetime import datetime
dates = ['10/02/09', '07/22/09', '09-08-2008', '9/9/2008', '11/4/2010',
         ' 03-07-2009', '09/01/2010']

reobj = re.compile(
r"""\s*  # optional whitespace
(\d+)    # Month
[-/]     # separator
(\d+)    # Day
[-/]     # separator
(?:20)?  # century (optional)
(\d+)    # years (YY)
\s*      # optional whitespace""",
re.VERBOSE)

te = clock()
for i in xrange(iterat):
    ndates = (reobj.sub(r"\1/\2/20\3", date) for date in dates)
    fdates1 = [datetime.strftime(datetime.strptime(date,"%m/%d/%Y"), "%m/%d/%Y")
               for date in ndates]
print "Tim's method   ",clock()-te,'seconds'



regx = re.compile('[-/]')


te = clock()
for i in xrange(iterat):
    ndates = (reobj.match(date).groups() for date in dates)
    fdates2 = ['%s/%s/20%s' % tuple(x.zfill(2) for x in tu) for tu in ndates]
print "mixing solution",clock()-te,'seconds'


te = clock()
for i in xrange(iterat):
    ndates = (regx.split(date.strip()) for date in dates)
    fdates3 = ['/'.join((m.zfill(2),d.zfill(2),('20'+y.zfill(2) if len(y)==2 else y)))
              for m,d,y in ndates]
print "eyquem's method",clock()-te,'seconds'



te = clock()
for i in xrange(iterat):
    fdates4 = ['{:0>2}/{:0>2}/20{}'.format(*reobj.match(date).groups()) for date in dates]
print "Tim + format   ",clock()-te,'seconds'


print fdates1==fdates2==fdates3==fdates4

结果

number of iteration's turns : 100
Tim's method    0.295053700959 seconds
mixing solution 0.0459111423379 seconds
eyquem's method 0.0192239516475 seconds
Tim + format    0.0153756971906 seconds 
True

这个混合解决方案很有意思,因为它结合了我方案的速度和 Tim Pietzcker 的正则表达式在字符串中检测日期的能力。

对于结合 Tim 的方案和使用 {:0>2} 格式化的解决方案,这一点尤其正确。我不能把 {:0>2} 和我的方案结合起来,因为 regx.split(date.strip()) 产生的年份可能是 2 位或 4 位数字。

撰写回答