[Python] Date format I'm trying to extract from sentence to get the date range

Discussão em 'Python' iniciado por Stack, Julho 23, 2025.

  1. Stack

    Stack Membro Participativo

    I’m parsing natural‑language date filters in a chatbot. The input can be “15/09/2025”, “December 9 2025”, “15092025”, or just a year like “2025”. I also need to understand phrases like “after 2021”, “between 2019 and 2022”, “on or before 15/07/2025”.

    The snippet below does two things:

    Extracts date tokens with a regex that covers the formats above.

    Maps cue words (“after”, “before”, “between”, “from … to …”) to comparison operators and returns a dict shaped like {'Date_Raised': {'$gte': '2019-01-01', '$lte': '2022-01-01'}}.

    Ambiguous numeric dates (e.g., 09/10/2025) are resolved with a DAYFIRST flag; you can flip it for US format. Bare years are normalized to Jan 1 of that year.

    import re
    from datetime import date
    from dateutil import parser as dparser

    DAYFIRST = True # set False if you want MM/DD/YYYY bias
    FIELD = "Date_Raised" # target field name

    DATE_RX = re.compile(r"""
    (?:\d{8}) # 15092025
    | (?:\d{1,2}[/-]\d{1,2}[/-]\d{2,4}) # 15/09/2025 or 09-15-25
    | (?:\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*/\d{1,2}/\d{2,4}\b)
    | (?:\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\s+\d{1,2},?\s+\d{2,4}\b)
    | (?:\b(19|20)\d{2}\b) # bare year
    """, re.IGNORECASE | re.VERBOSE)

    OPS = [
    (r"\bbetween\b", "between"),
    (r"\bfrom\b", "from"),
    (r"\bto\b", "to"),
    (r"\bafter|greater than|later than|newer than|>\b", "gt"),
    (r"\bon or after|>=|since\b", "gte"),
    (r"\bbefore|less than|earlier than|older than|<\b", "lt"),
    (r"\bon or before|<=|till|until|upto?\b", "lte"),
    ]

    def _smart_numeric(tok: str, dayfirst: bool) -> date:
    if re.fullmatch(r"\d{8}", tok):
    d, m, y = int(tok[:2]), int(tok[2:4]), int(tok[4:])
    return date(y, m, d)

    if re.fullmatch(r"\d{1,2}[/-]\d{1,2}[/-]\d{2,4}", tok):
    a, b, c = map(int, re.split(r"[/-]", tok))
    if c < 100:
    c += 2000 if c <= 69 else 1900
    if a > 12 and b <= 12:
    d_, m_ = a, b
    elif b > 12 and a <= 12:
    d_, m_ = b, a
    else:
    d_, m_ = (a, b) if dayfirst else (b, a)
    return date(c, m_, d_)

    return dparser.parse(tok, dayfirst=dayfirst, fuzzy=True).date()

    def to_date(tok: str, dayfirst: bool = DAYFIRST) -> date:
    tok = tok.strip()
    if re.fullmatch(r"(19|20)\d{2}", tok):
    return date(int(tok), 1, 1)
    return _smart_numeric(tok, dayfirst)

    def _fmt(d: date) -> str:
    return d.strftime("%Y-%m-%d")

    def parse_date_condition(text: str, dayfirst: bool = DAYFIRST, field: str = FIELD):
    low = text.lower()
    toks = [m.group(0) for m in DATE_RX.finditer(text)]
    dates = [to_date(t, dayfirst) for t in toks]

    for pat, op in OPS:
    if re.search(pat, low):
    if op == "between":
    if len(dates) < 2:
    raise ValueError("Found 'between' but fewer than 2 dates.")
    lo, hi = sorted(dates[:2])
    return {field: {"$gte": _fmt(lo), "$lte": _fmt(hi)}}

    if op == "from":
    if len(dates) >= 2 and re.search(r"\bto\b", low):
    lo, hi = sorted(dates[:2])
    return {field: {"$gte": _fmt(lo), "$lte": _fmt(hi)}}
    return {field: {"$gte": _fmt(dates[0])}}

    if op == "to":
    return {field: {"$lte": _fmt(dates[-1])}}

    if not dates:
    raise ValueError("Comparator word present but no date.")
    mongo_map = {"gt": "$gt", "gte": "$gte", "lt": "$lt", "lte": "$lte"}
    return {field: {mongo_map[op]: _fmt(dates[0])}}

    if dates:
    return {field: {"$eq": _fmt(dates[0])}}
    return None

    Continue reading...

Compartilhe esta Página