Validator CNP - clasa Delphi

Validator CNP - clasa Delphi

Postby DarkByte » 11 Jun 2011, 19:50

Acum cativa ani am fost interesat ce se ascunde in cifrele unui CNP ... bineinteles, in afara de lucrurile evidente, cum ar fi data nasterii sau sexul. In plus, am fost curios daca orice sir de 13 cifre reprezinta un CNP - ceea ce, evident, nu este adevarat.

Sa vedem cum arata structura unui CNP:

S AA LL ZZ JJ NNN C

  • S - evident, sexul. 1 pentru barbati, 2 pentru femei. Un lucru mai putin evident este ca aceasta cifra este folosita si pentru a specifica secolul sau persoane straine, in felul urmator:
    • 1 si 2 - barbat sau femeie, secolul XX
    • 3 si 4 - barbat sau femeie, secolul XIX
    • 5 si 6 - barbat sau femeie, secolul XXI
    • 7 si 8 - persoane straine rezidente in Romania - se subintelege secolul XX
    • 9 - persoane straine
  • AA - anul nasterii - care depinde si de prima cifra, dupa cum am specificat mai sus
  • LL - luna nasterii, o valoare pe doua cifre. Poate lua valori doar intre 01 si 12
  • ZZ - ziua nasterii, tot pe doua cifre. Poate lua valori intre 01 si 31, in functie de luna si an
  • JJ - judetul in care a fost inregistrat CNP-ul. Poate lua valori intre 01 si 52 (nealocand niciun judet valorilor intre 47 si 50).
  • NNN - dupa cate zice Wikipedia, un numar intre 1 si 999, unic per judet.
  • C - cifra de control. Pentru verificare, se iau primele 12 cifre din CNP si se inmultesc cu numarul 279146358279 (cifra cu cifra) si se aduna produsele. Suma aceasta se imparte intreg cu rest la 11 - daca restul e 10, se considera 1. Daca acest rest este egal cu cifra de control, CNP-ul este (sau poate fi !) valid.

Lista oraselor o puteti gasi aici (multumesc, Daniela !) sau pe Wikipedia.

Pentru a usura (si a evita erorile) validarea CNP-urilor, am implementat o clasa care expune doar proprietati read-only. Codul a fost scris in Delphi 7 si ar trebui sa compileze corect, fara modificari, pe orice versiune mai noua de Delphi.

  1. unit uCNP;
  2.  
  3. (*
  4.  
  5.   uCNP class - THE way to check for a CNP (cod numeric personal) validity in Delphi
  6.  
  7.   Instantiate an uCNP object, use "SetCNP" to set and verify a CNP and check if "ValidCNP".
  8.   If it's valid, you can get the birthday (in a TDate format or as integers - year, month, day),
  9.   the sex and the town encoded in the CNP.
  10.  
  11.   by DarkByte - http://www.bitcell.info
  12.  
  13.   You're allowed to use and modify this source freely, if you don't remove this notice !
  14.  
  15. *)
  16.  
  17. interface
  18.  
  19. uses SysUtils, Controls, DateUtils;
  20.  
  21. type
  22.   TSex = (sInvalid = -1,
  23.           sMale = 0,
  24.           sFemale = 1);
  25.  
  26.   TCNP = class(TObject)
  27.   private
  28.     CNP: String;
  29.  
  30.     FValidCNP: Boolean;
  31.     FSex: TSex;
  32.  
  33.     FYear: Word;
  34.     FMonth: Word;
  35.     FDay: Word;
  36.     FBirthDay: TDate;
  37.  
  38.     FAgeInYears: Integer;
  39.     FAgeInMinutes: Cardinal;
  40.     Today: TDateTime;
  41.  
  42.     FTown: Byte;
  43.     FTownName: String;
  44.  
  45.     function ContainsOnlyDigits: Boolean;
  46.     function MatchesControlDigit: Boolean;
  47.     function DateIsValid: Boolean;
  48.  
  49.     function IsValidCNP: Boolean;
  50.  
  51.     procedure ParseCNPData;
  52.   published
  53.     property ValidCNP: Boolean read FValidCNP;
  54.  
  55.     property Sex: TSex read FSex;
  56.  
  57.     property Year: Word read FYear;
  58.     property Month: Word read FMonth;
  59.     property Day: Word read FDay;
  60.     property BirthDay: TDate read FBirthDay;
  61.  
  62.     property Age: Integer read FAgeInYears;
  63.     property AgeInMinutes: Cardinal read FAgeInMinutes;
  64.  
  65.     property Town: Byte read FTown;
  66.     property TownName: String read FTownName;
  67.   public
  68.     procedure SetCNP(aCNP: String; aToday: TDateTime = -1);
  69.   end;
  70.  
  71. const
  72.   Judete: array [0 .. 52] of String =
  73.        ('N/A', 'Alba', 'Arad', 'Arges', 'Bacau', 'Bihor', 'Bistrita Nasaud', 'Botosani',
  74.         'Brasov', 'Braila', 'Buzau', 'Caras-Severin', 'Cluj', 'Constanta', 'Covasna',
  75.         'Dambovita', 'Dolj', 'Galati', 'Gorj', 'Harghita', 'Hunedoara', 'Ialomita',
  76.         'Iasi', 'Ilfov', 'Maramures', 'Mehedinti', 'Mures', 'Neamt', 'Olt', 'Prahova',
  77.         'Satu Mare', 'Salaj', 'Sibiu', 'Suceava', 'Teleorman', 'Timis', 'Tulcea',
  78.         'Vaslui', 'Valcea', 'Vrancea', 'Bucuresti', 'Bucuresti S1', 'Bucuresti S2',
  79.         'Bucuresti S3', 'Bucuresti S4', 'Bucuresti S5', 'Bucuresti S6',
  80.         '-', '-', '-', '-', 'Calarasi', 'Giurgiu');
  81.  
  82.   SexType: array [TSex] of String = ('N/A', 'Masculin', 'Feminin');
  83.  
  84. implementation
  85.  
  86. const
  87.   PCP: array [1 .. 12] of Byte = (2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9);
  88.  
  89.   Months: array [1 .. 12] of Byte = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  90.  
  91. procedure TCNP.SetCNP(aCNP: String; aToday: TDateTime = -1);
  92. begin
  93.   CNP := aCNP;
  94.   if (aToday <> -1)
  95.     then Today := aToday
  96.     else Today := NOW;
  97.  
  98.   FValidCNP := IsValidCNP;
  99.  
  100.   if not FValidCNP then
  101.     begin
  102.       FSex := sInvalid;
  103.       FYear := 0;
  104.       FMonth := 0;
  105.       FDay := 0;
  106.       FBirthDay := 0;
  107.  
  108.       FAgeInYears := -1;
  109.       FAgeInMinutes := 0;
  110.  
  111.       FTown := 0;
  112.       FTownName := Judete[0];
  113.     end;
  114. end;
  115.  
  116. function TCNP.ContainsOnlyDigits: Boolean;
  117. var
  118.   lIndex: Integer;
  119. begin
  120.   Result := True;
  121.  
  122.   if (Length(CNP) <> 13) then
  123.     begin
  124.       Result := False;
  125.       Exit;
  126.     end;
  127.  
  128.   for lIndex := 1 to 13 do
  129.     begin
  130.       if (Ord(CNP[lIndex]) < 48) and (Ord(CNP[lIndex]) > 57) then
  131.         begin
  132.           Result := False;
  133.           Break;
  134.         end;
  135.     end;
  136. end;
  137.  
  138. function TCNP.MatchesControlDigit: Boolean;
  139. var
  140.   lIndex: Integer;
  141.   lSuma, lRest: Word;
  142. begin
  143.   lSuma := 0;
  144.   for lIndex := 1 to 12 do
  145.     lSuma := lSuma + (Ord(CNP[lIndex]) - 48) * PCP[lIndex];
  146.  
  147.   lRest := lSuma mod 11;
  148.   if (lRest = 10) then
  149.     lRest := 1;
  150.  
  151.   Result := (Chr(lRest + 48) = CNP[13]);
  152. end;
  153.  
  154. function TCNP.DateIsValid: Boolean;
  155. var
  156.   lLeapYear: Boolean;
  157. begin
  158.   Result := True;
  159.   FYear := 1900 + StrToInt(Copy(CNP, 2, 2));
  160.   case CNP[1] of
  161.     '3', '4': Dec(FYear, 100);
  162.     '5', '6': Inc(FYear, 100);
  163.   end;
  164.  
  165.   lLeapYear := IsInLeapYear(FYear);
  166.   FMonth := StrToInt(Copy(CNP, 4, 2));
  167.   FDay := StrToInt(Copy(CNP, 6, 2));
  168.   FTown := StrToInt(Copy(CNP, 8, 2));
  169.  
  170.   if (lLeapYear and (months[FMonth] < FDay) and (FMonth <> 2) and (FDay <> 29))
  171.     then Result := False
  172.     else FBirthDay := EncodeDate(FYear, FMonth, FDay);
  173. end;
  174.  
  175. procedure TCNP.ParseCNPData;
  176. begin
  177.   if Odd(StrToInt(CNP[1]))
  178.     then FSex := sMale
  179.     else FSex := sFemale;
  180.  
  181.   FTown := StrToInt(Copy(CNP, 8, 2));
  182.   FTownName := Judete[FTown];
  183.  
  184.   FAgeInYears := YearsBetween(FBirthDay, Today);
  185.   FAgeInMinutes := MinutesBetween(FBirthDay, Today);
  186. end;
  187.  
  188. function TCNP.IsValidCNP: Boolean;
  189. begin
  190.   Result := ContainsOnlyDigits and MatchesControlDigit and DateIsValid;
  191.   if Result then
  192.     ParseCNPData;
  193. end;
  194.  
  195. end.


Un program demonstrativ (sursa si executabil) care foloseste aceasta clasa poate fi gasit in atasament - vedeti screenshot-ul atasat:
validare CNP, Delphi

Bafta !
5p / 1 votes
Attachments
CNP Check.zip
(219.4 KiB) Downloaded 62 times
User avatar
DarkByte
11011011
 
Joined: 29 Dec 2009
Status: 140

Re: Validator CNP - clasa Delphi

Postby ori » 12 Jun 2011, 01:20

SUUUPER!!! >:D<
Este perfect.
Am completat si cu varsta, in rest n-am modificat nimic.
  1. unit cnpUnit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls, XPMan, uCNP;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     edtCNP: TEdit;
  12.     Label1: TLabel;
  13.     Label2: TLabel;
  14.     edtSex: TEdit;
  15.     Label3: TLabel;
  16.     edtBirthday: TEdit;
  17.     Button1: TButton;
  18.     Label4: TLabel;
  19.     edtTown: TEdit;
  20.     XPManifest1: TXPManifest;
  21.     Label5: TLabel;
  22.     Label6: TLabel;
  23.     edtAge: TEdit;
  24.     procedure FormCreate(Sender: TObject);
  25.     procedure Button1Click(Sender: TObject);
  26.   private
  27.     { Private declarations }
  28.   public
  29.     { Public declarations }
  30.     CNP: TCNP;
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.dfm}
  39.  
  40. function CalculVarsta(Birthday, CurrentDate: TDate): Integer;
  41. var
  42.   Month, Day, Year, CurrentYear, CurrentMonth, CurrentDay: Word;
  43. begin
  44.   DecodeDate(Birthday, Year, Month, Day);
  45.   DecodeDate(CurrentDate, CurrentYear, CurrentMonth, CurrentDay);
  46.   if (Year = CurrentYear) and (Month = CurrentMonth) and (Day = CurrentDay) then
  47.   begin
  48.     Result := 0;
  49.   end
  50.   else
  51.   begin
  52.     Result := CurrentYear - Year;
  53.     if (Month > CurrentMonth) then
  54.       Dec(Result)
  55.     else
  56.     begin
  57.       if Month = CurrentMonth then
  58.         if (Day > CurrentDay) then
  59.           Dec(Result);
  60.     end;
  61.   end;
  62. end;
  63.  
  64. procedure TForm1.FormCreate(Sender: TObject);
  65. var
  66.   Style: DWord;
  67. begin
  68.   Style := GetWindowLong(edtCNP.Handle, GWL_STYLE);
  69.   SetWindowLong(edtCNP.Handle, GWL_STYLE, Style or ES_NUMBER or ES_CENTER);
  70.  
  71.   CNP := TCNP.Create;
  72. end;
  73.  
  74. procedure TForm1.Button1Click(Sender: TObject);
  75. begin
  76.   CNP.SetCNP(edtCNP.Text);
  77.  
  78.   if CNP.ValidCNP
  79.     then
  80.       begin
  81.         edtSex.Text := SexType[CNP.Sex];
  82.         edtBirthday.Text := DateToStr(CNP.BirthDay);
  83.         edtAge.Text := Format('%d',[CalculVarsta((CNP.BirthDay), Date)]);
  84.         edtTown.Text := CNP.TownName;
  85.         edtCNP.Color:=clWhite;
  86.       end
  87.     else
  88.       begin
  89.         ShowMessage('CNP-ul nu este corect!');
  90.         edtCNP.Focused;
  91.         edtCNP.Color:=clRed;
  92.       end;
  93. end;
  94.  
  95. end.


>:D<

P.S. Am reusit de am compilat si codul lui Sharky in Free Pascal (interfata programului = BIG DIFFERENCE :) ). Este ok.

Va multumesc tuturor pt. ajutor.
0,0p / 0 votes
User avatar
ori
Bit
 
Joined: 10 Jun 2011
Status: 0

Re: Validator CNP - clasa Delphi

Postby DarkByte » 12 Jun 2011, 10:02

Ma bucur ca ti-e de folos, ori.

Am, totusi, niste comentarii legate de cum folosesti clasa de validare - ai luat proprietatea BirthDay, expusa de clasa, si ai decodat-o in Year, Month, Day ... care sunt si ele expuse :D

Al doilea comentariu este legat de modul in care calculezi varsta : se putea calcula mai usor cu YearsBetween. Either way, am facut update clasei de validare a CNP-urilor pentru a expune proprietatile "Age" si "AgeInMinutes" (a doua e ... "just in case" :D) si am adaugat modificarile in primul post (am modificat si programul demonstrativ).

Bafta !
0,0p / 0 votes
User avatar
DarkByte
11011011
 
Joined: 29 Dec 2009
Status: 140

Re: Validator CNP - clasa Delphi

Postby ori » 12 Jun 2011, 14:02

:"> Pai da. Mult mai elegant si corect. *-:)
0,0p / 0 votes
User avatar
ori
Bit
 
Joined: 10 Jun 2011
Status: 0

Re: Validator CNP - clasa Delphi

Postby RaverX » 13 Jun 2011, 00:52

Poate faci si un CNP generator :)
Sa poti baga data nasterii si orasul si sa-ti genereze un CNP valid ...
0,0p / 0 votes
User avatar
RaverX
Bit
 
Joined: 06 Jan 2010
Status: 0

Re: Validator CNP - clasa Delphi

Postby DarkByte » 13 Jun 2011, 01:23

M-am gandit si la asta, dar nu asta a fost scopul acestui topic :)
0,0p / 0 votes
User avatar
DarkByte
11011011
 
Joined: 29 Dec 2009
Status: 140

Re: Validator CNP - clasa Delphi

Postby v0id » 13 Jun 2011, 21:40

Nice one.
Ma bucur ca printre altele am aflat si eu de ce incepe CNP-ul lui Vlad al meu cu 5 :D N-am avut timp sa ma documentez, deci asta a picat la fix.
Acum mai am o singura curiozitate: ce-or sa faca astia cand se va ajunge ca CNP-ul sa inceapa cu numarul 10? Vor trece la hex (A) sau CNP-urile vor avea cu o cifra mai mult decat acum? :D
0,0p / 0 votes
A good coder is never on holiday - he may be working on a different machine, that's about as far as it gets.
User avatar
v0id
Word
 
Joined: 05 Jan 2010
Location: 127.0.0.1
Status: 42.5

Re: Validator CNP - clasa Delphi

Postby DarkByte » 13 Jun 2011, 21:41

We can only wait and see - and hope for the best ! Daca se trezeste vreun cretin sa modifice structura CNP-ului, am incurcat-o - not to mention ca nici structura actuala nu e cine stie ce ...
0,0p / 0 votes
User avatar
DarkByte
11011011
 
Joined: 29 Dec 2009
Status: 140

Re: Validator CNP - clasa Delphi

Postby zoltan.satmari » 14 Jun 2011, 07:38

Offtopic
0,0p / 0 votes
User avatar
zoltan.satmari
Bit
 
Joined: 25 May 2011
Status: 0


Return to Tutoriale Delphi

Who is online

Users browsing this forum: No registered users and 0 guests