diff --git a/changelog.d/1499.added.md b/changelog.d/1499.added.md new file mode 100644 index 000000000..f1c38a930 --- /dev/null +++ b/changelog.d/1499.added.md @@ -0,0 +1 @@ +**`Effect.effective_time` returns the time of day a registration became effective.** `Effect.from_xml()` dropped the notice's `finalEffectivenessDispTime`: Lyntris Inc.'s S-1 notice (`9999999995-26-002694`) gave only `2026-08-18`, and now also gives `15:30:00`. The property is `None` when the notice has no time, as on Fitness Fanatics' POS AM notice. `effective_date` now documents that SEC publishes the notice at about 00:15 ET on the next business day. (GH #1499) diff --git a/docs/guides/effect-data-object-guide.md b/docs/guides/effect-data-object-guide.md index d60d0c5ef..3daac5ae8 100644 --- a/docs/guides/effect-data-object-guide.md +++ b/docs/guides/effect-data-object-guide.md @@ -49,6 +49,7 @@ Columns: `cik`, `entity`, `source` (the source form type), `live` (True for prod |----------|------|-------------|---------| | `submission_type` | `str` | Always `"EFFECT"` | `"EFFECT"` | | `effective_date` | `str` | Date the registration was declared effective | `"2024-06-15"` | +| `effective_time` | `str \| None` | Time of day it became effective (Eastern), when the notice gives one | `"15:30:00"` | | `cik` | `str` | CIK of the registrant | `"0000038723"` | | `entity` | `str` | Entity name | `"ACME CORP"` | | `source_submission_type` | `str` | Form type that was made effective | `"S-1"`, `"POS AM"`, `"S-3"` | @@ -68,6 +69,8 @@ Columns: `cik`, `entity`, `source` (the source form type), `live` (True for prod The `effective_date` is returned as a string in `YYYY-MM-DD` format, not a `datetime` object. +An EFFECT notice is not public on its filing date. Its `filing_date` equals `effective_date`, but SEC accepts and publishes the notice at about 00:15 ET on the next business day (Lyntris Inc.: filed 2026-08-18, accepted 2026-08-19 00:15:13). For point-in-time work, use `filing.header.acceptance_datetime`. + ## Related - [Working with Filings](../guides/working-with-filing.md) diff --git a/edgar/offerings/effect.py b/edgar/offerings/effect.py index c2098338e..de343767d 100644 --- a/edgar/offerings/effect.py +++ b/edgar/offerings/effect.py @@ -27,9 +27,11 @@ def __init__(self, accession_no: Optional[str], submission_type: Optional[str], form: Optional[str], - filer: Filer + filer: Filer, + final_effective_time: Optional[str] = None ): self.final_effective_date: str = final_effective_date + self.final_effective_time: Optional[str] = final_effective_time self.file_number: Optional[str] = file_number self.accession_no: Optional[str] = accession_no self.form: Optional[str] = form @@ -55,8 +57,24 @@ def __init__(self, @property def effective_date(self) -> str: + """Date the registration became effective (YYYY-MM-DD). + + The notice's filing date equals this date, but SEC accepts and publishes + the notice at about 00:15 ET on the next business day, so it is not + public on its filing date. For point-in-time use, read + `filing.header.acceptance_datetime`. + """ return self.effectiveness_data.final_effective_date + @property + def effective_time(self) -> Optional[str]: + """Time of day the registration became effective (HH:MM:SS, Eastern), or None. + + The element is optional: the POS AM notices for Fitness Fanatics + (9999999995-25-003344) and 1st Franklin Financial carry none. + """ + return self.effectiveness_data.final_effective_time + @property def cik(self): if self.effectiveness_data.filer: @@ -121,6 +139,8 @@ def to_context(self, detail: str = 'standard') -> str: # === CORE METADATA === lines.append(f"Effective Date: {self.effective_date}") + if self.effective_time: + lines.append(f"Effective Time: {self.effective_time} ET") lines.append(f"Source Form: {self.source_submission_type}") if self.source_accession_no: lines.append(f"Source Accession: {self.source_accession_no}") @@ -136,6 +156,7 @@ def to_context(self, detail: str = 'standard') -> str: lines.append(" .get_source_filing() Navigate to the source filing") lines.append(" .summary() Summary as DataFrame") lines.append(" .effective_date When the filing became effective") + lines.append(" .effective_time Time of day it became effective (ET), if given") lines.append(" .source_submission_type Form type that was made effective") return "\n".join(lines) @@ -186,6 +207,7 @@ def from_xml(cls, is_live=child_text(root, "testOrLive") == 'LIVE', effectiveness_data=EffectiveData( final_effective_date=child_text(effectiveness_el, "finalEffectivenessDispDate"), + final_effective_time=child_text(effectiveness_el, "finalEffectivenessDispTime"), accession_no=child_text(effectiveness_el, "accessionNumber"), file_number=child_text(effectiveness_el, "fileNumber"), submission_type=child_text(effectiveness_el, "submissionType"), diff --git a/tests/issues/regression/test_issue_1499.py b/tests/issues/regression/test_issue_1499.py new file mode 100644 index 000000000..20ab09ccc --- /dev/null +++ b/tests/issues/regression/test_issue_1499.py @@ -0,0 +1,66 @@ +""" +Regression test for GitHub issue #1499: the EFFECT notice's effective time was dropped + +An EFFECT notice for an initial registration carries +beside , and Effect.from_xml() read only the date. +The XML below is Lyntris Inc.'s S-1 notice (9999999995-26-002694) and Fitness +Fanatics' POS AM notice (9999999995-25-003344), as SEC serves them. Lyntris's +filing date is 2026-08-18 but SEC accepted the notice at 2026-08-19 00:15:13; +Fitness Fanatics' is 2025-11-21 (a Friday), accepted 2025-11-24 00:15:20. + +GitHub Issue: https://github.com/dgunning/edgartools/issues/1499 +""" + +from edgar.offerings.effect import Effect + +LYNTRIS_S1_EFFECT = """ + + X0101 + EFFECT + 33 + LIVE + + 2026-08-18 + 15:30:00 +
S-1
+ + 0002132582 + Lyntris Inc. + 333-297657 + +
+
+""" + +FITNESS_FANATICS_POS_AM_EFFECT = """ + + X0101 + EFFECT + 33 + LIVE + + 2025-11-21 + 0001493152-25-019212 + POS AM + + 0002065232 + Fitness Fanatics Ltd + 333-289484 + + + +""" + + +def test_effective_time_is_read_from_the_notice(): + effect = Effect.from_xml(LYNTRIS_S1_EFFECT) + assert effect.effective_date == "2026-08-18" + assert effect.effective_time == "15:30:00" + assert "Effective Time: 15:30:00 ET" in effect.to_context() + + +def test_effective_time_is_none_when_the_notice_has_none(): + effect = Effect.from_xml(FITNESS_FANATICS_POS_AM_EFFECT) + assert effect.effective_date == "2025-11-21" + assert effect.effective_time is None + assert "Effective Time" not in effect.to_context()