Skip to content

Commit

Permalink
Refactor parseDateYYMMDD to handle future dates correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
smlu committed May 29, 2024
1 parent 52e97a0 commit fa50698
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 25 deletions.
21 changes: 14 additions & 7 deletions lib/src/extension/string_apis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,34 @@ extension StringDecodeApis on String {
}

extension StringYYMMDDateApi on String {
DateTime parseDateYYMMDD() {
DateTime parseDateYYMMDD({bool futureDate = false}) {
if (length < 6) {
throw FormatException("invalid length of compact date string");
throw FormatException("Invalid length of compact date string");
}

int y = int.parse(substring(0, 2)) + 2000;
int m = int.parse(substring(2, 4));
int d = int.parse(substring(4, 6));

// Sub 100 years from parsed year if greater than 10 years and 5 months from now.
final now = DateTime.now();
final tenYearsFromNow = now.year + 10;
if (y > tenYearsFromNow || (y == tenYearsFromNow && now.month + 5 < m)) {
final maxYear = now.year;
final maxMonth = now.month;
if (futureDate) {
maxYear += 20; // cut off year 20 years from now
maxMonth += 5;
}

// If parsed year is greater than max wind back for 100 years
if (y > maxYear || ( y == maxYear && maxMonth > m)) {
y -= 100;
}

return DateTime(y, m, d);
}

DateTime parseDate() {
DateTime parseDate({bool futureDate = false}) {
if (length == 6) {
return this.parseDateYYMMDD();
return this.parseDateYYMMDD(futureDate: futureDate);
} else {
return DateTime.parse(this);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/lds/df1/efdg12.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class EfDG12 extends DataGroup {

DateTime? get dateOfIssue => _dateOfIssue;
String? get issuingAuthority => _issuingAuthority;


EfDG12.fromBytes(Uint8List data) : super.fromBytes(data);

Expand Down
16 changes: 8 additions & 8 deletions lib/src/lds/mrz.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ class MRZ {
_docNum = _read(istream, 9);
final cdDocNum = _readWithPad(istream, 1);
_optData = _read(istream, 15);
dateOfBirth = _readDate(istream);
dateOfBirth = _readDate(istream, futureDate: false);

_assertCheckDigit(dateOfBirth.formatYYMMDD(), _readCD(istream),
"Data of Birth check digit mismatch");

gender = _read(istream, 1);
dateOfExpiry = _readDate(istream);
dateOfExpiry = _readDate(istream, futureDate: true);

_assertCheckDigit(dateOfExpiry.formatYYMMDD(), _readCD(istream),
"Data of Expiry check digit mismatch");
Expand Down Expand Up @@ -151,12 +151,12 @@ class MRZ {
final cdDocNum = _readWithPad(istream, 1);

nationality = _read(istream, 3);
dateOfBirth = _readDate(istream);
dateOfBirth = _readDate(istream, futureDate: false);
_assertCheckDigit(dateOfBirth.formatYYMMDD(), _readCD(istream),
"Data of Birth check digit mismatch");

gender = _read(istream, 1);
dateOfExpiry = _readDate(istream);
dateOfExpiry = _readDate(istream, futureDate: true);
_assertCheckDigit(dateOfExpiry.formatYYMMDD(), _readCD(istream),
"Data of Expiry check digit mismatch");

Expand Down Expand Up @@ -185,12 +185,12 @@ class MRZ {
_docNum, _readCD(istream), "Document Number check digit mismatch");

nationality = _read(istream, 3);
dateOfBirth = _readDate(istream);
dateOfBirth = _readDate(istream, futureDate: false);
_assertCheckDigit(dateOfBirth.formatYYMMDD(), _readCD(istream),
"Data of Birth check digit mismatch");

gender = _read(istream, 1);
dateOfExpiry = _readDate(istream);
dateOfExpiry = _readDate(istream, futureDate: true);
_assertCheckDigit(dateOfExpiry.formatYYMMDD(), _readCD(istream),
"Data of Expiry check digit mismatch");

Expand Down Expand Up @@ -240,8 +240,8 @@ class MRZ {
return _readWithPad(istream, maxLength).replaceAll(RegExp(r'<+$'), '');
}

static DateTime _readDate(InputStream istream) {
return _read(istream, 6).parseDateYYMMDD();
static DateTime _readDate(InputStream istream, {bool futureDate = false}) {
return _read(istream, 6).parseDateYYMMDD(futureDate: futureDate);
}

static int _readCD(InputStream istream) {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/proto/dba_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ class DBAKey extends AccessKey {
String get mrtdNumber => _mrtdNum;

/// Returns passport owner's date of birth used for calculating key seed.
DateTime get dateOfBirth => _dob.parseDateYYMMDD();
DateTime get dateOfBirth => _dob.parseDateYYMMDD(futureDate: false);

/// Returns passport date of expiry used for calculating key seed.
DateTime get dateOfExpiry => _doe.parseDateYYMMDD();
DateTime get dateOfExpiry => _doe.parseDateYYMMDD(futureDate: true);

/// Very sensitive data. Do not use in production!
@override
Expand Down
38 changes: 31 additions & 7 deletions test/extensions_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,43 @@ void main() {
expect( '121212'.parseDateYYMMDD() , DateTime(2012, 12, 12) );
expect( '201212'.parseDateYYMMDD() , DateTime(2020, 12, 12) );

expect( '891109'.parseDateYYMMDD(futureDate: false) , DateTime(1989, 11, 9) );
expect( '760501'.parseDateYYMMDD(futureDate: false) , DateTime(1976, 05, 01) );
expect( '000215'.parseDateYYMMDD(futureDate: false) , DateTime(2000, 02, 15) );
expect( '111111'.parseDateYYMMDD(futureDate: false) , DateTime(2011, 11, 11) );
expect( '121212'.parseDateYYMMDD(futureDate: false) , DateTime(2012, 12, 12) );
expect( '201212'.parseDateYYMMDD(futureDate: false) , DateTime(2020, 12, 12) );

expect( '891109'.parseDateYYMMDD(futureDate: true) , DateTime(1989, 11, 9) );
expect( '760501'.parseDateYYMMDD(futureDate: true) , DateTime(1976, 05, 01) );
expect( '000215'.parseDateYYMMDD(futureDate: true) , DateTime(2000, 02, 15) );
expect( '111111'.parseDateYYMMDD(futureDate: true) , DateTime(2011, 11, 11) );
expect( '121212'.parseDateYYMMDD(futureDate: true) , DateTime(2012, 12, 12) );
expect( '201212'.parseDateYYMMDD(futureDate: true) , DateTime(2020, 12, 12) );

final now = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
expect( now.formatYYMMDD().parseDateYYMMDD() , now );
expect( now.formatYYMMDD().parseDateYYMMDD(), now );
expect( now.formatYYMMDD().parseDateYYMMDD(futureDate: false), now );
expect( now.formatYYMMDD().parseDateYYMMDD(futureDate: true) , now );

// 1 month future date
final nextMonth = DateTime(now.year, now.month + 1, now.day);
expect( nextMonth.formatYYMMDD().parseDateYYMMDD(), nextMonth );
expect( nextMonth.formatYYMMDD().parseDateYYMMDD() != nextMonth, true ); // by default future date is not expected
expect( nextMonth.formatYYMMDD().parseDateYYMMDD(futureDate: false) != nextMonth, true );
expect( nextMonth.formatYYMMDD().parseDateYYMMDD(futureDate: true) == nextMonth, true );

// 10 years future date
final tenFromNow = DateTime(now.year + 10, now.month, now.day);
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD(), tenFromNow );
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD() != tenFromNow, true ); // by default future date is not expected
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: false) != tenFromNow, true );
expect( tenFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: true) == tenFromNow, true );

// 10 years and 6 months from now should wind date back for a century.
// 10 years and 6 months future date
final tenAnd6MonthsFromNow = DateTime(now.year + 10, now.month + 6, now.day);
final ninetyYearsAgo = DateTime(now.year - 90, now.month + 6, now.day);
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD(), ninetyYearsAgo );
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD() , ninetyYearsAgo );
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: false), ninetyYearsAgo );
expect( tenAnd6MonthsFromNow.formatYYMMDD().parseDateYYMMDD(futureDate: true) , tenAnd6MonthsFromNow );
});
});

Expand Down Expand Up @@ -152,7 +176,7 @@ void main() {
Logger.root.level = Level.FINEST;
Logger.root.trace(traceMsg);
expect( logMsg, traceMsg );
expect( level , Level.FINEST );
expect( level , Level.FINEST );
});

test('Sensitive data log test', () {
Expand Down Expand Up @@ -404,7 +428,7 @@ void main() {
dlog.sdDebug(debugMsg);
expect( dlogMsg, debugMsg );
expect( dlevel , Level.FINE );

// Verbose test
Logger.root.logSensitiveData = false;
dlog.logSensitiveData = false;
Expand Down

0 comments on commit fa50698

Please sign in to comment.