diff --git a/stdnum/gs1_128.py b/stdnum/gs1_128.py index 001dd584..03cff8a4 100644 --- a/stdnum/gs1_128.py +++ b/stdnum/gs1_128.py @@ -122,8 +122,15 @@ def _encode_date(fmt: str, value: object) -> str: # Format date in different formats if fmt in ('N6', 'N6..12', 'N6[+N6]'): return value.strftime('%y%m%d') + elif fmt == 'N8': + # Date with a four-digit year (YYYYMMDD), e.g. AI 7250 (DOB). + return value.strftime('%Y%m%d') elif fmt == 'N10': return value.strftime('%y%m%d%H%M') + elif fmt == 'N12': + # Date and time with a four-digit year (YYYYMMDDhhmm), e.g. AI 7251 + # (DOB TIME). + return value.strftime('%Y%m%d%H%M') elif fmt in ('N6+N..4', 'N6[+N..4]', 'N6[+N4]'): value = value.strftime('%y%m%d%H%M') if value.endswith('00'): @@ -185,7 +192,14 @@ def _decode_decimal(ai: str, fmt: str, value: str) -> decimal.Decimal | tuple[st def _decode_date(fmt: str, value: str) -> datetime.date | datetime.datetime | tuple[datetime.date, datetime.date]: """Decode the specified date value given the fmt.""" - if len(value) == 6: + if fmt == 'N8': + # Date with a four-digit year (YYYYMMDD), e.g. AI 7250 (DOB). + return datetime.datetime.strptime(value, '%Y%m%d').date() + elif fmt == 'N12': + # Date and time with a four-digit year (YYYYMMDDhhmm), e.g. AI 7251 + # (DOB TIME). This is a single datetime, not two YYMMDD dates. + return datetime.datetime.strptime(value, '%Y%m%d%H%M') + elif len(value) == 6: if value[4:] == '00': # When day == '00', it must be interpreted as last day of month date = datetime.datetime.strptime(value[:4], '%y%m') @@ -196,7 +210,7 @@ def _decode_date(fmt: str, value: str) -> datetime.date | datetime.datetime | tu return date.date() else: return datetime.datetime.strptime(value, '%y%m%d').date() - elif len(value) == 12 and fmt in ('N12', 'N6..12', 'N6[+N6]'): + elif len(value) == 12 and fmt in ('N6..12', 'N6[+N6]'): return (_decode_date('N6', value[:6]), _decode_date('N6', value[6:])) # type: ignore[return-value] else: # Other lengths are interpreted as variable-length datetime values diff --git a/tests/test_gs1_128.doctest b/tests/test_gs1_128.doctest index 7036d271..98172f4a 100644 --- a/tests/test_gs1_128.doctest +++ b/tests/test_gs1_128.doctest @@ -94,6 +94,10 @@ We generate dates in various formats, depending on the AI. '(7011)181119' >>> gs1_128.encode({'7011': datetime.datetime(2018, 11, 19, 12, 45)}, parentheses=True) '(7011)1811191245' +>>> gs1_128.encode({'7250': datetime.date(1980, 7, 15)}, parentheses=True) # four-digit year +'(7250)19800715' +>>> gs1_128.encode({'7251': datetime.datetime(1980, 7, 15, 14, 30)}, parentheses=True) +'(7251)198007151430' If we try to encode an invalid EAN we will get an error. @@ -154,6 +158,10 @@ We an decode date files from various formats. {'7011': datetime.date(2018, 11, 19)} >>> pprint.pprint(gs1_128.info('(7011)1811191245')) {'7011': datetime.datetime(2018, 11, 19, 12, 45)} +>>> pprint.pprint(gs1_128.info('(7250)19800715')) +{'7250': datetime.date(1980, 7, 15)} +>>> pprint.pprint(gs1_128.info('(7251)198007151430')) +{'7251': datetime.datetime(1980, 7, 15, 14, 30)} While the compact() function can clean up the number somewhat the validate()