validation_CZ_BN.c (3764B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021, 2026 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file reducer/validation_CZ_BN.c 18 * @brief validation of Czech birth numbers 19 * @author Christian Grothoff 20 */ 21 #include <string.h> 22 #include <stdbool.h> 23 #include <stdio.h> 24 25 26 /** 27 * Check the YYMMDD part of a birth number. 28 * 29 * The month carries the sex and, since 2004, an overflow marker: women have 30 * 50 added, and an exhausted daily serial adds another 20. 31 * 32 * @param d the first six characters of the birth number 33 * @param allow_overflow true if the +20/+70 month variants are acceptable 34 * @param[out] yy set to the two-digit year 35 * @return true if the date is well-formed 36 */ 37 static bool 38 check_date (const char *d, 39 bool allow_overflow, 40 unsigned int *yy) 41 { 42 unsigned int mm; 43 unsigned int dd; 44 45 for (unsigned int i = 0; i<6; i++) 46 if ( ('0' > d[i]) || ('9' < d[i]) ) 47 return false; 48 *yy = (d[0] - '0') * 10 + (d[1] - '0'); 49 mm = (d[2] - '0') * 10 + (d[3] - '0'); 50 dd = (d[4] - '0') * 10 + (d[5] - '0'); 51 if ( (mm >= 71) && (mm <= 82) ) 52 { 53 if (! allow_overflow) 54 return false; 55 mm -= 70; 56 } 57 else if ( (mm >= 51) && (mm <= 62) ) 58 { 59 mm -= 50; 60 } 61 else if ( (mm >= 21) && (mm <= 32) ) 62 { 63 if (! allow_overflow) 64 return false; 65 mm -= 20; 66 } 67 if ( (mm < 1) || (mm > 12) ) 68 return false; 69 if ( (dd < 1) || (dd > 31) ) 70 return false; 71 return true; 72 } 73 74 75 /** 76 * Function to validate a Czech birth number (rodné číslo). 77 * 78 * The modern form is YYMMDD/XXXC, where the ten digits must be divisible by 79 * 11; as a documented exception, a first-nine-digits remainder of 10 is 80 * written with a check digit of 0. Numbers issued before 1954 have only nine 81 * digits (YYMMDD/XXX) and carry no check digit at all, so nothing beyond the 82 * date can be verified for them. Those are accepted only when the year reads 83 * as 1900-1953; a nine-digit number claiming a later year is a truncated 84 * modern one. (People born before 1900 also hold nine-digit numbers, which 85 * this therefore rejects.) 86 * 87 * @param b_number birth number to validate (input) 88 * @return true if b_number is valid 89 */ 90 bool 91 CZ_BN_check (const char *b_number); 92 93 /* declaration to fix compiler warning */ 94 bool 95 CZ_BN_check (const char *b_number) 96 { 97 size_t len = strlen (b_number); 98 unsigned int yy; 99 100 if ( (10 != len) && (11 != len) ) 101 return false; 102 if ('/' != b_number[6]) 103 return false; 104 for (size_t i = 7; i<len; i++) 105 if ( ('0' > b_number[i]) || ('9' < b_number[i]) ) 106 return false; 107 if (! check_date (b_number, 108 11 == len, 109 &yy)) 110 return false; 111 if (10 == len) 112 { 113 /* pre-1954 form: no check digit exists */ 114 return (yy < 54); 115 } 116 { 117 unsigned long long n = 0; 118 unsigned long long base; 119 unsigned int check; 120 121 for (unsigned int i = 0; i<6; i++) 122 n = n * 10 + (b_number[i] - '0'); 123 for (unsigned int i = 7; i<11; i++) 124 n = n * 10 + (b_number[i] - '0'); 125 base = n / 10; 126 check = (unsigned int) (n % 10); 127 if (10 == base % 11) 128 return (0 == check); 129 return (0 == n % 11); 130 } 131 }