Completed
Push — master ( a004d3...9bce71 )
by Hannes
03:41 queued 02:35
created

PersonalId::getDate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace byrokrat\id;
4
5
/**
6
 * Swedish personal identity numbers
7
 */
8
class PersonalId extends AbstractId
9
{
10
    /**
11
     * Regular expression describing id structure
12
     */
13
    const PATTERN = '/^((?:\d\d)?)(\d{6})([-+]?)(\d{3})(\d)$/';
14
15
    /**
16
     * @var string[] Map of county number high limit to county identifier
17
     */
18
    private static $birthCountyMap = [
19
        13 => Id::COUNTY_STOCKHOLM,
20
        15 => Id::COUNTY_UPPSALA,
21
        18 => Id::COUNTY_SODERMANLAND,
22
        23 => Id::COUNTY_OSTERGOTLAND,
23
        26 => Id::COUNTY_JONKOPING,
24
        28 => Id::COUNTY_KRONOBERG,
25
        31 => Id::COUNTY_KALMAR,
26
        32 => Id::COUNTY_GOTLAND,
27
        34 => Id::COUNTY_BLEKINGE,
28
        38 => Id::COUNTY_KRISTIANSTAD,
29
        45 => Id::COUNTY_MALMOHUS,
30
        47 => Id::COUNTY_HALLAND,
31
        54 => Id::COUNTY_GOTEBORG_BOUHUS,
32
        58 => Id::COUNTY_ALVSBORG,
33
        61 => Id::COUNTY_SKARABORG,
34
        64 => Id::COUNTY_VARMLAND,
35
        65 => Id::COUNTY_UNDEFINED,
36
        68 => Id::COUNTY_OREBRO,
37
        70 => Id::COUNTY_VASTMANLAND,
38
        73 => Id::COUNTY_KOPPARBERG,
39
        74 => Id::COUNTY_UNDEFINED,
40
        77 => Id::COUNTY_GAVLEBORG,
41
        81 => Id::COUNTY_VASTERNORRLAND,
42
        84 => Id::COUNTY_JAMTLAND,
43
        88 => Id::COUNTY_VASTERBOTTEN,
44
        92 => Id::COUNTY_NORRBOTTEN
45
    ];
46
47
    /**
48
     * @var \DateTime Date of birth
49
     */
50
    private $date;
51
52
    /**
53
     * Swedish personal identity numbers
54
     *
55
     * Format is YYYYMMDD(+-)NNNC or YYMMDD(+-)NNNC where parenthesis represents
56
     * an optional one char delimiter, N represents the individual number and C
57
     * the check digit. If year is set using two digits century is calculated
58
     * based on delimiter (+ signals more than a hundred years old). If year is
59
     * set using four digits delimiter is calculated based on century.
60
     *
61
     * @param  string $number
62
     * @throws Exception\InvalidDateStructureException If date is not logically valid
63
     */
64 97
    public function __construct($number)
65
    {
66 74
        list(, $century, $this->serialPre, $delimiter, $this->serialPost, $this->checkDigit)
67 97
            = $this->parseNumber(self::PATTERN, $number);
68
69 74
        $this->delimiter = $delimiter ?: '-';
70
71 74
        if ($century) {
72
            // Set delimiter based on date (+ if date is more then a hundred years old)
73 16
            $this->date = DateTimeCreator::createFromFormat('Ymd', $century.$this->serialPre);
74 16
            $hundredYearsAgo = new \DateTime();
75 16
            $hundredYearsAgo->modify('-100 year');
76 16
            $this->delimiter = $this->getBirthDate() < $hundredYearsAgo ? '+' : '-';
77
        } else {
78
            // No century defined
79 59
            $this->date = DateTimeCreator::createFromFormat('ymd', $this->serialPre);
80
81
            // If in the future century is wrong
82 59
            if ($this->date > new \DateTime) {
83 2
                $this->date->modify('-100 year');
84
            }
85
86
            // Date is over a hundred years ago if delimiter is +
87 59
            if ($this->getDelimiter() == '+') {
88 21
                $this->date->modify('-100 year');
89
            }
90
        }
91
92
        // Validate that date is logically valid
93 74
        if ($this->date->format('ymd') != $this->serialPre) {
94 1
            throw new Exception\InvalidDateStructureException("Invalid date in <{$this->getId()}>");
95
        }
96
97 73
        $this->validateCheckDigit();
98 37
    }
99
100
    /**
101
     * Get date of birth
102
     *
103
     * @return \DateTime
104
     */
105 21
    public function getBirthDate()
106
    {
107 21
        return $this->date;
108
    }
109
110
    /**
111
     * Get sex as denoted by id
112
     *
113
     * @return string One of the sex identifier constants
114
     */
115 4
    public function getSex()
116
    {
117 4
        return (intval($this->getSerialPostDelimiter()[2])%2 == 0) ? self::SEX_FEMALE : self::SEX_MALE;
118
    }
119
120
    /**
121
     * Get string describing birth county
122
     *
123
     * @return string One of the birth county identifier constants
124
     */
125 3
    public function getBirthCounty()
126
    {
127 3
        if ($this->getBirthDate() < DateTimeCreator::createFromFormat('Ymd', '19900101')) {
128 3
            $countyNr = (int) substr($this->getSerialPostDelimiter(), 0, 2);
129 3
            foreach (self::$birthCountyMap as $limit => $identifier) {
130 3
                if ($countyNr <= $limit) {
131 3
                    return $identifier;
132
                }
133
            }
134
        }
135
136 2
        return Id::COUNTY_UNDEFINED;
137
    }
138
}
139