【问题标题】:Invalid dates parser无效的日期解析器
【发布时间】:2021-12-11 22:33:45
【问题描述】:

有没有一种简单的方法可以将“11 月 31 日”等无效日期减少到该月的最后一个有效日期? 11 月 31 日不存在,因为 11 月没有 31 天。

我正在使用的日期字符串非常混乱且不一致,因此我想避免尝试对字符串进行切片或类似的事情。当日期不无效时,Parser.parse() 非常适合我的用例。

from dateutil import parser

datstrings_list = ["Nov 31, 1976", "11/31/76", "11/31/1976", "November 31st, 1976","1/32/1976"]

date_list = []

for i in datestrings_list:

    datestring = i

    date = parser.parse(datestring).date()

    date_list.append(date)


创建错误:


ParserError: day is out of range for month: Nov 31, 1976

date_list 的期望值:


[datetime.date(1976, 11, 30), datetime.date(1976, 11, 30),datetime.date(1976, 11, 30),datetime.date(1976, 11, 30), datetime.date(1/31/1976)]

【问题讨论】:

  • 很好奇,为什么你的日期无效?
  • 杂乱无章的数据超出我的控制——日期是在代码过程之外提供的。
  • 数据有多“不一致”?正则表达式可能会有所帮助:re.findall(r"\d+", "Nov 31, 1976") 返回 ['31', '1976']
  • 在try里面做一下,抓到错误,然后改值再尝试解析?
  • 日期有各种不同的格式 -- m/d/yy -- mm/dd/yyyy -- 像在代码中一样写出来 -- 可能有一种方法可以做很多正则表达式在众多尝试语句中,但我希望有更好的解决方案。

标签: python string date parsing


【解决方案1】:

您可以使用while 循环并以这种方式完成它。

from dateutil import parser

datestring = "Nov 31, 1976"
date = None
while date is None:
    date_array = datestring.split()
    
    try:
        date = parser.parse(datestring).date()
    except parser._parser.ParserError:
        day = int("".join(x for x in date_array[1] if x.isdigit()))-1
        date_array[1] = f"{day},"
        datestring = f"{date_array[0]} {date_array[1]} {date_array[2]}"

print(date)

这应该可以满足您的需求。

问题更新:

from dateutil import parser
import calendar

datestrings_list = ["Nov 31, 1976", "11/31/76", "11/31/1976", "November 31st, 1976","1/32/1976"]
c = {month: index for index, month in enumerate(calendar.month_abbr) if month}

# Format string list
def standardise_list(date_list):
    lst = []
    for index, ls in enumerate(date_list):
        if "/" not in ls:
            ds = ls.split()
            if len(ds[0]) > 3:
                ds[0] = ds[0][:3]

            if len(ds[1]) > 2:
                ds[1] = ds[1][:2]

            nd = f"{c[ds[0]]}/{ds[1]}/{ds[2]}"
            lst.append(nd)
        else:
            lst.append(ls)
    return lst

# Fix out of range dates
def date_fix(datestring):
    date = None
    while date is None:
        date_array = datestring.split("/")
        
        try:
            date = parser.parse(datestring).date()
        except parser._parser.ParserError:
            day = int("".join(x for x in date_array[1] if x.isdigit()))-1
            date_array[1] = f"{day}"
            datestring = f"{date_array[0]} {date_array[1]} {date_array[2]}"
    return date

standard_string_list = standardise_list(datestrings_list)

dates = [date_fix(ds) for ds in standard_string_list]
print(dates)

>>> [datetime.date(1976, 11, 30), datetime.date(1976, 11, 30), datetime.date(1976, 11, 30), datetime.date(1976, 11, 30), datetime.date(1976, 1, 31)]

【讨论】:

  • 抱歉,请查看我的帖子的更新——我添加了一个列表来显示代码需要处理的所有日期格式——我不确定您的解决方案是否适用于更新。
  • 查看修改后的解决方案。
  • 很酷,谢谢——让我测试一下,如果可行,我将其标记为答案。
  • 太好了,非常感谢!
  • 很高兴来到这里。一切顺利。
【解决方案2】:

我不确定是否有一种简单的方法可以将无效日期替换为该月的最大天数,但一种方法可能是使用 calendar 模块下的辅助函数来获取给定月份的最大天数,并且年份:

import calendar

# Mapping of month abbreviation to month index. Ex: 'Jan': 1
month_indices = {month: i for i, month in enumerate(calendar.month_abbr)}

datestring = "Nov 31, 1976"

month_abbr, day, yr = datestring.replace(',', '').split()
last_day_in_month = calendar.monthrange(int(yr), month_indices[month_abbr])[-1]

assert last_day_in_month == 30

或者,如果您有像 11/31/76 这样的日期字符串,则可以通过以下方式获取本月和本年的最大天数:

datestring = "11/31/76"

month, day, year = map(int, datestring.split('/'))
# checking if we have abbreviation like `76` for year
# we have to make sure the year has 4 digits, otherwise `monthrange`
# appears to parse the year as `2076`, which is not what we want.
if year < 100:
    year += 1900

assert calendar.monthrange(year, month)[-1] == 30

【讨论】:

  • 谢谢你——你能看看我的更新吗?我添加了一个列表,其中显示了日期可能出现的所有不同格式——您的解决方案在更新后的情况下是否有效?
  • 不,它不适用于其他情况。我认为最好的选择是使用正则表达式从给定的字符串中解析出月、日和年,然后检查给定的日期是否对该月有效-尽管如果您确定大多数日期都是有效的,您可能只是尝试解析它们,然后捕获错误,然后如上所述更改日期。
  • @DataofAllKinds 我更新了以展示如何像上面的其他示例之一那样处理字符串。尽管您可能需要使用正则表达式或某种验证来确定您作为输入接收的字符串的格式。
猜你喜欢
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多