certflow.utils.date_utils 源代码
"""日期处理工具模块
提供全局统一的日期格式化和提取功能。
分层约束:本模块属 utils 层,**不得依赖 PySide6**,须保证在无 Qt
环境下可导入。原先内置的 QDate 构造/解析工具已上移至
``certflow.views.common.qdate_utils``,此处仅保留向后兼容垫片。
"""
from __future__ import annotations
import contextlib
from datetime import datetime
from typing import Any
import pandas as pd
# Qt 的最小有效日期(格里高利历在英美国家的起点),
# 也是 QDateEdit.setDate() 收到无效 QDate 时的“回退零日期”。
# 任何日期解析都绝不能静默回退到这个值。
QT_MIN_VALID_DATE = (1752, 9, 14)
[文档]
def safe_qdate(year: int, month: int, day: int) -> Any | None:
"""向后兼容垫片:转发至 ``views.common.qdate_utils.safe_qdate``
Deprecated:
新代码应直接从 ``certflow.views.common.qdate_utils`` 导入。
本垫片仅为兼容存量调用点保留,在无 PySide6 环境调用会抛 ImportError。
Args:
year: 年份
month: 月份 (1-12)
day: 日 (1-31,需符合该月实际天数)
Returns:
QDate 实例(有效),或 None(参数非法时)
"""
from certflow.views.common.qdate_utils import safe_qdate as _impl
return _impl(year, month, day)
[文档]
def parse_qdate(text: str, fmt: str = "yyyy-MM-dd") -> Any | None:
"""向后兼容垫片:转发至 ``views.common.qdate_utils.parse_qdate``
Deprecated:
新代码应直接从 ``certflow.views.common.qdate_utils`` 导入。
本垫片仅为兼容存量调用点保留,在无 PySide6 环境调用会抛 ImportError。
Args:
text: 日期字符串,如 "2026-07-01"
fmt: Qt 日期格式,默认 "yyyy-MM-dd"
Returns:
QDate 实例(有效),或 None(解析失败时)
"""
from certflow.views.common.qdate_utils import parse_qdate as _impl
return _impl(text, fmt)
# 支持的日期格式列表(按优先级排列)
_DATE_FORMATS = [
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d",
"%Y/%m/%d",
"%Y.%m.%d",
"%d-%m-%Y",
"%d/%m/%Y",
"%Y%m%d",
"%y%m%d",
]
def _is_empty_or_placeholder(date_value: Any) -> bool:
"""检查日期值是否为空值或占位符。"""
if date_value is None:
return True
s = date_value.strip() if isinstance(date_value, str) else str(date_value).strip()
return not s or s.lower() == "nan" or "【空白" in s
def _try_parse_date(value_str: str) -> str | None:
"""尝试用预定义格式列表解析日期字符串,返回 YYYY-MM-DD 或 None。"""
for fmt in _DATE_FORMATS:
try:
return datetime.strptime(value_str, fmt).strftime("%Y-%m-%d")
except ValueError:
continue
return None
def _is_already_formatted(value_str: str) -> bool:
"""检查字符串是否已经是 YYYY-MM-DD 格式。"""
return len(value_str) >= 10 and value_str[4] == "-" and value_str[7] == "-"
[文档]
def normalize_plan_date(date_value: Any) -> str | None:
"""将日期值统一格式化为 YYYY-MM-DD 标准格式(不带时分秒)。
全局统一的日期格式化方法,所有模块导入 plan_date 时均应调用此方法。
支持格式:
- datetime / pandas.Timestamp 对象
- 字符串: 2024-01-15 14:30:00, 2024-01-15, 2024/01/15,
2024.01.15, 15-01-2024, 15/01/2024, 20240115, 240115
Args:
date_value: 原始日期值,可以是 datetime、Timestamp、字符串或 None
Returns:
str | None: 格式化后的 YYYY-MM-DD 日期字符串,
如果值为空、无效或无法解析,返回 None
"""
if _is_empty_or_placeholder(date_value):
return None
with contextlib.suppress(Exception):
# datetime / Timestamp 对象
if isinstance(date_value, (datetime | pd.Timestamp)):
return date_value.strftime("%Y-%m-%d")
value_str = str(date_value).strip()
# 已是标准格式,直接返回
if _is_already_formatted(value_str):
return value_str[:10]
# 尝试预定义格式解析
result = _try_parse_date(value_str)
if result:
return result
# 兜底:含 '-' 分隔符则截取前 10 位
if "-" in value_str:
return value_str[:10]
return None