h3 / python-libvin

A fork/rewrite of vinlib: http://pypi.python.org/pypi/vinlib/ (https://github.com/lszyba1/vinlib)
55 stars 36 forks source link

Model year incorrectly identified for certain heavy vehicles #24

Open emesterhazy opened 6 years ago

emesterhazy commented 6 years ago

Issue

The current implementation of Vin.year returns the incorrect year for some heavy trucks and vans:

In [1]: from libvin import Vin

In [2]: v = Vin('1FUJGLDR69LAC9984')

In [3]: v.year
Out[3]: 2039

In [4]: v.is_pre_2010
Out[4]: False

The correct model year for this VIN is 2009 (source).

For vehicles with a gross vehicle weight rating (GVWR) over 10,000 lbs the 7th digit of the VIN does not indicate if the vehicles was manufactured before or after 2009 as it does for passenger vehicles.

Potential solutions

One potential solution(https://github.com/emesterhazy/python-libvin/commit/58de6905448b774de2127944b76198400b3f46e7) is to check Vin.year and overwrite the is_pre_2010 condition if the model year is more than X years into the future. For the example above, the fix would check the model year when the Vin object is created, and overwrite the is_pre_2010 property since 2039 is too far into the future. Then when Vin.year is accessed it will return the correct year (2009), and Vin.is_pre_2010 will return True.

class Vin(object):
    def __init__(self, vin):
        self.vin = vin.upper()

        # Overwrite is_pre_2010 attribute if it would result in a model year
        # more than 2 years into the future. See notes in is_pre_2010
        self._pre_2010_overwrite = False
        if (not self.is_pre_2010 and
                YEARS_CODES_PRE_2040[self.vin[9]] >= datetime.now().year + 2):
            self._pre_2010_overwrite = True

...

    @property
        def is_pre_2010(self):

            if self._pre_2010_overwrite:
                return True
            else:
                return self.vin[6].isdigit()

The downside to this is that the fix will also "correct" valid passenger car VINs for vehicles with model years more than 2 years into the future. However, I don't think this will be an issue in real world use.

I have a pull request ready if you agree with this approach.

iamdavehawkins commented 6 years ago

Nice find on this issue, I had no idea!

Is there a way to identify (from the VIN) if a vehicle is over 10,000 lb GVWR? I feel like an approach that calculates the model-year-related props differently depending on a prop/flag like _is_gvwr_gt10000 might be nice.

Do you know if your proposal (checking if the "had-this-been-a-light-duty-vehicle" model year is 2 years in the future) is robust for all heavy vehicles for all historical model years? Also - should we depend on the system time being accurate in order to decode a vin successfully? Thanks.

emesterhazy commented 6 years ago

I agree with your suggestion - it would be best to identify the GVWR directly from the VIN and adjust accordingly. Relying on the system time isn't ideal if there is another work around.

It looks like this may not be possible though. I did a quick read through the Part 565 VIN specification and it looks like there is no requirement to include the GVWR in the VIN number.

Relevant quote: §565.15(b)

In submitting the required information to NHTSA relating gross vehicle weight rating, the designations in Table II shall be used. The use of these designations within the VIN itself is not required.

There is also another issue that my solution does not address. Heavy trucks which were manufactured before 2009 and have a letter in position 7 may still have the wrong year reported. Since the libvin looks at position 7 to decide which model year lookup table to use, a vehicle manufactured in 1984 (E in position 10) will be erroneously identified as a 2014 model year. This is not addressed by my fix since 2014 is less than 2018 (current year). See model year encoding table.

It seems we may not be able to completely fix the model year decode for heavy trucks due to limitations in the information contained within the VIN number. Thoughts?

h3 commented 5 years ago

@emesterhazy Thanks for the infos, I'll be glad to accept your merge request.

From what I understand the best scenario (if possible) is to use GVWR first if it's specified and fallback on the year method if it isn't.

The model year for vehicles with a GVWR greater than 10,000 lb (4,500 kg), as well as buses, motorcycles, trailers and low-speed vehicles, may no longer be identified within a 30-year range. VIN characters 1–8 and 10 that were assigned from 1980–2009 can be repeated beginning with the 2010 model year.

The VIN specification is really a mess and the older the VIN the messier it gets :|

emesterhazy commented 5 years ago

@h3 It's been a while since I looked into this. From what I remember, the problem is that GVWR is cannot be decoded from the VIN number. See the quote in my last comment above.

I have a potential solution to this issue (see top comment), but as @iamdavehawkins pointed out it relies on the system time being accurate. I'm not sure how NHTSA's decoder addresses this issue, but my guess is that it has access to databases that go beyond the info this lib has in static.py.

Happy to submit a PR for https://github.com/emesterhazy/python-libvin/commit/58de6905448b774de2127944b76198400b3f46e7 if you like. Unless I missed something in the VIN Specification there doesn't seem to be any other way to infer the correct model year for these heavy vehicles directly from the VIN.