Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swedish SSN Regular expression reject users under a specific age

I Have a problem with my regular expression. I have made it possible to valdiate correct swedish social security number to match these criteria.

  • YYMMDDNNNN
  • YYMMDD-NNNN
  • YYYYMMDDNNNN
  • YYYYMMDD-NNNN

But i would also like to reject a user to register if the user is under 18 years old. My reqular expression is looking like this at the moment: Do anyone encountered the same problem with the age range Swedish SSN?

  private const string RegExForValidation =
        @"^(\d{6}|\d{8})[-|(\s)]{0,1}\d{4}$";

UPDATE

 private const string RegExForValidation = @"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
        string date = Regex.Match("19970918-1234", RegExForValidation).Groups["date"].Value;
        DateTime dt;

 [Required(ErrorMessage = "Du måste ange personnummer")]
        [RegularExpression(RegExForValidation, ErrorMessage = "Personnummer anges med 10 siffror (yymmddnnnn)")]
        public string PersonalIdentityNumber { get; set; }

Second Update

 public class ValidateOfAge : ValidationAttribute
{
    public bool IsOfAge(DateTime birthdate)
    {
        DateTime today = DateTime.Today;
        int age = today.Year - 18;
        if (birthdate.AddYears(birthdate.Year) < today.AddYears(-age))
            return false;
        else
            return true;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        string RegExForValidation = @"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
        string date = Regex.Match((string)value, RegExForValidation).Groups["date"].Value;
        DateTime dt;
        if (DateTime.TryParseExact(date, new[] { "yyMMdd", "yyyyMMdd" }, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
            if (IsOfAge(dt))
                return ValidationResult.Success;
        return new ValidationResult("Personnummer anges med 10 siffror (yymmddnnnn)");
    }
}
like image 874
Andreas Jangefalk Avatar asked Sep 17 '15 07:09

Andreas Jangefalk


2 Answers

You need to get the birthdate from the SSN, parse to DateTime, and then compare with today's date.

Here is the method checking if a person is of age:

public bool IsOfAge(DateTime birthdate)
{
    DateTime today = DateTime.Today;       // Calculating age...
    int age = today.Year - birthdate.Year;
    if (birthdate > today.AddYears(-age)) 
        age--;
    return age < 18 ? false : true;   // If the age is 18+ > true, else false.
}

And here is how you can use this:

string RegExForValidation = @"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
string date = Regex.Match("19970918-1234", RegExForValidation).Groups["date"].Value;
DateTime dt;
if (DateTime.TryParseExact(date, new[] { "yyMMdd", "yyyyMMdd" }, new CultureInfo("sv-SE"), DateTimeStyles.None, out dt))
   Console.WriteLine(IsOfAge(dt));

Note that [-|(\s)] matches a -, |, (, whitespace or ). I am sure you only want to match a hyphen or whitespace.

I added a named capture to the regex and removed unnecessary symbols from the character class. Also, note that {0,1} is the same as ?.

UPDATE

To make it work in an MVC app, you need to implement a custom validator:

[Required(ErrorMessage = "Du måste ange personnummer")]
[ValidateOfAge]  // <---------------------------- HERE
public string PersonalIdentityNumber { get; set; }

And implement this as follows:

public class ValidateOfAge: ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {                 
        string RegExForValidation = @"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
        string date = Regex.Match((string)value, RegExForValidation).Groups["date"].Value;
        DateTime dt;
        if (DateTime.TryParseExact(date, new[] { "yyMMdd", "yyyyMMdd" }, new CultureInfo("sv-SE"), DateTimeStyles.None, out dt))
            if (IsOfAge(dt))
                return ValidationResult.Success;
        return new ValidationResult("Personnummer anges med 10 siffror (yymmddnnnn)");
    }
}
like image 21
Wiktor Stribiżew Avatar answered Nov 13 '22 16:11

Wiktor Stribiżew


This is a case where I wouldn't use regular expressions, but instead rely on the base class library's built-in DateTime parsing functionality:

public DateTime GetBirthDate(string ssn)
{
    var strippedOfSerial =
        ssn.Substring(0, ssn.Length - 4).TrimEnd('-');
    return DateTime.ParseExact(
        strippedOfSerial,
        new[] { "yyMMdd", "yyyyMMdd" },
        new CultureInfo("se-SE"),
        DateTimeStyles.None);
}

Now you can look at the returned DateTime value and compare it to DateTime.Now in order to figure out if you want to reject it or not.

In my opinion, this is much more readable than relying on regular expressions, and may be safer and more flexible as well. As you can tell, you can e.g. use other cultures etc. to tweak the parsing strategy.

like image 55
Mark Seemann Avatar answered Nov 13 '22 16:11

Mark Seemann