正则处理复杂日期需分三步:先精准提取(用命名分组匹配多格式)、再验证归一(转datetime校验合法性)、最后按需重组(strftime自由格式化)。

直接用正则处理复杂日期格式化,核心不是“匹配完就完事”,而是分三步走:先精准提取、再验证归一、最后按需重组。pandas 的 to_datetime 虽快,但遇到混合格式(比如同时有 "2023/05/01"、"01-May-2023"、"2023年5月1日")或含脏数据(空值、中文“未知”、越界日期 "9999-99-99")时容易报错或静默失效。这时候正则就是最可控的前置清洗工具。
精准提取:用分组捕获关键字段
不追求“一个正则通吃所有格式”,而是为每种常见变体写独立 pattern,并用括号分组提取年、月、日——这是后续统一处理的基础。import re
# 各格式分别定义,带命名分组更清晰
patterns = [
(r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})', 'yyyy-mm-dd'),
(r'(?P<day>\d{1,2})/(?P<month>\d{1,2})/(?P<year>\d{4})', 'dd/mm/yyyy'),
(r'(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<year>\d{4})', 'mm/dd/yyyy'),
(r'(?P<day>\d{1,2})[-\s]+(?P<month>[A-Za-z]+)[-\s]+(?P<year>\d{4})', 'dd Month yyyy'),
(r'(?P<month>[A-Za-z]+)[-\s]+(?P<day>\d{1,2})[,.、\s]*(?P<year>\d{4})', 'Month dd, yyyy'),
(r'(?P<year>\d{4})[年\s](?P<month>\d{1,2})[月\s](?P<day>\d{1,2})[日\s]*', 'yyyy年mm月dd日'),
]</day></month></year></year></day></month></year></month></day></year></day></month></year></month></day></day></month></year>
对每个字符串遍历 patterns,用 re.search() 尝试匹配,一旦成功就拿到命名组字典(如 {'year': '2023', 'month': '5', 'day': '1'}),不用再手动索引 .group(1)。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
验证与归一:转成标准日期对象再校验
光提取数字不够——“2023-02-30”或“2023-13-01”是非法的。必须用datetime.strptime() 做真实校验:
from datetime import datetime
def safe_parse_date(match_dict):
try:
# 统一转成 int,适配 str 月份(如 "May" → 5)
year = int(match_dict['year'])
month = int(match_dict['month']) if match_dict['month'].isdigit() else \
['jan', 'feb', 'mar', 'apr', 'may', 'jun',
'jul', 'aug', 'sep', 'oct', 'nov', 'dec'].index(
match_dict['month'][:3].lower()) + 1
day = int(match_dict['day'])
# 真实构造日期对象,自动拦截非法组合
return datetime(year, month, day)
except (ValueError, IndexError, KeyError):
return None # 校验失败,返回 None 表示该条无效
这一步把字符串“活”起来,既能过滤掉格式正确但语义错误的日期,又为后续排序、计算打下基础。
按需重组:用 format 字符串控制输出样式
有了标准的datetime 对象,格式化就完全自由了:
dt = safe_parse_date(extracted_dict)
if dt:
iso_str = dt.strftime('%Y-%m-%d') # 2023-05-01
cn_str = dt.strftime('%Y年%m月%d日') # 2023年05月01日
us_str = dt.strftime('%m/%d/%Y') # 05/01/2023
full_str = dt.strftime('%B %d, %Y') # May 01, 2023
不需要写多个替换正则,也不用拼接字符串;strftime 是经过充分测试的工业级方案,支持所有常见格式符号和本地化(配合 locale 模块)。
应对脏数据和多格式混杂的实操建议
- 先用
errors="coerce"过一遍pd.to_datetime(),快速标出 NaT 行,再聚焦这些样本做正则清洗 - 对含干扰字符的列(如
"date: 2023/05/01"或"'07/04/2020'"),先用.str.replace(r"[^0-9A-Za-z/\-\s,.、]", "", regex=True)清理非关键字符,再进正则匹配 - 若原始数据中日期作为分隔符(如列表里以
"Friday, April 8, 2022"开头),用re.fullmatch()判断是否整行匹配,避免误切内容中的类似片段 - 性能敏感场景:提前编译所有 pattern(
re.compile(...)),避免每次循环重复编译










