dateutil
解析器可以很好地从各种来源正确猜测日期和时间。
我们正在处理文件,其中每个文件只使用一种日期/时间格式,但格式因文件而异。分析显示dateutil.parser.parse
使用了大量时间。由于每个文件只需要确定一次,因此每次实现不猜测格式的内容都可以加快速度。
我事先并不知道格式,我仍然需要推断格式。类似的东西:
from MysteryPackage import date_string_to_format_string
import datetime
# e.g. mystring = '1 Jan 2016'
myformat = None
...
# somewhere in a loop reading from a file or connection:
if myformat is None:
myformat = date_string_to_format_string(mystring)
# do the usual checks to see if that worked, then:
mydatetime = datetime.strptime(mystring, myformat)
有这样的功能吗?
答案 0 :(得分:9)
这是一个棘手的问题。我的方法使用正则表达式和(?(DEFINE)...)
语法,只有较新的 regex
模块支持该语法。
DEFINE
让我们在匹配它们之前定义子程序,所以首先我们为日期猜测函数定义所有需要的块:
(?(DEFINE)
(?P<year_def>[12]\d{3})
(?P<year_short_def>\d{2})
(?P<month_def>January|February|March|April|May|June|
July|August|September|October|November|December)
(?P<month_short_def>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
(?P<day_def>(?:0[1-9]|[1-9]|[12][0-9]|3[01]))
(?P<weekday_def>(?:Mon|Tue|Wednes|Thurs|Fri|Satur|Sun)day)
(?P<weekday_short_def>Mon|Tue|Wed|Thu|Fri|Sat|Sun)
(?P<hms_def>\d{2}:\d{2}:\d{2})
(?P<hm_def>\d{2}:\d{2})
(?P<ms_def>\d{5,6})
(?P<delim_def>([-/., ]+|(?<=\d|^)T))
)
# actually match them
(?P<hms>^(?&hms_def)$)|(?P<year>^(?&year_def)$)|(?P<month>^(?&month_def)$)|(?P<month_short>^(?&month_short_def)$)|(?P<day>^(?&day_def)$)|
(?P<weekday>^(?&weekday_def)$)|(?P<weekday_short>^(?&weekday_short_def)$)|(?P<hm>^(?&hm_def)$)|(?P<delim>^(?&delim_def)$)|(?P<ms>^(?&ms_def)$)
""", re.VERBOSE)
在此之后,我们需要考虑可能的分隔符:
# delim
delim = re.compile(r'([-/., ]+|(?<=\d)T)')
格式映射:
# formats
formats = {'ms': '%f', 'year': '%Y', 'month': '%B', 'month_dec': '%m', 'day': '%d', 'weekday': '%A', 'hms': '%H:%M:%S', 'weekday_short': '%a', 'month_short': '%b', 'hm': '%H:%M', 'delim': ''}
函数GuessFormat()
在分隔符的帮助下拆分部分,尝试匹配它们并输出strftime()
的相应代码:
def GuessFormat(datestring):
# define the bricks
bricks = re.compile(r"""
(?(DEFINE)
(?P<year_def>[12]\d{3})
(?P<year_short_def>\d{2})
(?P<month_def>January|February|March|April|May|June|
July|August|September|October|November|December)
(?P<month_short_def>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
(?P<day_def>(?:0[1-9]|[1-9]|[12][0-9]|3[01]))
(?P<weekday_def>(?:Mon|Tue|Wednes|Thurs|Fri|Satur|Sun)day)
(?P<weekday_short_def>Mon|Tue|Wed|Thu|Fri|Sat|Sun)
(?P<hms_def>T?\d{2}:\d{2}:\d{2})
(?P<hm_def>T?\d{2}:\d{2})
(?P<ms_def>\d{5,6})
(?P<delim_def>([-/., ]+|(?<=\d|^)T))
)
# actually match them
(?P<hms>^(?&hms_def)$)|(?P<year>^(?&year_def)$)|(?P<month>^(?&month_def)$)|(?P<month_short>^(?&month_short_def)$)|(?P<day>^(?&day_def)$)|
(?P<weekday>^(?&weekday_def)$)|(?P<weekday_short>^(?&weekday_short_def)$)|(?P<hm>^(?&hm_def)$)|(?P<delim>^(?&delim_def)$)|(?P<ms>^(?&ms_def)$)
""", re.VERBOSE)
# delim
delim = re.compile(r'([-/., ]+|(?<=\d)T)')
# formats
formats = {'ms': '%f', 'year': '%Y', 'month': '%B', 'month_dec': '%m', 'day': '%d', 'weekday': '%A', 'hms': '%H:%M:%S', 'weekday_short': '%a', 'month_short': '%b', 'hm': '%H:%M', 'delim': ''}
parts = delim.split(datestring)
out = []
for index, part in enumerate(parts):
try:
brick = dict(filter(lambda x: x[1] is not None, bricks.match(part).groupdict().items()))
key = next(iter(brick))
# ambiguities
if key == 'day' and index == 2:
key = 'month_dec'
item = part if key == 'delim' else formats[key]
out.append(item)
except AttributeError:
out.append(part)
return "".join(out)
最后的测试:
import regex as re
datestrings = [datetime.now().isoformat(), '2006-11-02', 'Thursday, 10 August 2006 08:42:51', 'August 9, 1995', 'Aug 9, 1995', 'Thu, 01 Jan 1970 00:00:00', '21/11/06 16:30',
'06 Jun 2017 20:33:10']
# test
for dt in datestrings:
print("Date: {}, Format: {}".format(dt, GuessFormat(dt)))
这会产生:
Date: 2017-06-07T22:02:05.001811, Format: %Y-%m-%dT%H:%M:%S.%f
Date: 2006-11-02, Format: %Y-%m-%d
Date: Thursday, 10 August 2006 08:42:51, Format: %A, %m %B %Y %H:%M:%S
Date: August 9, 1995, Format: %B %m, %Y
Date: Aug 9, 1995, Format: %b %m, %Y
Date: Thu, 01 Jan 1970 00:00:00, Format: %a, %m %b %Y %H:%M:%S
Date: 21/11/06 16:30, Format: %d/%m/%d %H:%M
Date: 06 Jun 2017 20:33:10, Format: %d %b %Y %H:%M:%S
答案 1 :(得分:7)
我没有现成的解决方案,但这是一个非常棘手的问题,因为dateutil已花费了太多的脑力时间,而不是试图取代它,我&# 39;提出一种融合它的方法:
由于您声明&#34;每个文件只使用一种日期/时间格式&#34;,这种方法应该有效(假设您在每个文件中有不同的日期,以便通过比较多个日期可以解决mm / dd歧义值)。
答案 2 :(得分:3)
您可以编写自己的解析器:
import datetime
class DateFormatFinder:
def __init__(self):
self.fmts = []
def add(self,fmt):
self.fmts.append(fmt)
def find(self, ss):
for fmt in self.fmts:
try:
datetime.datetime.strptime(ss, fmt)
return fmt
except:
pass
return None
您可以按如下方式使用它:
>>> df = DateFormatFinder()
>>> df.add('%m/%d/%y %H:%M')
>>> df.add('%m/%d/%y')
>>> df.add('%H:%M')
>>> df.find("01/02/06 16:30")
'%m/%d/%y %H:%M'
>>> df.find("01/02/06")
'%m/%d/%y'
>>> df.find("16:30")
'%H:%M'
>>> df.find("01/02/06 16:30")
'%m/%d/%y %H:%M'
>>> df.find("01/02/2006")
然而,事情并非如此简单,因为日期可能含糊不清,如果没有某些背景,就无法确定其格式。
>>> datetime.strptime("01/02/06 16:30", "%m/%d/%y %H:%M") # us format
datetime.datetime(2006, 1, 2, 16, 30)
>>> datetime.strptime("01/02/06 16:30", "%d/%m/%y %H:%M") # european format
datetime.datetime(2006, 2, 1, 16, 30)
答案 3 :(得分:0)
您可以尝试dateinfer模块。非常好,可以轻松处理所有案件。尽管如果您需要更好地控制代码并准备从头开始编写代码,则regex确实是最好的选择。