Python 从单个字符串中分离两个日期时间值
我需要编写一个方法来接收包含两个datetime值的字符串,并将这些值分离出来。这些日期时间值可以是任何有效的ISO-8601格式,这意味着我不能只在字符索引上拆分。这些值将用连字符分隔,这也意味着我不能只使用str.split() 我已经使用一些Reg-Ex编写了这个函数,但是客户端要求我使用python dateutilPython 从单个字符串中分离两个日期时间值,python,datetime,python-dateutil,Python,Datetime,Python Dateutil,我需要编写一个方法来接收包含两个datetime值的字符串,并将这些值分离出来。这些日期时间值可以是任何有效的ISO-8601格式,这意味着我不能只在字符索引上拆分。这些值将用连字符分隔,这也意味着我不能只使用str.split() 我已经使用一些Reg-Ex编写了这个函数,但是客户端要求我使用python dateutil def split_范围(次): regex=re.compile(“[0-9]{4}-?[0-9]{2}-?[0-9]{2}([T]([0-9]{2}:?){2,3}(\
def split_范围(次):
regex=re.compile(“[0-9]{4}-?[0-9]{2}-?[0-9]{2}([T]([0-9]{2}:?){2,3}(\[0-9]{3})?)?Z?”)
split_times=regex.finditer(次)
最终_次=[]
对于拆分时间中的时间:
时间=时间。组(0)
datetime_值=datetime.fromisoformat(时间)
final_times.append(datetime_value.isoformat())
返回最后的\u次
此函数应接收如下字符串:
(这些是我在测试中使用的所有字符串)
200809-20080815
2008-08-08-2008-08-09
2008-08-08T17:21-2008-08-09T17:31
2008-08-08T17:21-2008-08-09T17:31
2008-08-08T17:21:000-2008-08-09T17:31:000
2008-08-08T17:21:000-2008-08-09T17:310:00
2008-08-08T17:21:000.000-2008-08-09T17:31:000.000
并将其拆分为两个单独的值
例如2019-08-08
和2019-08-09
客户不太喜欢这里使用正则表达式,希望我用dateutil替换它,但我还没有看到它能满足我的需要。是否有一个dateutil方法可以用来实现这一点,如果没有,是否还有另一个库具有某种功能?使用re.findall()
输出:
['2019-08-03', '2019-08-09']
['2019-08-03', '2019-08-09', '2017-01-01']
例如:
import re
text = "2019-08-03-2019-08-09xxxxxThis is test xxxxx---2017-01-01"
match = re.findall(r'\d{4}-\d{2}-\d{2}', text)
print (match)
输出:
['2019-08-03', '2019-08-09']
['2019-08-03', '2019-08-09', '2017-01-01']
使用re.findall()
输出:
['2019-08-03', '2019-08-09']
['2019-08-03', '2019-08-09', '2017-01-01']
例如:
import re
text = "2019-08-03-2019-08-09xxxxxThis is test xxxxx---2017-01-01"
match = re.findall(r'\d{4}-\d{2}-\d{2}', text)
print (match)
输出:
['2019-08-03', '2019-08-09']
['2019-08-03', '2019-08-09', '2017-01-01']
我认为最好的办法可能是要求您的客户将分隔符从
-
更改为其他类似空格、制表符或不会在ISO 8601字符串中显示并拆分的内容,但如果您必须使用-
作为分隔符,并且必须支持任何有效的ISO 8601字符串,最好的选择是尝试查找模式-(-|\d{4})
,因为所有有效的ISO 8601日期时间要么以4位数字开头,要么以--
开头。如果您发现一个破折号后跟4位数字,则表示您找到了一个负时区或下一个ISO 8601日期时间的开始
此外,没有有效的ISO 8601日期时间格式包含\d{4}-\d{4}
,如果您发现表示时区偏移的-(\d{4})
,则它必须位于第一个ISO 8601字符串的末尾,因此使用负前瞻就足以确保模式不会重复,因此,总而言之:
import re
from dateutil.parser import isoparse
def parse_iso8601_pairs(isostr):
# In a string containing two ISO 8601 strings delimited by -, the substring
# "-\d{4}" is only found at the beginning of the second datetime or the
# end of *either* datetime. If it is found at the end of the first datetime,
# it will always be followed by `-\d{4}`, so we can use negative lookahead
# to find the beginning of the next string.
#
# Note: ISO 8601 datetimes can also begin with `--`, but parsing these is
# not supported yet in dateutil.parser.isoparse, as of verison 2.8.0. The
# regex includes this type of string in order to make at least the splitting
# method work even if the parsing method doesn't support "missing year"
# ISO 8601 strings.
m = re.search(r"-(--|\d{4})(?!-(--|\d{4}))", isostr)
dt1 = None
dt2 = None
if m is None:
raise ValueError(f"String does not contain two ISO 8601 datetimes " +
"delimited by -: {isostr}")
split_on = m.span()[0]
str1 = isostr[0:split_on]
str2 = isostr[split_on + 1:]
# You may want to wrap the error handling here with a nicer message
dt1 = isoparse(str1)
dt2 = isoparse(str2)
return dt1, dt2
据我所知,这将适用于由-
分隔的任何一对符合ISO 8601的字符串,但模糊的“年份缺失”格式除外:--MM-?DD
。代码的拆分部分即使在字符串(如--04-01
)中也可以工作,但当前不支持该格式,因此解析将失败。可能更成问题的是,--MMDD
也是一种有效的ISO8601格式,它将匹配-\d{4}
,并给出错误的拆分。如果您想支持这种格式,并且有一个经过修改的解析器可以处理--MMDD
,我相信您可以制作一个更复杂的正则表达式来处理--MMDD
的情况(如果有人想这样做,我很乐意将其编辑到文章中),或者您可以简单地“猜测并检查”通过使用re.finditer
对匹配项进行迭代,直到找到拆分字符串的位置,从而在分隔符的两侧生成有效的ISO 8601日期时间
注意:如果将
dateutil.parser.isoparse
替换为datetime.datetime.fromisoformat
,此方法也会起作用。不同之处在于,datetime.datetime.fromisoformat
解析的字符串主要是dateutil.parser.isoparse
处理的字符串的子集-它与datetime.datetime.isoformat
相反,并将解析通过调用datetime对象上的isoformat
方法可以创建的任何内容,其中,seisoparse
用于解析任何有效的ISO 8601字符串。如果您知道日期时间是通过调用isoformat()
方法生成的,然后,ISO8601解析器的更好选择是来自ISOFORMAT
。我认为最好的办法是让您的客户机将分隔符从-
更改为其他类似空格、制表符或不会显示在ISO8601字符串中的内容,然后在此基础上进行拆分,但是,如果必须使用-
作为分隔符,并且必须支持任何有效的ISO 8601字符串,那么最好的选择是尝试查找模式-(-\d{4})
,因为所有有效的ISO 8601日期时间要么以4位数字开头,要么以-
开头。如果您发现一个破折号后跟4位数字,则表示您找到了一个负时区或下一个ISO 8601日期时间的开始
此外,没有有效的ISO 8601日期时间格式包含\d{4}-\d{4}
,如果您发现表示时区偏移的-(\d{4})
,则它必须位于第一个ISO 8601字符串的末尾,因此使用负前瞻就足以确保模式不会重复,因此,总而言之:
import re
from dateutil.parser import isoparse
def parse_iso8601_pairs(isostr):
# In a string containing two ISO 8601 strings delimited by -, the substring
# "-\d{4}" is only found at the beginning of the second datetime or the
# end of *either* datetime. If it is found at the end of the first datetime,
# it will always be followed by `-\d{4}`, so we can use negative lookahead
# to find the beginning of the next string.
#
# Note: ISO 8601 datetimes can also begin with `--`, but parsing these is
# not supported yet in dateutil.parser.isoparse, as of verison 2.8.0. The
# regex includes this type of string in order to make at least the splitting
# method work even if the parsing method doesn't support "missing year"
# ISO 8601 strings.
m = re.search(r"-(--|\d{4})(?!-(--|\d{4}))", isostr)
dt1 = None
dt2 = None
if m is None:
raise ValueError(f"String does not contain two ISO 8601 datetimes " +
"delimited by -: {isostr}")
split_on = m.span()[0]
str1 = isostr[0:split_on]
str2 = isostr[split_on + 1:]
# You may want to wrap the error handling here with a nicer message
dt1 = isoparse(str1)
dt2 = isoparse(str2)
return dt1, dt2
据我所知,这将适用于由-
分隔的任何一对符合ISO 8601的字符串,但模糊的“年份缺失”格式除外:--MM-?DD
。代码的拆分部分甚至可以在字符串(如--04-01
)中使用,但目前不支持该格式,因此pa