最直接方式是用Substring提取:18位身份证取Substring(6,8),15位补"19"后取Substring(6,6);须先校验长度,避免异常;勿用正则或DateTime.TryParseExact校验,提取后需ParseExact转换再格式化。

用 Substring 提取身份证号中的出生日期最直接
18位身份证号第7到第14位(索引6~13)固定为 YYYYMMDD 格式,直接截取最稳妥。别用正则或分割——没规律可言,反而引入容错负担。
-
Substring(6, 8)是唯一推荐方式,参数分别是起始索引(0开始)和长度,不是结束位置 - 必须先校验字符串长度是否为18,否则
Substring会抛出ArgumentOutOfRangeException - 不建议用
DateTime.TryParseExact验证截出来的8位是否合法日期——身份证可能含无效日期(如20000230),业务上应按规则提取,而非按日历逻辑校验
15位身份证号要补前缀再处理
老式15位身份证没有年份前两位,第7~12位是 YYMMDD。直接 Substring 会漏年份,必须补 "19"(1900–1999年发放)。
- 先判断长度:
id.Length == 15 ? id.Substring(6, 6) : id.Substring(6, 8) - 15位时拼成
"19" + id.Substring(6, 6),再转DateTime或保留字符串 - 注意:极少数15位证可能是1999年后换发的,但标准规则仍按19xx处理;若业务需区分,得依赖额外字段(如发证机关),不能靠号码本身
别在 ToString("yyyy-MM-dd") 上踩坑
提取出的8位字符串(如 "19950812")不是 DateTime 类型,直接调 ToString 会报错——这是常见类型误用。
- 想格式化输出?先解析:
DateTime.ParseExact(id.Substring(6, 8), "yyyyMMdd", null) - 解析失败说明不是有效日期(如
"20230230"),此时应返回null或抛自定义异常,而不是静默吞掉 - 高频场景(如批量解析)建议缓存
DateTimeFormatInfo.InvariantInfo,避免每次创建开销
正则只适合初筛,别拿来提日期
有人用 Regex.Match(id, @"\d{6}(\d{8})") 想“更灵活”,实际多此一举,还埋下隐患。
- 身份证号结构是国标强制的,不存在“可能有空格/分隔符”的情况;正则增加CPU开销且无收益
- 如果输入混杂了其他文本(如“身份证:11010119900307271X”),那该做的是一次性清洗,而不是让日期提取逻辑承担文本定位职责
- 真要匹配混合文本,请用
Regex.Match(text, @"[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]")先抓完整号,再对结果做Substring
真正麻烦的是15位与18位混用、末尾X大小写、以及前端传来的全角数字——这些不在号码结构里,得在进C#之前就清理干净。别让身份证日期提取函数背锅。


















