42 lines
1.3 KiB
Python
42 lines
1.3 KiB
Python
"""Asia/Shanghai 日切与时段桶。时间戳一律 UTC ms 入,上海出。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime, timedelta
|
||
from zoneinfo import ZoneInfo
|
||
|
||
_SH = ZoneInfo("Asia/Shanghai")
|
||
|
||
|
||
def _to_shanghai(ts_ms: int) -> datetime:
|
||
return datetime.fromtimestamp(int(ts_ms) / 1000.0, tz=_SH)
|
||
|
||
|
||
def shanghai_day(ts_ms: int) -> str:
|
||
"""上海自然日 YYYY-MM-DD。"""
|
||
return _to_shanghai(ts_ms).strftime("%Y-%m-%d")
|
||
|
||
|
||
def shanghai_bucket(ts_ms: int, bucket_minutes: int = 60) -> int:
|
||
"""
|
||
日内时段桶起点(分钟,0–1439)。
|
||
默认 60 → 0,60,120,...,1380(即小时 0–23)。
|
||
"""
|
||
if bucket_minutes <= 0:
|
||
raise ValueError("bucket_minutes must be > 0")
|
||
dt = _to_shanghai(ts_ms)
|
||
minutes = dt.hour * 60 + dt.minute
|
||
return (minutes // bucket_minutes) * bucket_minutes
|
||
|
||
|
||
def shanghai_bucket_hour(ts_ms: int) -> int:
|
||
"""0–23 小时桶。"""
|
||
return shanghai_bucket(ts_ms, 60) // 60
|
||
|
||
|
||
def rolling_day_start(anchor_ymd: str, days: int) -> str:
|
||
"""锚点日(含)往前 days-1 天的起始 YYYY-MM-DD。"""
|
||
d = datetime.strptime(anchor_ymd, "%Y-%m-%d").date()
|
||
start = d - timedelta(days=max(0, days - 1))
|
||
return start.strftime("%Y-%m-%d")
|