从字符串确定日期时间格式

Posted

技术标签:

【中文标题】从字符串确定日期时间格式【英文标题】:Determine datetime format from string 【发布时间】:2020-08-15 08:42:12 【问题描述】:

假设我有一些包含任意日期和时间信息的字符串,例如'2020-01-01T10:00:10.200', '2020-01-01 10:00', '2020-Jan-01'

通过尝试将其转换为datetime 对象来确保每个字符串确实包含此类信息是相对简单的:

from dateutil.parser import parse

def is_datetime(mystring):
    try: 
        parse(mystring)
        return True
    except ValueError:
        return False

如果知道日期时间格式,也很容易将字符串转换为datetime 对象:

import datetime
dt_1 = datetime.datetime.strptime('2020-01-01T10:00:10.200', '%Y-%m-%dT%H:%M:%S.%f')
dt_2 = datetime.datetime.strptime('2020-01-01 10:00', '%Y-%m-%d %H:%M')
dt_3 = datetime.datetime.strptime('2020-Jan-01', '%Y-%b-%d')

但是,有没有办法根据给定的日期时间字符串确定 日期时间格式? 例如:

get_dt_format('2020-01-01T10:00:10.200') # should return '%Y-%m-%dT%H:%M:%S.%f'
get_dt_format('2020-01-01 10:00') # should return '%Y-%m-%d %H:%M'
get_dt_format('2020-Jan-01') # should return '%Y-%b-%d'

我设法通过检查每个字符串与可能的日期时间格式的集合来做到这一点,直到找到匹配项,但有没有更好、更有效的方法来做到这一点?

简化的当前解决方案:

def _try_format(mystring, dt_format):
    try:
        datetime.datetime.strptime(mystring, dt_format)
        return True
    except ValueError:
        return False


def get_dt_format(mystring):
    possible_formats = ['%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%d %H:%M', '%Y-%b-%d'] # and many others
    for possible_format in possible_formats:
        if _try_format(mystring, possible_format):
            return possible_format
    return 'Cannot determine format for ' + mystring

谢谢!

【问题讨论】:

你可能会在正则表达式之间找到更便宜的并尝试解析到日期 也可以看看dateutil.parser.parse 例如,如何判断 01-01-2020 是日-月-年还是月-日-年? @Roy2012 对于超过 1 个可能的匹配,它会达到一个峰值并通知用户(或者为了简单起见,只达到第一个匹配)。 如果您真的想实现,请查看此库github.com/jeffreystarr/dateinfer 作为起点。使用 python3 导入失败,因此如果需要,请使用 this 修复 【参考方案1】:

我也一直在研究这个问题,我能找到的最佳解决方案是py-dateinfer(也有人在 cmets 中建议了这个库的前身)。

来自文档:

>>> import dateinfer
>>> dateinfer.infer(['Mon Jan 13 09:52:52 MST 2014', 'Tue Jan 21 15:30:00 EST 2014'])
'%a %b %d %H:%M:%S %Z %Y'
>>>

【讨论】:

【参考方案2】:

您可以根据需要使用正则表达式模块。我觉得你的方法有点难,因为你必须写出所有的可能性。

例如,如果您想捕捉像 2020-Jan-01 00:00 这样的日期

import re
date=2020-Jan-01 00:00

re.finditer(r"20\d\d-[a-z]3-[0123]\d \d\d:\d\d",date)

它将找到适合该模式的所有匹配项。该示例模式并非特定于日期,即它也可以匹配 2020-abc-39 99:99 但您可以找到更大的模式。或者您可以使用 %Y %M 键来查找它。

或者我有其他解决方案:

from itertools import permutations
import datetime
dates_str="20-Jan-2020 2000/May/06 11-Feb-2006 Mar-20-2013 We have an example about the date finding. You can write anything to here: 20/Jul/2020"

year_full="%Y"
month_abb="%b"
p_day="%d"      #1

for s_date in dates_str.split(" "):   #2
  for i in permutations([p_day,month_abb,year_full]):   #3
    for c in " -/.":   #4
      try:
        if datetime.datetime.strptime(s_date,f"c".join(list(i))):
          print(datetime.datetime.strptime(s_date,f"c".join(list(i))),"We found a date")
          break
      except:
        pass

让我解释一下这段代码...

#1 我在日期模块中写了表示年、月和日期的键。如果你愿意,你可以写一些其他的键来搜索。

#2 在这里,我们从字符串中获取所有单词。对于此代码,术语“单词”表示由空格 (" ") 分隔的字符

#3 for 循环很重要。在这里,我们尝试所有模式,这意味着一些日期信息,所有单词。该生成器包含以下项目:('%d', '%b', '%Y'), ('%d', '%Y', '%b'), ('%b', '%d' , '%Y'), ('%b', '%Y', '%d'), ('%Y', '%d', '%b'), ('%Y', '%b ', '%d')

#4 你想要关于日期的所有可能性,所以我们需要一些规范。日期可能在数字或任何位置之间有“.”、“-”、“/”或“”:2020 年 1 月 20 日或 20/Jan/2020。

为了捕捉这些日期,我在函数 join 中使用了一个 f 字符串。 最后,代码可以捕获任何模式中包含“/”、“-”或“”的日期。我们有 24 种模式,我想写这些模式:

'%d %b %Y', '%d-%b-%Y', '%d/%b/%Y', '%d.%b.%Y',

'%d %Y %b', '%d-%Y-%b', '%d/%Y/%b', '%d.%Y.%b',

'%b %d %Y', '%b-%d-%Y', '%b/%d/%Y', '%b.%d.%Y',

'%b %Y %d', '%b-%Y-%d', '%b/%Y/%d', '%b.%Y.%d',

'%Y %d %b', '%Y-%d-%b', '%Y/%d/%b', '%Y.%d.%b',

'%Y %b %d', '%Y-%b-%d', '%Y/%b/%d', '%Y.%b.%d'

但是这段代码有一些问题:

1-如果日期有 2012 年 1 月 5 日这样的空格,我不确定这段代码能否找到它。因为我们将字符串与空格分开。

2-我使用生成器来节省 RAM,但一个小问题是时间。对于很长的字符串,可能需要一段时间,因为我们为每个单词尝试每种模式。

3-我无法将这些循环写在一行中,因为我们需要一个 try/except 块,但该块不可写在一行中。

如果您发现在正则表达式中使用键“%Y”、“%d”来搜索日期,请通知我...

【讨论】:

是的,但格式可以是任何格式。这个想法是在不知道它是什么样子的情况下找到日期时间格式。

以上是关于从字符串确定日期时间格式的主要内容,如果未能解决你的问题,请参考以下文章

将日期转换为格式日、日月年

如何使用pyspark函数处理日期格式的T和Z

从字符串转换为日期时间时获取常规日期时间格式而不是纪元时间?

给定特定格式的日期,我如何确定以月为单位的年龄?

电脑如何修改日期格式

确定字符串格式是“2013 年 5 月 16 日”还是带有 Javascript 的 UNIX 时间戳