Skip to content

Instantly share code, notes, and snippets.

@esson
Last active August 29, 2015 14:19
Show Gist options
  • Select an option

  • Save esson/1c30bd946dd380f2a040 to your computer and use it in GitHub Desktop.

Select an option

Save esson/1c30bd946dd380f2a040 to your computer and use it in GitHub Desktop.
Class for dealing with Swedish personal identity numbers.
public class SwedishPersonalIdentityNumber
{
/// <summary>
/// Pattern for finding common ways of writing a Swedish personal identity number. It is forgiving and will match the format rather than valid numbers.
///
/// Illustrating how it works:
/// ((19)99)(06)(01)(-)((01)|(TF))(1)(1)
/// </summary>
private const string RegexPattern = @"^(?<year>((19)|(20))?(\d\d))(?<month>[01]\d)(?<day>\d\d)(?<flag>[-]|[\+])?(?<county>(\d\d)|(TF))(?<gender>\d)(?<checksum>\d)$";
public int BirthYear { get; set; }
public int BirthMonth { get; set; }
public int BirthDay { get; set; }
public int CountyCode { get; set; }
public int GenderCode { get; set; }
public int Checksum { get; set; }
public bool IsCoordinationNumber { get; set; }
public bool IsTemporaryNumber { get; set; }
public DateTime Birthdate
{
get
{
return new DateTime(this.BirthYear, this.BirthMonth, this.BirthDay);
}
set
{
this.BirthYear = value.Year;
this.BirthMonth = value.Month;
this.BirthDay = value.Day;
}
}
public int Age
{
get
{
var birthdate = this.Birthdate;
var today = DateTime.Today;
var result = today.Year - birthdate.Year;
if (today.Month < birthdate.Month || (today.Month == birthdate.Month && today.Day < birthdate.Day))
{
result--;
}
return result;
}
}
public SwedishPersonalIdentityNumber()
{
this.Birthdate = DateTime.MinValue;
}
public SwedishPersonalIdentityNumber(string swedishPersonalIdentityNumber)
: this()
{
var match = Regex.Match(swedishPersonalIdentityNumber.Trim(), RegexPattern);
if (match.Success == false)
{
throw new FormatException("The provided personal identity number is not a valid Swedish personal identity number.");
}
this.BirthMonth = Convert.ToInt32(match.Groups["month"].Value);
this.BirthDay = Convert.ToInt32(match.Groups["day"].Value);
if (this.BirthDay > 60)
{
// For coordination numbers, the day part is added with 60, so we deduct 60.
this.IsCoordinationNumber = true;
this.BirthDay = this.BirthDay - 60;
}
this.BirthYear = ParseBirthYear(match.Groups["year"].Value, this.BirthMonth, this.BirthDay, match.Groups["flag"] != null ? match.Groups["flag"].Value : null);
// For temporary numbers, the county part is "TF"
if (match.Groups["county"].Value == "TF")
{
this.IsTemporaryNumber = true;
this.CountyCode = 0;
}
else
{
this.CountyCode = Convert.ToInt32(match.Groups["county"].Value);
}
this.GenderCode = Convert.ToInt32(match.Groups["gender"].Value);
this.Checksum = Convert.ToInt32(match.Groups["checksum"].Value);
}
private int ParseBirthYear(string birthYear, int month, int day, string flag)
{
if (birthYear.Length == 4)
{
// String contains full year.
return Convert.ToInt32(birthYear);
}
else
{
// String contains partial year.
var currentDate = DateTime.Today;
var birthdayThisYear = new DateTime(currentDate.Year, month, day);
// Convert the two digit year to four digits.
//
// Create an object with Swedish culture information.
var swedishCultureInfo = new CultureInfo("sv-SE");
// Set the maximum possible year we can convert to. If this years birthday has occurred, last year will be the maximum year.
swedishCultureInfo.Calendar.TwoDigitYearMax = birthdayThisYear > currentDate ? currentDate.Year - 1 : currentDate.Year;
if (flag == "+")
{
// If the flag is a plus symbol, that means the birthyear is at least 100 years ago. So we can safely remove 100 years from the maximum possible year.
swedishCultureInfo.Calendar.TwoDigitYearMax -= 100;
}
// Calculate the result.
return swedishCultureInfo.Calendar.ToFourDigitYear(Convert.ToInt32(birthYear));
}
}
#region ToString Methods
public override string ToString()
{
return this.ToString("yymmdd-nnnn");
}
public string ToLongString(bool useFlag = true)
{
return this.ToString(useFlag ? "yyyymmdd-nnnn" : "yyyymmddnnnn");
}
public string ToString(string format)
{
var result = format;
result = result.Replace("yyyy", this.Birthdate.ToString("yyyy"));
result = result.Replace("yyy", this.Birthdate.ToString("yyy"));
result = result.Replace("yy", this.Birthdate.ToString("yy"));
result = result.Replace("mm", this.Birthdate.ToString("MM"));
result = result.Replace("m", this.Birthdate.ToString("M"));
result = result.Replace("dd", string.Format("{0:D2}", this.IsCoordinationNumber ? this.BirthDay + 60 : this.BirthDay));
result = result.Replace("d", string.Format("{0}", this.IsCoordinationNumber ? this.BirthDay + 60 : this.BirthDay));
result = result.Replace("-", this.Age > 99 ? "+" : "-");
result = result.Replace("nnnn", string.Format(this.IsTemporaryNumber ? "TF{1}{2}" : "{0:D2}{1}{2}", this.CountyCode, this.GenderCode, this.Checksum));
return result;
}
#endregion
#region Static Methods
public static SwedishPersonalIdentityNumber Parse(string s)
{
return new SwedishPersonalIdentityNumber(s);
}
public static bool Validate(string s)
{
var match = Regex.Match(s.Trim(), RegexPattern);
return match.Success;
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment