问题
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)");
}
}
回答1:
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)");
}
}
回答2:
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.
回答3:
Instead of doing custom RegEx, I'd suggest looking at a NuGet package available that is able to parse a valid Swedish Personal Identity Number and then extract information such as DateOfBirth, Age, Gender etc that could be used as input to your validation. The method .GetAge() would be the most relevant in your case. Disclaimer: I'm one of the co-authors of that package.
An implementation of ValidateOfAge using SwedishPersonalIdentityNumber could look like:
public class ValidateOfAge : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (SwedishPersonalIdentityNumber.TryParse((string)value, out var personalIdentityNumber))
{
if(personalIdentityNumber.GetAge() >= 18) {
return ValidationResult.Success;
} else {
return new ValidationResult("Du måste vara minst 18 år gammal.");
}
}
return new ValidationResult("Ogiltigt personnummer.");
}
}
SwedishPersonalIdentityNumber supports the patterns you describe, including a few more that is valid (did you know that a PIN can contain a + for example?).
- You can find the source code here:
- https://github.com/ActiveLogin/ActiveLogin.Identity
- And the packages on NuGet here:
- https://www.nuget.org/packages/ActiveLogin.Identity.Swedish/
- https://www.nuget.org/packages/ActiveLogin.Identity.Swedish.AspNetCore/
The second one contains a validation attribute for validating a PIN in your model. If the use case of validating age is common enough, we could implement it into that validation attribute. I created an issue to find out about interest and the design.
The API design proposed at the moment is this (for your scenario):
[SwedishPersonalIdentityNumber(MinimumAge = 18)]
public string PersonalIdentityNumber { get; set; }
Please share your thoughts in the issue if this is something you'd like to see implemented.
来源:https://stackoverflow.com/questions/32624800/swedish-ssn-regular-expression-reject-users-under-a-specific-age