Files
ZNJJ-api-server/src/agent/clock.py
T

77 lines
2.5 KiB
Python
Raw Normal View History

"""可控时钟(作业 4-6)。
测试不依赖运行机器的当天时间:注入 ``FixedClock`` 即可固定
“当前时间”,校验相对时间、未来时间、边界。
"""
from datetime import datetime
from typing import Protocol
import re
class Clock(Protocol):
"""时钟协议:图节点与校验逻辑只依赖它获取当前时间。"""
def now(self) -> datetime:
...
class SystemClock:
"""真实时钟(生产环境)。"""
def now(self) -> datetime:
return datetime.now()
class FixedClock:
"""固定时钟(测试):now 永远返回构造时给定的时间。"""
def __init__(self, fixed: datetime):
self._fixed = fixed
def now(self) -> datetime:
return self._fixed
_FUTURE_MARKERS = ("明天", "后天", "未来", "等会", "稍后", "接下来")
_ABS_RE = re.compile(r"^\s*(\d{1,2})\s*点(?:\s*(\d{1,2})\s*分?)?\s*$")
_REL_MIN_RE = re.compile(r"(\d+)\s*分钟前")
_REL_HOUR_RE = re.compile(r"(?:半\s*小时|(\d+)\s*小时)前")
def validate_accident_time(value: str, now: datetime) -> bool:
"""校验事故时间字符串是否合法(可解析且不晚于 now)。
参考实现覆盖的常见口语形态(不依赖机器当天时间,now 由调用方注入):
- 相对时间:“刚才/刚刚”、“N 分钟前”、“N 小时前/半小时前”;
- 绝对时间:“11点”“11点45分”——按 now 的当天日期解析,
解析结果晚于 now(未来时间)判为非法;
- 含明显未来标记(“明天”“稍后”等)一律非法;
- 无法解析的形态返回 False,由调用方进入澄清,不得猜值。
已知限制:未覆盖“昨天上午”“上周五”等跨日表达,作业细化时补充。
"""
text = (value or "").strip()
if not text:
return False
if any(marker in text for marker in _FUTURE_MARKERS):
return False
if text in ("刚才", "刚刚"):
return True
m = _REL_MIN_RE.search(text)
if m:
return 0 <= int(m.group(1)) <= 24 * 60 # Sanity 上限,防荒谬输入
m = _REL_HOUR_RE.search(text)
if m:
hours = 1 if m.group(1) is None else int(m.group(1))
return 0 < hours <= 24
m = _ABS_RE.match(text)
if m:
hour, minute = int(m.group(1)), int(m.group(2) or 0)
if hour > 23 or minute > 59:
return False
candidate = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
return candidate <= now
return False