检查字符串是否包含日期,任意格式

106 投票
3 回答
148773 浏览
提问于 2025-04-18 17:30

我该怎么检查一个字符串是否可以转成日期呢?

  • 1990年1月19日
  • 1990年1月19日
  • 1990年1月19日
  • 01/19/1990
  • 01/19/90
  • 1990
  • 1990年1月
  • 1990年1月

这些都是有效的日期。如果你担心第3项和最后一项之间没有空格,这个问题可以通过自动在字母、字符和数字之间插入空格来轻松解决,如果需要的话。

但首先,我们先了解一些基础:

我试着把它放在一个if语句里:

if datetime.strptime(item, '%Y') or datetime.strptime(item, '%b %d %y') or datetime.strptime(item, '%b %d %Y')  or datetime.strptime(item, '%B %d %y') or datetime.strptime(item, '%B %d %Y'):

但是这段代码是在一个try-except块里,结果总是返回类似这样的东西:

16343 time data 'JUNE1890' does not match format '%Y'

除非它满足if语句中的第一个条件。

为了更清楚,我其实不需要日期的具体值 - 我只是想知道它是否是一个日期。理想情况下,它应该是这样的:

if item is date:
    print date
else:
    print "Not a date"

有没有什么方法可以做到这一点呢?

3 个回答

5

流行的Python库pandas里面有一个功能,可以很稳定地解析日期。如果你给它的参数设置为errors='coerce',那么对于那些不是日期的字符串,它会返回NaN(也就是“不是一个数字”的意思)。

txt='''\
Jan 19, 1990
January 19, 1990
Jan 19,1990
01/19/1990
01/19/90
1990
Jan 1990
January1990
19 Jan 1990
this is not date'''

for s in txt.split('\n'):
    dt = pd.to_datetime(s.replace(',', ' '), errors='coerce')
    print(dt, dt == dt)
    
# 1990-01-19 00:00:00 True
# 1990-01-19 00:00:00 True
# 1990-01-19 00:00:00 True
# 1990-01-19 00:00:00 True
# 1990-01-19 00:00:00 True
# 1990-01-01 00:00:00 True
# 1990-01-01 00:00:00 True
# 1990-01-01 00:00:00 True
# 1990-01-19 00:00:00 True
# NaT False

pd.to_datetime的一个好处是它是向量化的,这意味着你可以把整个列表都传给它处理。

converted = pd.to_datetime(txt.split('\n'), errors='coerce')

如果你想得到一个布尔值的序列,可以在结果上调用notna()

converted.notna()
17

如果你想处理这些特定的格式,你可以直接对照一个格式列表来进行匹配:

txt='''\
Jan 19, 1990
January 19, 1990
Jan 19,1990
01/19/1990
01/19/90
1990
Jan 1990
January1990'''

import datetime as dt

fmts = ('%Y','%b %d, %Y','%b %d, %Y','%B %d, %Y','%B %d %Y','%m/%d/%Y','%m/%d/%y','%b %Y','%B%Y','%b %d,%Y')

parsed=[]
for e in txt.splitlines():
    for fmt in fmts:
        try:
           t = dt.datetime.strptime(e, fmt)
           parsed.append((e, fmt, t)) 
           break
        except ValueError as err:
           pass

# check that all the cases are handled        
success={t[0] for t in parsed}
for e in txt.splitlines():
    if e not in success:
        print e    

for t in parsed:
    print '"{:20}" => "{:20}" => {}'.format(*t) 

输出结果是:

"Jan 19, 1990        " => "%b %d, %Y           " => 1990-01-19 00:00:00
"January 19, 1990    " => "%B %d, %Y           " => 1990-01-19 00:00:00
"Jan 19,1990         " => "%b %d,%Y            " => 1990-01-19 00:00:00
"01/19/1990          " => "%m/%d/%Y            " => 1990-01-19 00:00:00
"01/19/90            " => "%m/%d/%y            " => 1990-01-19 00:00:00
"1990                " => "%Y                  " => 1990-01-01 00:00:00
"Jan 1990            " => "%b %Y               " => 1990-01-01 00:00:00
"January1990         " => "%B%Y                " => 1990-01-01 00:00:00
185

parse 函数在dateutils.parser中,可以把很多不同格式的日期字符串转换成一个 datetime 对象。

pip install python-dateutil

如果你只是想知道某个字符串是否可能表示一个有效的日期,可以试试下面这个简单的函数:

from dateutil.parser import parse

def is_date(string, fuzzy=False):
    """
    Return whether the string can be interpreted as a date.

    :param string: str, string to check for date
    :param fuzzy: bool, ignore unknown tokens in string if True
    """
    try: 
        parse(string, fuzzy=fuzzy)
        return True

    except ValueError:
        return False

接下来你会得到:

>>> is_date("1990-12-1")
True
>>> is_date("2005/3")
True
>>> is_date("Jan 19, 1990")
True
>>> is_date("today is 2019-03-27")
False
>>> is_date("today is 2019-03-27", fuzzy=True)
True
>>> is_date("Monday at 12:01am")
True
>>> is_date("xyz_not_a_date")
False
>>> is_date("yesterday")
False

自定义解析

parse 可能会把一些你不想当作日期的字符串识别为日期。例如:

  • 解析 "12""1999" 会返回一个表示当前日期的 datetime 对象,只不过日期和年份会用字符串中的数字替代。

  • "23, 4""23 4" 会被解析为 datetime.datetime(2023, 4, 16, 0, 0)

  • "Friday" 会返回最近的一个星期五的日期。

  • 同样,"August" 会对应当前日期,只不过月份会改成八月。

另外,parse 不会考虑地区设置,所以它无法识别非英语的月份或星期几。

这两个问题在一定程度上可以通过使用自定义的parserinfo类来解决,这个类定义了如何识别月份和星期几的名称:

from dateutil.parser import parserinfo
                                                
class CustomParserInfo(parserinfo):

    # three months in Spanish for illustration
    MONTHS = [("Enero", "Enero"), ("Feb", "Febrero"), ("Marzo", "Marzo")]

然后你可以用这个类的实例来配合 parse 使用:

>>> parse("Enero 1990")
# ValueError: Unknown string format
>>> parse("Enero 1990", parserinfo=CustomParserInfo())
datetime.datetime(1990, 1, 27, 0, 0)

撰写回答