Skip to content

fix(calendar): decode bare year ipoDate as a year across all SDKs - #395

Merged
benbenbang merged 3 commits into
mainfrom
fix/379-ipo-date-year
Sep 30, 2026
Merged

benbenbang merged 3 commits into
mainfrom
fix/379-ipo-date-year

Conversation

@benbenbang

@benbenbang benbenbang commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

The provider sometimes sends ipoDate on IPO prospectus rows as a bare year (e.g. "2020") instead of a full YYYY-MM-DD date, which previously failed to decode. This introduces a shared DateOrYear type across the Rust, Python, and Go SDKs so a full date stays a date, a bare year is preserved as a year, and each is re-encoded in the shape it was received.

What changed:

  • Added a DateOrYear codec in libfmp that parses, serialises, and deserialises both the date and bare-year forms, reports which arrived via as_date / as_year, and rejects malformed input without echoing the offending value.
  • Switched IpoProspectus.ipo_date from Date to DateOrYear and mapped it consistently: datetime.date | int in Python (with to_dict and pickling support) and fmp.DateOrYear in Go with .Date() / .Year() accessors.
  • Added a Go DateOrYear temporal type mirroring the Rust crate, with parsing, accessors, and text/JSON encoding that rejects the zero value and non-string JSON.
  • Recorded the decision in ADR 0034 and referenced it from the shared wire type, Go SDK, and design docs to keep the language mappings aligned.
  • Added Rust, Python, and Go tests covering year-only, full-date, and invalid inputs, verifying byte-identical round-trips and that errors never echo the raw value.

Closes #379

BREAKING CHANGE: IpoProspectus.ipo_date is now DateOrYear in Rust (match Date or Year, or use as_date/as_year), datetime.date | int in Python (a bare year is an int), and fmp.DateOrYear in Go (use .Date() / .Year()).

close #379

Add a `DateOrYear` codec that keeps a bare `YYYY` year as a year and never
widens it to a date. It parses, serialises, and deserialises the two
documented forms, exposes `as_date` / `as_year` to report which arrived,
and rejects malformed input without echoing the offending value.

Record the decision in ADR 0034 and reference it from the shared wire
type, Go SDK, and design docs so Python (`datetime.date | int`) and Go
(`fmp.DateOrYear`) mappings stay aligned.
Introduce a DateOrYear type that represents a member documented as a
"YYYY-MM-DD" date but sometimes sent by the provider as a bare "YYYY"
year. It mirrors the DateOrYear type in the Rust crate: a year stays a
year and is never widened to a date, and the zero value holds neither
form and cannot be encoded.

**New capability:**
* Added `ParseDateOrYear` accepting exactly a "YYYY-MM-DD" date or four
  ASCII digits, the same two shapes the Rust crate accepts.
* Added `Date`, `Year`, `IsZero`, and `String` accessors that report the
  form that arrived and return the exact wire text.
* Added text and JSON encoding via `MarshalText`, `UnmarshalText`,
  `MarshalJSONTo`, and `UnmarshalJSONFrom`, rejecting the zero value and
  any non-string JSON kind including null.

**Testing:**
* Added tests verifying the parsed form is preserved, invalid shapes are
  rejected, JSON round-trips are byte-identical, and errors do not echo
  the offending value.
The provider sometimes sends `ipoDate` as a bare year (e.g. `"2020"`)
instead of a full date on IPO prospectus rows, which previously failed to
decode. Introduce a `DateOrYear` type across all three SDKs so a full date
stays a date and a bare year is preserved as a year.

**Response decoding:**
* Change `IpoProspectus.ipo_date` in `libfmp` from `Date` to the new
  `DateOrYear` codec, which decodes either a full date or a bare year and
  re-encodes it in the shape it was received.

**Python bindings:**
* Add a `DateOrYear` PyO3 enum exposing the member as `datetime.date | int`,
  wire it into the `IpoProspectus` model and stub, and support it in
  `to_dict` and pickling.

**Go SDK:**
* Change `IPOProspectus.IPODate` to `DateOrYear`, exposing `.Date()` and
  `.Year()` accessors.

**Tests:**
* Add Rust, Python, and Go tests covering a year-only `ipoDate`, a full
  date, and an invalid value that must not echo the raw input in its error.

Closes #379

BREAKING CHANGE: IpoProspectus.ipo_date is now DateOrYear in Rust (match Date or Year, or use as_date/as_year), datetime.date | int in Python (a bare year is an int), and fmp.DateOrYear in Go (use .Date() / .Year()).

close #379
@benbenbang
benbenbang enabled auto-merge (rebase) September 30, 2026 14:53
@github-actions github-actions Bot added documentation Improvements or additions to documentation enhancement New feature or request labels Sep 30, 2026
@benbenbang
benbenbang merged commit ac0c07b into main Sep 30, 2026
9 of 16 checks passed
@benbenbang
benbenbang deleted the fix/379-ipo-date-year branch September 30, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix!: IpoProspectus.ipoDate can be year-only

1 participant