date_range.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. """按自然日(YYYY-MM-DD)解析为 [day_start, day_end) 的 create_time 筛选条件。"""
  2. from datetime import datetime, timedelta
  3. def current_month_bounds(now: datetime | None = None) -> tuple[datetime, datetime]:
  4. """当前自然月 [month_start, next_month_start),与服务器本地时间一致(项目约定 Asia/Shanghai)。"""
  5. if now is None:
  6. now = datetime.now()
  7. month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
  8. if now.month == 12:
  9. month_end = datetime(now.year + 1, 1, 1)
  10. else:
  11. month_end = datetime(now.year, now.month + 1, 1)
  12. return month_start, month_end
  13. def parse_create_time_day_bounds(apply_date: str | None) -> tuple[datetime, datetime] | None:
  14. if not apply_date or not str(apply_date).strip():
  15. return None
  16. try:
  17. day_start = datetime.strptime(str(apply_date).strip(), "%Y-%m-%d")
  18. return day_start, day_start + timedelta(days=1)
  19. except ValueError:
  20. return None
  21. def _parse_day_start(value: str | None) -> datetime | None:
  22. if not value or not str(value).strip():
  23. return None
  24. try:
  25. return datetime.strptime(str(value).strip(), "%Y-%m-%d")
  26. except ValueError:
  27. return None
  28. def parse_create_time_range_bounds(
  29. apply_date: str | None = None,
  30. apply_date_start: str | None = None,
  31. apply_date_end: str | None = None,
  32. ) -> tuple[datetime | None, datetime | None]:
  33. """解析申请时间筛选为 [inclusive_start, exclusive_end),任一端可为 None 表示不限制。"""
  34. if apply_date_start or apply_date_end:
  35. start = _parse_day_start(apply_date_start)
  36. end_day = _parse_day_start(apply_date_end)
  37. end = end_day + timedelta(days=1) if end_day else None
  38. if start is None and end is None:
  39. return None, None
  40. return start, end
  41. single = parse_create_time_day_bounds(apply_date)
  42. if single:
  43. return single[0], single[1]
  44. return None, None
  45. def build_create_time_filters(
  46. column,
  47. apply_date: str | None = None,
  48. apply_date_start: str | None = None,
  49. apply_date_end: str | None = None,
  50. ) -> list:
  51. """生成 create_time 区间筛选条件列表。"""
  52. start, end = parse_create_time_range_bounds(apply_date, apply_date_start, apply_date_end)
  53. filters: list = []
  54. if start is not None:
  55. filters.append(column >= start)
  56. if end is not None:
  57. filters.append(column < end)
  58. return filters
  59. def resolve_apply_create_time(apply_date: str | None, now: datetime | None = None) -> datetime:
  60. """申请日 YYYY-MM-DD → create_time;当天用当前时刻,其它日期用当日 0 点。"""
  61. if now is None:
  62. now = datetime.now()
  63. bounds = parse_create_time_day_bounds(apply_date)
  64. if not bounds:
  65. return now
  66. day_start, _ = bounds
  67. if day_start.date() == now.date():
  68. return now
  69. return day_start