Python中是否有类似于C#的DateTime.TryParse()的等效函数?

34

Python中是否有与C#的DateTime.TryParse()相等的功能?

我指的是它避免抛出异常的特性,而不是它猜测日期格式的特性。


1
一般注释:在Python中你不会找到很多try_parse_*方法。惯用语通常是“宁愿请求宽恕,也不要事先获得许可”,即只需执行并捕获异常(如果您可以处理它们)。 - user395760
1
@delnan:C#的DateTime.TryParse会猜测字符串的时间格式。这不是“三思而后行”的问题。 - Steven Rumbalski
1
在这个问题中,关键是要找到Python中猜测字符串日期格式的方法。EAFP和LBYL与此问题无关。方法名称中的“try”一词并不指代EAFP或LBYL。 - Steven Rumbalski
1
@Steven:我实际上是在指它没有抛出异常,而不是它猜测日期格式的事实...我会澄清这一点。 - user541686
这个问题的标题对99.9%的Python程序员来说是无法理解的,我强烈建议将其编辑为:“如何尝试...捕获多个日期时间格式,并抑制/处理所有不匹配的异常?”去掉C#术语。 - smci
显示剩余4条评论
8个回答

28

如果你不想要异常,就捕获它。

try:
    d = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
except ValueError:
    d = None
在Python的“禅语”中,显式优于隐式。 strptime始终会返回按照指定精确格式解析后的 datetime 对象。这是有道理的,因为在失败时必须定义行为,也许你真正想要的是。
except ValueError:
    d = datetime.datetime.now()
或者
except ValueError:
    d = datetime.datetime.fromtimestamp(0)
或者
except ValueError:
    raise WebFramework.ServerError(404, "Invalid date")

通过明确指定,下一个阅读该代码的人就能清楚了解故障转移行为,并且确认它符合你的需求。


或者你很确定日期不会无效;因为它是从数据库DATETIME列中获取的,那么就不需要捕获异常。


9
“如果你不想要异常,就捕获异常。” 你真的让我用异常来控制程序流程吗?x____x - user541686
3
我告诉过你在异常处理中要使用异常,而日期格式错误不正是一个异常情况吗? - SingleNegationElimination
1
这可能是一个日期,也可能是其他类型的数据(整数等)。在我的程序中这是完全有效的,我只需要知道它是哪种类型的数据。 - user541686
1
我只是不这样做,因为我的程序是这样工作的...它有点跑题了,抱歉。我也很难过。 :( - user541686
1
@匿名的downvoter:很抱歉您不同意我的答案;这实际上归结为“没有python等效于c#的DateTime.TryParse(),因为TryParse 不符合python的风格”。Python中的所有解析器在遇到无法解析的输入时都会引发ValueError异常。 - SingleNegationElimination
显示剩余2条评论

14

我们希望尝试使用多个日期时间格式 fmt1,fmt2,...,fmtn 并抑制/处理所有不匹配的异常(来自 strptime ),特别是避免需要丑陋的n重缩进阶梯 try..catch 子句。我找到了两种优雅的方法,第二种通常最好。(这是实际数据中的一个大问题,其中多个、不匹配、不完整、不一致、多语言/地区的日期格式经常混合在一个数据集中。)

1)逐个尝试应用每个格式,并将每个单独的 strptime()失败处理为 None 的返回值,以便您可以链接fn调用......

首先,从@OrWeis的答案进行调整:

def try_strptime_single_format(s, fmt):
    try:
        return datetime.datetime.strptime(s, fmt)
    except ValueError:
        return None

现在您可以使用try_strptime(s, fmt1) or try_strptime(s, fmt2) or try_strptime(s, fmt3) ...来调用。但我们可以将其改进为:

2)应用多个可能的格式(作为参数传递或使用合理的默认值),遍历这些格式,内部捕获和处理任何错误:

更加简洁、简单和面向对象的做法是将formats参数通用化为单个字符串或列表,然后对其进行迭代... 这样,您的调用就可以简化为try_strptime(s, [fmt1, fmt2, fmt3, ...])

def try_strptime(s, fmts=['%d-%b-%y','%m/%d/%Y']):
    for fmt in fmts:
        try:
            return datetime.strptime(s, fmt)
        except:
            continue

    return None # or reraise the ValueError if no format matched, if you prefer

(顺便提一下,需要注意的是...finally语句并不是我们想要的东西,因为它会在每次循环通过即每个候选格式上执行,而不是在循环结束时执行一次。)

我认为实现方式2)更加简洁和优秀。特别是该函数/方法可以存储默认格式列表,这使得它在真实世界的数据上更加安全和少出现异常(我们甚至可以根据其他列推断应用哪些默认格式,例如在德语数据上首先尝试德语日期格式,在阿拉伯语数据上尝试阿拉伯语日期格式,在网络日志数据上尝试日志日期时间格式等等)。


为了完整性,异常应该是 ValueError。 - rob

5
这是一个等价的函数实现:
import datetime

def try_strptime(s, format):
    """
    @param s the string to parse
    @param format the format to attempt parsing of the given string
    @return the parsed datetime or None on failure to parse 
    @see datetime.datetime.strptime
    """
    try:
        date = datetime.datetime.strptime(s, format)
    except ValueError:
        date = None
    return date

太好了!如果我们需要尝试多种格式并且不想构建笨重的n层缩进的try..except阶梯,这实际上是最好的实现方式。您可能想指出其优雅之处:您可以将其链接为try_strptime(s, fmt1) or try_strptime(s, fmt2) or try_strptime(s, fmt3) ...。您甚至可以将其泛化,使formats参数成为单个字符串或列表,并对其进行迭代... try_strptime(s, [fmt1, fmt2, fmt3, ...]) - smci
我将你的答案概括为下面的我的回答,我认为它更好... - smci

5
不,您所要求的不是Python的惯用语,因此通常不会有像这样丢弃错误的函数在标准库中。相关的标准库模块在此处记录:http://docs.python.org/library/datetime.htmlhttp://docs.python.org/library/time.html。所有解析函数都会在无效输入时引发异常。然而,正如其他答案所述,为您的应用程序构建一个不太困难(您的问题被措辞为“使用Python”,而不是“在Python标准库中”,因此不清楚协助编写此类函数“使用Python”是否回答了问题)。

枚举可能抛出的异常的最佳方法是什么? - qneill
我怀疑这是不可能的。如果你想捕获所有异常(几乎所有,也请参阅 BaseException),请使用 except Exception: - Eli Stevens
2
@qneill 你的意思是在同一行中捕捉多个异常以相同的方式对待它们吗? except ValueError, KeyboardError: (py3); except (ValueError, KeyboardError): (py2) - Natacha

3

暴力破解也是一种选择:

def TryParse(datestring, offset):
    nu = datetime.datetime.now()
    retval = nu
    formats = ["%d-%m-%Y","%Y-%m-%d","%d-%m-%y","%y-%m-%d"]  

    if datestring == None:
        retval = datetime.datetime(nu.year,nu.month,nu.day,0,0,0) - datetime.timedelta(offset,0,0,0,0,0,0)
    elif datestring == '':
        retval = datetime.datetime(nu.year,nu.month,nu.day,0,0,0) - datetime.timedelta(offset,0,0,0,0,0,0) 
    else:
        succes = False
        for aformat in formats:
            try:
                retval = datetime.datetime.strptime(datestring,aformat)
                succes = True
                break
            except:
                pass
        if not succes:
            retval = datetime.datetime(nu.year,nu.month,nu.day,0,0,0) - datetime.timedelta(offset,0,0,0,0,0,0) 
    return retval

你可以轻松地重构减去 datetime.timedelta(offset,0,0,0,0,0,0) 到梯子底部。实际上,这更适合于一个帮助函数,return retval 只需要一行代码就能实现。 - smci

0

使用time.strptime从字符串中解析日期。

文档:http://docs.python.org/library/time.html#time.strptime

示例来自:http://pleac.sourceforge.net/pleac_python/datesandtimes.html

#----------------------------- 
# Parsing Dates and Times from Strings

time.strptime("Tue Jun 16 20:18:03 1981")
# (1981, 6, 16, 20, 18, 3, 1, 167, -1)

time.strptime("16/6/1981", "%d/%m/%Y")
# (1981, 6, 16, 0, 0, 0, 1, 167, -1)
# strptime() can use any of the formatting codes from time.strftime()

# The easiest way to convert this to a datetime seems to be; 
now = datetime.datetime(*time.strptime("16/6/1981", "%d/%m/%Y")[0:5])
# the '*' operator unpacks the tuple, producing the argument list.

1
我不想打负分(抱歉...),但我之所以说相当于**TryParse**而不是Parse,是有原因的... - user541686
6
“不,没有对应的词汇”这个回答是否更有帮助? - FogleBird

0

我同意tryparse在C#中是非常有用的函数。不幸的是,在Python中没有直接等效的函数(也许是我不知道)。我相信你想检查一个字符串是否为日期,而不必担心日期格式。我的建议是使用 pandasto_datetime 函数:

def is_valid_date(str_to_validate: str) -> bool:
    try:
        if pd.to_datetime(str_to_validate):
            return True
        else:
            return False
    except ValueError:
        return False

-2

strptime怎么样?

http://docs.python.org/library/time.html#time.strptime

如果无法根据提供的格式解析字符串,它将抛出 ValueError 异常。

编辑:

由于问题在我回答后被编辑以包括关于异常的部分,我想添加一些注释。

正如其他答案所指出的那样,如果您不希望程序引发异常,您可以简单地捕获并处理它:

try:
    date = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
except ValueError:
    date = None

这是一种Pythonic的方式来实现你想要的。


1
我不想打负分(抱歉...),但我之所以说相当于**TryParse**而不是Parse,是有原因的... - user541686

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