Skip to content

fix(list): include full end day in 'Assigned on' between filter - #3597

Open
amaanJvd wants to merge 1 commit into
frappe:developfrom
amaanJvd:fix/assigned-on-between-day-boundary
Open

fix(list): include full end day in 'Assigned on' between filter#3597
amaanJvd wants to merge 1 commit into
frappe:developfrom
amaanJvd:fix/assigned-on-between-day-boundary

Conversation

@amaanJvd

Copy link
Copy Markdown

Summary

The between operator in apply_datetime_filter bounded a Datetime column with the raw filter values, so a date-only upper bound was treated as midnight. This drops results on the end day and returns nothing for a single-day range.

helpdesk/api/doc.py:

elif operator == "between":
    if isinstance(value, list) and len(value) == 2:
        query = query.where(field >= value[0]).where(field <= value[1])   # date-only bounds
elif operator == "timespan":
    ...
    start_dt = get_datetime(str(start)).replace(hour=0, minute=0, second=0)
    end_dt   = get_datetime(str(end)).replace(hour=23, minute=59, second=59)  # full day
    query = query.where(field >= start_dt).where(field <= end_dt)

The sibling timespan branch already normalizes to the full start/end day ("convert to datetime to include full start and end day"); the between branch does not.

Where it bites

__assigned_on (label "Assigned on") is a filterable Date field, so the list UI offers the Between operator. Its value flows through handle_assigned_on_filterapply_datetime_filter(query, ToDo.creation, ["between", [start, end]]). Because ToDo.creation is a Datetime column:

  • field <= "2026-07-02" means <= 2026-07-02 00:00:00, so anything assigned that day after midnight is excluded; and
  • selecting a single day (start == end) yields >= 2026-07-02 00:00:00 AND <= 2026-07-02 00:00:00, matching only the exact-midnight instant — effectively no results.

Fix

Normalize the bounds to the start and end of the day, mirroring the timespan branch:

elif operator == "between":
    if isinstance(value, list) and len(value) == 2:
        from frappe.utils import get_datetime

        start_dt = get_datetime(str(value[0])).replace(hour=0, minute=0, second=0)
        end_dt = get_datetime(str(value[1])).replace(hour=23, minute=59, second=59)
        query = query.where(field >= start_dt).where(field <= end_dt)

Relationship to #3518

This is the same "date Between returns no results" class reported in #3518. Note #3518 is specifically about "Created On", which filters through frappe.get_list directly (a different path that normalizes datetime between bounds itself), so this change does not by itself close #3518 — but it fixes the same failure mode on the "Assigned on" filter, which is entirely helpdesk-side. Cross-referencing for context.

Verification

I don't have a bench site to run the integration suite, so this is verified by inspection: the change mirrors the existing, already-shipped normalization in the timespan branch of the same function. Happy to add a unit test for apply_datetime_filter if you'd like one.

apply_datetime_filter's `between` branch bounded a Datetime column
(ToDo.creation) with the raw date values, so a date-only upper bound
like "2026-07-02" was treated as 2026-07-02 00:00:00. Any ToDo created
on the end day after midnight was excluded, and selecting a single day
(start == end) returned nothing.

Normalize the bounds to the start and end of the day, mirroring the
sibling `timespan` branch which already does this ("convert to datetime
to include full start and end day"). This is reached from the
"Assigned on" list filter (a Date field offering the Between operator)
via handle_assigned_on_filter.

Same date-Between-returns-nothing class as frappe#3518. Refs frappe#3518.
@mergify

mergify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

Safe to merge; the core fix is correct and the only gap is a microsecond edge case on the upper bound.

The between branch now correctly normalizes date-only bounds to include the full end day. The one gap is that end_dt is capped at 23:59:59 without setting microsecond=999999, which could drop sub-second timestamps on the last day.

helpdesk/api/doc.py — the end_dt replace call

Reviews (1): Last reviewed commit: "fix(list): include full end day in 'Assi..." | Re-trigger Greptile

Comment thread helpdesk/api/doc.py
# "2026-07-02" is treated as 2026-07-02 00:00:00 and drops everything
# on the end day (and returns nothing when start and end are equal).
start_dt = get_datetime(str(value[0])).replace(hour=0, minute=0, second=0)
end_dt = get_datetime(str(value[1])).replace(hour=23, minute=59, second=59)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Using second=59 without setting microsecond=0 leaves sub-second timestamps (e.g. 23:59:59.500) potentially excluded if get_datetime ever returns a datetime with microseconds. Add microsecond=999999 to reliably cover the full end day.

Suggested change
end_dt = get_datetime(str(value[1])).replace(hour=23, minute=59, second=59)
end_dt = get_datetime(str(value[1])).replace(hour=23, minute=59, second=59, microsecond=999999)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: "Created On" date filter returns no results when using the "Between" operator.

2 participants