90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
"""Canonical period helpers shared by analytics report pages."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from calendar import monthrange
|
|
from datetime import date, datetime
|
|
|
|
|
|
def parse_iso_date(value) -> date | None:
|
|
if not value:
|
|
return None
|
|
if isinstance(value, datetime):
|
|
return value.date()
|
|
if isinstance(value, date):
|
|
return value
|
|
try:
|
|
return datetime.strptime(str(value)[:10], "%Y-%m-%d").date()
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
|
|
def parse_month(value) -> date | None:
|
|
if not value:
|
|
return None
|
|
if isinstance(value, datetime):
|
|
return value.date().replace(day=1)
|
|
if isinstance(value, date):
|
|
return value.replace(day=1)
|
|
try:
|
|
return datetime.strptime(str(value)[:7], "%Y-%m").date()
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
|
|
def month_end(value) -> date | None:
|
|
month_start = parse_month(value)
|
|
if not month_start:
|
|
return None
|
|
return month_start.replace(day=monthrange(month_start.year, month_start.month)[1])
|
|
|
|
|
|
def make_analysis_period(start, end=None, *, mode="range", label=None) -> dict:
|
|
start_date = parse_iso_date(start)
|
|
end_date = parse_iso_date(end) if end else start_date
|
|
if not start_date and end_date:
|
|
start_date = end_date
|
|
if not start_date or not end_date:
|
|
return {
|
|
"mode": mode,
|
|
"label": label or "全部資料",
|
|
"start_date": "",
|
|
"end_date": "",
|
|
"start_month": "",
|
|
"end_month": "",
|
|
"is_single_day": False,
|
|
"is_single_month": False,
|
|
}
|
|
if start_date > end_date:
|
|
start_date, end_date = end_date, start_date
|
|
|
|
start_month = start_date.strftime("%Y-%m")
|
|
end_month_value = end_date.strftime("%Y-%m")
|
|
is_single_day = start_date == end_date
|
|
is_single_month = start_month == end_month_value
|
|
if not label:
|
|
if is_single_day:
|
|
label = start_date.strftime("%Y-%m-%d")
|
|
elif is_single_month and start_date.day == 1 and end_date == month_end(start_date):
|
|
label = start_date.strftime("%Y 年 %m 月")
|
|
else:
|
|
label = f"{start_date:%Y-%m-%d} 至 {end_date:%Y-%m-%d}"
|
|
|
|
return {
|
|
"mode": mode,
|
|
"label": label,
|
|
"start_date": start_date.isoformat(),
|
|
"end_date": end_date.isoformat(),
|
|
"start_month": start_month,
|
|
"end_month": end_month_value,
|
|
"is_single_day": is_single_day,
|
|
"is_single_month": is_single_month,
|
|
}
|
|
|
|
|
|
def make_month_period(value, *, mode="month") -> dict:
|
|
start = parse_month(value)
|
|
if not start:
|
|
return make_analysis_period(None, mode=mode)
|
|
return make_analysis_period(start, month_end(start), mode=mode)
|