A date, a currency amount and a scientific measurement are all values, but they need different grammars. A permissive constructor can accept spellings your feed never promised. Guessing a locale can turn 03/04 into either March 4 or April 3. Dropping punctuation can turn malformed currency into a plausible amount. Write down the allowed spelling, range and precision before converting it.
The build has three independent parsers. Each raises ValueError for rejected
input; none uses zero as an error sentinel. Boundary whitespace is stripped from
text, but internal notation must match its own policy. This lets a caller decide
whether to reject the whole document, record a row error or ask for correction.
A parser should not secretly choose that downstream workflow.
Dates need both syntax and a calendar
date.fromisoformat accepts several ISO forms, including compact dates. A feed
requiring exactly YYYY-MM-DD needs an ASCII shape check before that constructor.
The constructor then rejects impossible days such as 2023-02-29. A regex alone
cannot establish leap-year validity. Return the canonical date.isoformat()
string so downstream comparisons have a consistent spelling.
A calendar date is not an instant. This lesson does not assign midnight, a time zone or a UTC offset to it. Event timestamps require separate rules for offsets, ambiguous local times and normalization. Do not pretend a date-only field carries that information.
from datetime import date
from decimal import Decimal
print(date.fromisoformat('2024-02-29').isoformat())
print(int(Decimal('0.29') * 100))
print(int(float('0.29') * 100))The exact decimal path returns 29 cents; the float path can truncate a binary approximation to 28. Construct Decimal from text, not from an already-rounded float. The build uses a local precision sized for its bounded input so a lower ambient precision does not round away cents.
Currency punctuation is a grammar
The exercise accepts USD-style comma groups, optional dollar signs, an optional
leading minus and at most two fractional digits. -$1,000.20 is valid; $-1,
1,00, .50 and 1.001 are rejected. Removing all commas without checking their
groups would repair an unknown notation without evidence. Supporting other
currencies, locale conventions, exchange rates or accounting rounding requires
new policy rather than another broad replacement.
The general measurement parser deliberately uses Python's float grammar and then
requires math.isfinite. NaN and infinities are valid float objects, yet invalid
observations here. An enormous exponent can produce infinity without a parse
error. Also reject booleans before conversion, and handle conversion overflow
from a huge integer.
In practice, test a leap day in a century year, a compact date, negative five cents, malformed comma grouping, a zero amount and a non-finite exponent. Explain why strict grammar, exact arithmetic and finiteness are three different checks.