Repository: joke2k/faker

Issue:
Latvian ssn generator is not correct
* Faker version: 17.0.0
* OS: Windows


Latvian ssn generator generates ssn in format 'ddd-dd-dddd', which is not correct. 

### Steps to reproduce

```
from faker import Faker
fake = Faker('lv_LV')
fake.ssn()

```

### Expected behavior

It should be 12 characters long DDMMYY-CZZZQ e.g. 170334-19862

### Actual behavior

fake.ssn() for latvian locale outputs ssn in format  'ddd-dd-dddd'
'604-87-6475'
'824-21-9489'
'411-57-8156'
'938-77-8408'


Discussion:

Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 5a7da4bc..26d4ff16 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 17.0.0
+current_version = 17.1.0
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dfa37e88..f36ac715 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 ## Changelog
 
+### [v17.1.0 - 2023-02-24](https://github.com/joke2k/faker/compare/v17.0.0...v17.1.0)
+
+* Add argument validation to `pybool()`. Thanks @Kolyunya.
+
 ### [v17.0.0 - 2023-02-13](https://github.com/joke2k/faker/compare/v16.9.0...v17.0.0)
 
 * Remove deprecated placeimg image provider. Thanks @Wonskcalb.
diff --git a/VERSION b/VERSION
index aac58983..d9e58927 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-17.0.0
+17.1.0
diff --git a/docs/conf.py b/docs/conf.py
index b56209cb..1296da2c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = "2014, Daniele Faraglia"
 # built documents.
 #
 # The short X.Y version.
-version = "17.0.0"
+version = "17.1.0"
 # The full version, including alpha/beta/rc tags.
-release = "17.0.0"
+release = "17.1.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/faker/__init__.py b/faker/__init__.py
index 43e6d1b6..e119878d 100644
--- a/faker/__init__.py
+++ b/faker/__init__.py
@@ -2,6 +2,6 @@ from faker.factory import Factory
 from faker.generator import Generator
 from faker.proxy import Faker
 
-VERSION = "17.0.0"
+VERSION = "17.1.0"
 
 __all__ = ("Factory", "Generator", "Faker")
diff --git a/faker/providers/ssn/lv_LV/__init__.py b/faker/providers/ssn/lv_LV/__init__.py
index 371b1d17..49e7d4aa 100644
--- a/faker/providers/ssn/lv_LV/__init__.py
+++ b/faker/providers/ssn/lv_LV/__init__.py
@@ -1,7 +1,56 @@
-from .. import Provider as BaseProvider
+import datetime
 
+from .. import Provider as SsnProvider
+
+
+class Provider(SsnProvider):
+    def ssn(self, min_age: int = 0, max_age: int = 105) -> str:
+        """
+        Returns 11 character Latvian personal identity code (Personas kods).
+        This function assigns random age to person.
+
+        Personal code consists of eleven characters of the form DDMMYYCZZZQ, where
+        DDMMYY is the date of birth, C the century sign, ZZZ the individual
+        number and Q the control character (checksum). The number for the
+        century is either 0 (1800–1899), 1 (1900–1999), or 2 (2000–2099).
+        """
+
+        def _checksum(ssn_without_checksum):
+            weights = [1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
+            weighted_sum = sum(int(digit) * weight for digit, weight in zip(ssn_without_checksum, weights))
+            reminder = (1 - weighted_sum) % 11
+            if reminder == 10:
+                return 0
+            elif reminder < -1:
+                return reminder + 11
+            return reminder
+
+        age = datetime.timedelta(days=self.generator.random.randrange(min_age * 365, max_age * 365))
+        birthday = datetime.date.today() - age
+        ssn_date = "%02d%02d%s" % (
+            birthday.day,
+            birthday.month,
+            str(birthday.year)[-2:],
+        )
+        century = self._get_century_code(birthday.year)  # Century
+        suffix = self.generator.random.randrange(111, 999)
+        checksum = _checksum(f"{ssn_date}{century:01d}{suffix:03d}")
+        ssn = f"{ssn_date}-{century:01d}{suffix:03d}{checksum:01d}"
+        return ssn
+
+    @staticmethod
+    def _get_century_code(year: int) -> int:
+        """Returns the century code for a given year"""
+        if 2000 <= year < 3000:
+            code = 2
+        elif 1900 <= year < 2000:
+            code = 1
+        elif 1800 <= year < 1900:
+            code = 0
+        else:
+            raise ValueError("SSN do not support people born before the year 1800 or after the year 2999")
+        return code
 
-class Provider(BaseProvider):
     """
     A Faker provider for the Latvian VAT IDs
     """


Test patch:
diff --git a/tests/providers/test_ssn.py b/tests/providers/test_ssn.py
index 996d9b2e..dc338708 100644
--- a/tests/providers/test_ssn.py
+++ b/tests/providers/test_ssn.py
@@ -26,6 +26,7 @@ from faker.providers.ssn.fi_FI import Provider as fi_Provider
 from faker.providers.ssn.fr_FR import calculate_checksum as fr_calculate_checksum
 from faker.providers.ssn.hr_HR import checksum as hr_checksum
 from faker.providers.ssn.it_IT import checksum as it_checksum
+from faker.providers.ssn.lv_LV import Provider as lv_Provider
 from faker.providers.ssn.no_NO import Provider as no_Provider
 from faker.providers.ssn.no_NO import checksum as no_checksum
 from faker.providers.ssn.pl_PL import calculate_month as pl_calculate_mouth
@@ -1236,3 +1237,37 @@ class TestAzAz(unittest.TestCase):
     def check_length(self):
         for sample in self.samples:
             assert len(sample) == 7
+
+
+class TestLvLV(unittest.TestCase):
+    num_sample_runs = 10
+
+    def setUp(self):
+        self.fake = Faker("lv_LV")
+        Faker.seed(0)
+        self.samples = [self.fake.ssn() for _ in range(self.num_sample_runs)]
+        self.provider = lv_Provider
+
+    def test_century_code(self):
+        assert self.provider._get_century_code(1900) == 1
+        assert self.provider._get_century_code(1999) == 1
+        assert self.provider._get_century_code(2000) == 2
+        assert self.provider._get_century_code(2999) == 2
+        assert self.provider._get_century_code(1800) == 0
+        assert self.provider._get_century_code(1899) == 0
+        with pytest.raises(ValueError):
+            self.provider._get_century_code(1799)
+        with pytest.raises(ValueError):
+            self.provider._get_century_code(3000)
+
+    def test_ssn_sanity(self):
+        for age in range(100):
+            self.fake.ssn(min_age=age, max_age=age + 1)
+
+    def check_length(self):
+        for sample in self.samples:
+            assert len(sample) == 12
+
+    def test_vat_id(self):
+        for _ in range(100):
+            assert re.search(r"^LV\d{11}$", self.fake.vat_id())