guessIssueDate function

IssueDateGuess? guessIssueDate({
  1. required DocumentStandardType type,
  2. required String line1,
  3. required String line2,
  4. String? line3,
  5. bool isVisaMRVB = false,
  6. String? birthYYMMDD,
  7. String? expiryYYMMDD,
})

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;
}