Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests/test_passport.py::TestEnUS::testDates failure: ValueError: day is out of range for month #1870

Closed
mgorny opened this issue Jun 2, 2023 · 4 comments · Fixed by #1872

Comments

@mgorny
Copy link
Contributor

mgorny commented Jun 2, 2023

  • Faker version: 18.10.0
  • OS: Gentoo Linux amd64

It's possible that I've been incredibly lucky:

>           expiry_date = issue_date.replace(year=issue_date.year + expiry_years)
E           ValueError: day is out of range for month
[…]
expiry_years = 10
issue_date = datetime.datetime(2020, 2, 29, 6, 57, 56)

Full traceback below.

Steps to reproduce

  1. python -m pytest ;-)

Expected behavior

Tests passing ;-).

Actual behavior

_________________________________________________________ TestEnUS.testDates __________________________________________________________

self = <tests.test_passport.TestEnUS object at 0x7f6db2c3a920>, faker = <faker.proxy.Faker object at 0x7f6db21159f0>, num_samples = 20

    def testDates(self, faker, num_samples=20):
        age4 = date.today() - timedelta(days=4 * 365)
        age12 = date.today() - timedelta(days=12 * 365)
        age17 = date.today() - timedelta(days=17 * 365)
        age23 = date.today() - timedelta(days=23 * 365)
        age30 = date.today() - timedelta(days=30 * 365)
    
        birthdays = [(age4, 4), (age12, 12), (age17, 17), (age23, 23), (age30, 30)]
        for _ in range(num_samples):
            for birthday in birthdays:
>               birth_date_f, issue_date_f, expiry_date_f = faker.passport_dates(birthday[0])

_          = 4
age12      = datetime.date(2011, 6, 5)
age17      = datetime.date(2006, 6, 6)
age23      = datetime.date(2000, 6, 7)
age30      = datetime.date(1993, 6, 9)
age4       = datetime.date(2019, 6, 3)
birth_date = datetime.date(2006, 6, 6)
birth_date_f = '06 Jun 2006'
birthday   = (datetime.date(2000, 6, 7), 23)
birthdays  = [(datetime.date(2019, 6, 3), 4),
 (datetime.date(2011, 6, 5), 12),
 (datetime.date(2006, 6, 6), 17),
 (datetime.date(2000, 6, 7), 23),
 (datetime.date(1993, 6, 9), 30)]
expiry_date = datetime.date(2025, 4, 8)
expiry_date_f = '08 Apr 2025'
faker      = <faker.proxy.Faker object at 0x7f6db21159f0>
issue_date = datetime.date(2020, 4, 8)
issue_date_f = '08 Apr 2020'
num_samples = 20
self       = <tests.test_passport.TestEnUS object at 0x7f6db2c3a920>

tests/test_passport.py:55: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <faker.providers.passport.en_US.Provider object at 0x7f6db20938b0>, birthday = datetime.date(2000, 6, 7)

    def passport_dates(self, birthday: date = date.today()) -> Tuple[str, str, str]:
        """Generates a formatted date of birth, issue, and expiration dates.
        issue and expiration dates are conditioned to fall within U.S. standards of 5 and 10 year expirations
    
    
        The ``birthday`` argument is a datetime.date object representing a date of birth.
    
        Sources:
    
        -https://travel.state.gov/content/travel/en/passports/passport-help/faqs.html
        """
        birth_date = birthday.strftime("%d ") + birthday.strftime("%b ") + birthday.strftime("%Y")
        today = date.today()
        age = (today - birthday).days // 365
        if age < 16:
            expiry_years = 5
            issue_date = self.generator.date_time_between(today - timedelta(days=expiry_years * 365 - 1), today)
            # Checks if age is less than 5 so issue date is not before birthdate
            if age < 5:
                issue_date = self.generator.date_time_between(birthday, today)
            expiry_date = issue_date.replace(year=issue_date.year + expiry_years)
    
            issue_date_fromat = issue_date.strftime("%d ") + issue_date.strftime("%b ") + issue_date.strftime("%Y")
            expiry_date_format = expiry_date.strftime("%d ") + expiry_date.strftime("%b ") + expiry_date.strftime("%Y")
            return birth_date, issue_date_fromat, expiry_date_format
        elif age >= 26:
            expiry_years = 10
            issue_date = self.generator.date_time_between(today - timedelta(days=expiry_years * 365 - 1), today)
            expiry_date = issue_date.replace(year=issue_date.year + expiry_years)
            issue_date_fromat = issue_date.strftime("%d ") + issue_date.strftime("%b ") + issue_date.strftime("%Y")
            expiry_date_format = expiry_date.strftime("%d ") + expiry_date.strftime("%b ") + expiry_date.strftime("%Y")
            return birth_date, issue_date_fromat, expiry_date_format
    
        else:
            # In cases between age 16 and 26, the issue date is 5 years ago, but expiry may be in 10 or 5 years
            expiry_years = 5
            issue_date = self.generator.date_time_between(
                today - timedelta(days=expiry_years * 365 - 1), birthday + timedelta(days=16 * 365 - 1)
            )
            # all people over 21 must have been over 16 when they recieved passport or it will be expired otherwise
            if age >= 21:
                issue_date = self.generator.date_time_between(today - timedelta(days=expiry_years * 365 - 1), today)
                expiry_years = 10
    
>           expiry_date = issue_date.replace(year=issue_date.year + expiry_years)
E           ValueError: day is out of range for month

age        = 23
birth_date = '07 Jun 2000'
birthday   = datetime.date(2000, 6, 7)
expiry_years = 10
issue_date = datetime.datetime(2020, 2, 29, 6, 57, 56)
self       = <faker.providers.passport.en_US.Provider object at 0x7f6db20938b0>
today      = datetime.date(2023, 6, 2)

faker/providers/passport/en_US/__init__.py:69: ValueError
@stefan6419846
Copy link
Contributor

Thanks for the report. Forced date replacement with leap years is always tricky (I remember a similar issue in another library: FactoryBoy/factory_boy#969).

Would you be interested in submitting a corresponding PR to fix this?

@mgorny
Copy link
Contributor Author

mgorny commented Jun 2, 2023

I suppose I could do that. Should the date move to the next day or the previous day?

@mgorny
Copy link
Contributor Author

mgorny commented Jun 2, 2023

CC @llw2128

@fcurella
Copy link
Collaborator

fcurella commented Jun 2, 2023

let's the previous day, but on the issue date. IE: let's special-case issue date of Feb 29th, and make it Feb 28th

mgorny added a commit to mgorny/faker that referenced this issue Jun 2, 2023
Fix handling the issue date of 29 Feb (of a leap year).  Since
the passports expire in 5 or 10 years, the expiration year will not be
a leap year, and therefore the date needs to be adjusted to 28 Feb.

Fixes joke2k#1870
fcurella pushed a commit that referenced this issue Jun 2, 2023
* Refactor common code in passport_dates() in en_US Passport provider

* Fix handling 29 Feb in en_US Passport provider

Fix handling the issue date of 29 Feb (of a leap year).  Since
the passports expire in 5 or 10 years, the expiration year will not be
a leap year, and therefore the date needs to be adjusted to 28 Feb.

Fixes #1870
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants