guessIssueDate function
Scan optional segments for a plausible issue date (YYMMDD), avoiding birth/expiry. Boost confidence if a following check digit matches.
Implementation
IssueDateGuess? guessIssueDate({
required DocumentStandardType type,
required String line1,
required String line2,
String? line3,
bool isVisaMRVB = false,
String? birthYYMMDD,
String? expiryYYMMDD,
}) {
final birth = (birthYYMMDD != null && RegExp(r'^\d{6}$').hasMatch(birthYYMMDD))
? _expandYYMMDD(birthYYMMDD)
: null;
final expiry = (expiryYYMMDD != null && RegExp(r'^\d{6}$').hasMatch(expiryYYMMDD))
? _expandYYMMDD(expiryYYMMDD, birth: birth)
: null;
final segs = _optionalSegments(type, line1, line2, line3: line3, isVisaMRVB: isVisaMRVB);
if (segs.isEmpty) return null;
IssueDateGuess? best;
double bestScore = -1;
for (final (seg, src) in segs) {
// find YYMMDD or YYMMDD + check digit
final re = RegExp(r'(\d{6})(\d)?');
for (final m in re.allMatches(seg)) {
final yymmdd = m.group(1)!;
// skip if equals known birth/expiry
if (birthYYMMDD == yymmdd || expiryYYMMDD == yymmdd) continue;
final expanded = _expandYYMMDD(yymmdd, birth: birth, expiry: expiry);
if (expanded == null) continue;
// base confidence
double score = 0.6;
// if followed by a digit that matches MRZ check of YYMMDD, boost a bit
final chk = m.group(2);
if (chk != null && chk == _mrzCheck(yymmdd).toString()) score += 0.2;
// closeness to expiry (issue is typically within 1–10 years before expiry)
if (expiry != null) {
final diffDays = expiry.difference(expanded).inDays;
if (diffDays >= 0 && diffDays <= 365 * 15) score += 0.1;
}
// later (more recent) issue date slightly preferred
score += expanded.millisecondsSinceEpoch / 1e15; // tiny tiebreaker
if (score > bestScore) {
bestScore = score;
best = IssueDateGuess(yymmdd, expanded, src, score.clamp(0.0, 1.0));
}
}
}
return best;
}