The governed definitions this build depends on. Read them first if a term is unfamiliar.
- PrimaryAvailability TimeAvailability time is the earliest evidenced instant at which information was usable by a specified system or decision process.
- ImportantCausal Knowledge CutoffThe time boundary that prevents information unavailable at that moment from influencing a calculation.
- ImportantData LineageData lineage records where data came from, how it was transformed, and which entities, activities, and agents affected it.
- ImportantEffective TimeEffective time is the instant or interval boundary from which a rule, status, or fact applies in its modeled business domain.
- ImportantLate-Arriving DataLate-arriving data is a record that reaches a processing boundary after the expected time for its event, partition, or decision window.
- ImportantPoint-in-Time DatasetA point-in-time dataset can reconstruct what information was valid and available to a specified user or system at a historical decision time.
- ImportantProcessing TimeProcessing time is when a computing system received, transformed, stored, or otherwise handled a record.
- ImportantPublication TimePublication time is when a source officially released or disseminated information.
- ImportantRevision LineageAn ordered chain connecting each correction to the exact earlier record it supersedes.
- ImportantSuffix RecalculationRecomputing a corrected observation and every later output whose recursive state may depend on it.
Practical promise. Reconstruct what a data system could actually have known at a historical cutoff without confusing the period described, the source's timestamps, and your pipeline's usable-data boundary.
A quarterly value may describe June, reach a regulator in August, appear on a public channel moments later, enter a vendor product later still, and become usable by your feature pipeline only after validation. If a historical join stores only “June,” every later revision can masquerade as June knowledge.
The guard in this article solves a narrow, important problem: given immutable revisions and defensible timestamps, it selects the latest observation valid at one cutoff and the latest version knowable at another. It does not guess missing availability times and it does not establish investment significance.
One record, several clocks
The terms are easiest to understand as separate questions:
| Clock or boundary | Question it answers |
|---|---|
| Valid time / observation time | When does the fact apply? |
| Source acceptance | When did the source accept the submission? |
| Public dissemination | When did a declared public channel expose it? |
| Vendor ingestion | When did a vendor capture or transform it? |
| Internal receipt | When did your organization receive it? |
| Transaction time | When did your audited store commit this version? |
| Feature-ready time | When could the downstream calculation actually use it? |
The first is about the world described. Transaction time is about database
history. The intermediate boundaries are about the information's journey.
available_at must name one declared boundary; in this package it means
feature-ready time.
This distinction is not pedantic. The SEC documents separate EDGAR fields for period of report, filing date, and acceptance time, and states that it does not provide a timestamp for when filing content first appears on sec.gov (SEC timestamp FAQ).
The two-cutoff rule
Let (v) be the valid-time cutoff and (k) the knowledge-time cutoff. A record is eligible only when
For each entity and feature, selection then has two ordered maxima:
- choose the greatest eligible
observation_time; - among rows for that observation, choose the greatest tuple
(
available_at,revision,record_id).
Written compactly:
That ordering matters. If a correction to January arrives in March, a global maximum over availability could incorrectly displace an already-known February observation.
A numerical revision example
Suppose one metric describes 2026-02-13. Its three synthetic revisions become feature-ready on February 15, February 22, and March 13.
| Knowledge cutoff | Valid cutoff | Result |
|---|---|---|
| February 20 | February 20 | revision 0 |
| February 25 | February 20 | revision 1 |
| March 15 | February 20 | revision 2 |
At each step, later releases remain stored but ineligible. Equality is
inclusive: a revision whose available_at exactly equals the knowledge cutoff
is eligible.
A real filing demonstrates what remains unknown
Apple's Form 10-Q for the quarter ended 2024-06-29 is a useful historical case because its official dates are visibly different. The SEC filing-detail page reports:
- period of report:
2024-06-29; - accepted:
2024-08-01 18:03:34; and - filing date:
2024-08-02.
These are directly verifiable on the SEC filing-detail page, and the filed 10-Q confirms the reporting period.
What do these facts prove? They prove the filing's period, EDGAR acceptance timestamp, and assigned filing date. They do not prove when a data vendor updated a field, when your downloader received the document, when a warehouse committed it, or when a feature became ready.
The SEC says filings are often available on sec.gov within one to three minutes of the EDGAR timestamp, but it also says the lag can increase and is neither guaranteed nor predictable. Adding one or three minutes to this acceptance time would therefore create a fictional event timestamp, not a verified one. The EDGAR evidence record keeps every unknown explicit.
Build the data contract before the query
The executable record contains entity, feature, observation time,
available_at, revision, finite value, and immutable record ID. Production
lineage should additionally retain every timestamp it truly observes:
acceptance, dissemination, vendor ingestion, internal receipt, transaction,
and feature-ready time.
Do not overwrite one timestamp with another. Store availability_basis with
the record so a reviewer knows whether available_at means public source,
warehouse commit, or feature-ready. If the timestamp is unknown, keep it
unknown and exclude the record from analyses requiring that boundary.
Implementation walkthrough
Both reference implementations follow the same sequence:
validate records and UTC offsets
filter observation_time <= valid cutoff
filter available_at <= knowledge cutoff
group by entity and feature
choose latest observation in each group
choose latest eligible revision for that observation
emit the original record with provenance
The Python and TypeScript implementations accept separate knowledge and valid cutoffs. Omitting the valid cutoff makes it equal to the knowledge cutoff, which is convenient for a “latest value knowable then” query but should remain an explicit design choice.
Use the lab to inspect the boundary
The guided as-of playground starts with a complete canonical snapshot. It provides three scenarios:
- a normal revision chain;
- delayed internal ingestion after public dissemination; and
- separate valid-time and knowledge-time cutoffs.
Use Back and Step to cross one boundary at a time. The audit table shows why each record is selected, blocked by knowledge time, or excluded by valid time. Reset is deterministic, Play uses the same transition function as Step, and reduced-motion mode remains fully usable.
Tests that matter
The shared fixture contains 180 synthetic rows across 3 entities, 20 weekly observations, and 3 releases. Tests verify:
- no selected row exceeds either cutoff;
- an availability equality is eligible;
- valid time can remain in February while knowledge time advances to May;
- a late old correction cannot displace a newer observation;
- malformed timestamps, negative revisions, non-finite values, and duplicate IDs fail explicitly; and
- Python and TypeScript select the same fixture records.
These tests establish agreement with the definition. They do not prove that an upstream timestamp is truthful or that a downstream backtest is unbiased in every other respect.
Common failures
- Joining only on a reporting-period date.
- Treating an official filing date as an intraday availability timestamp.
- Equating source acceptance with consumer feature readiness.
- Replacing a specific observation with a service's “typical” delay.
- Keeping only the latest revision.
- Letting revision numbers override availability order.
- Forgetting that identifiers, units, taxonomies, and calendars also change through knowledge time.
Production checklist
- Declare the valid-time and knowledge-time cutoffs separately.
- State exactly what
available_atmeans for this system. - Preserve raw records and every observed timestamp.
- Keep unknown boundaries unknown.
- Store source, schema, unit, identifier, and transformation versions.
- Test cutoff equality, clock precision, duplicate IDs, late revisions, and empty eligibility sets.
- Monitor the delay from receipt through transaction commit to feature-ready.
- Reproduce snapshots from immutable history before trusting a backtest.
Summary
A point-in-time guard is a two-axis filter followed by a two-stage selection. Valid time answers what period is in scope; knowledge time answers what the declared system could use. Source acceptance, public dissemination, ingestion, transaction time, and feature readiness supply evidence for that second axis, but they are not interchangeable.
Continue with Filing-Revision Versioning to design the immutable version history that this guard queries, and with Asynchronous Return Alignment to align market observations after eligibility is established.
ReferencesPrimary sources and evidence notesExpand the source trail, evidence role, and limitations behind the engineering choices.
Expand the source trail, evidence role, and limitations behind the engineering choices.
SEC-APPLE-10Q-INDEX — Apple 2024 third-quarter Form 10-Q filing detail
- Organization or authors: U.S. Securities and Exchange Commission; filer Apple Inc.
- Source type: Official EDGAR filing-detail record
- Publication or effective date: Accepted 2024-08-01; filing date 2024-08-02
- Version: Accession 0000320193-24-000081
- URL or DOI: https://www.sec.gov/Archives/edgar/data/320193/000032019324000081/0000320193-24-000081-index.htm
- Accessed: 2026-07-22
- Jurisdiction: United States
- Supports: Issuer, form, accession, period of report, acceptance timestamp, and official filing date used in the historical case.
- Limitations: Does not report vendor ingestion, consumer receipt, feature-ready time, or the first instant content appeared on sec.gov.
SEC-APPLE-10Q — Apple Form 10-Q for the period ended 2024-06-29
- Organization or authors: Apple Inc.; filed with the U.S. Securities and Exchange Commission
- Source type: Primary issuer filing in EDGAR
- Publication or effective date: Period ended 2024-06-29; filed 2024-08-02
- Version: Accession 0000320193-24-000081, primary document aapl-20240629.htm
- URL or DOI: https://www.sec.gov/Archives/edgar/data/320193/000032019324000081/aapl-20240629.htm
- Accessed: 2026-07-22
- Jurisdiction: United States
- Supports: The reporting-period fact used as the valid-time anchor.
- Limitations: Filing contents do not establish downstream system availability.
SEC-TIMESTAMPS — Webmaster Frequently Asked Questions
- Organization or authors: U.S. Securities and Exchange Commission
- Source type: Official technical FAQ
- Publication or effective date: Last reviewed 2024-08-23
- Version: Accessed 2026-07-22
- URL or DOI: https://www.sec.gov/about/webmaster-frequently-asked-questions
- Accessed: 2026-07-22
- Jurisdiction: United States
- Supports: EDGAR timestamp definitions; distinction among period of report, filed-as-of date, and acceptance; typical sec.gov lag; absence of a first-public-availability timestamp.
- Limitations: The one-to-three-minute lag is explicitly not guaranteed or predictable and cannot establish availability for an individual consumer.
SEC-APIS — EDGAR Application Programming Interfaces
- Organization or authors: U.S. Securities and Exchange Commission
- Source type: Official API documentation
- Publication or effective date: Last reviewed 2025-04-08
- Version: Accessed 2026-07-22
- URL or DOI: https://www.sec.gov/search-filings/edgar-application-programming-interfaces
- Accessed: 2026-07-22
- Jurisdiction: United States
- Supports: Submissions and XBRL APIs have different typical processing delays; bulk archives have a nightly schedule; APIs update as filings disseminate.
- Limitations: Typical processing delays are not guarantees for a particular request, vendor, or internal pipeline.
SEC-SUBMISSION-GUIDE — Attach and Submit a Filing Through the EDGAR Filing Website
- Organization or authors: U.S. Securities and Exchange Commission
- Source type: Official filer guidance
- Publication or effective date: Last reviewed 2026-01-15
- Version: Accessed 2026-07-22
- URL or DOI: https://www.sec.gov/submit-filings/filer-support-resources/how-do-i-guides/attach-submit-filing-through-edgar-filing-website
- Accessed: 2026-07-22
- Jurisdiction: United States
- Supports: EDGAR may accept and disseminate a submission immediately, and acceptance/suspension is a processing outcome.
- Limitations: It describes possible system behavior, not the observed dissemination time of the Apple case.
ALFRED-VINTAGES — FRED Series Vintage Dates
- Organization or authors: Federal Reserve Bank of St. Louis
- Source type: Official API documentation
- Publication or effective date: Current API documentation
- Version: Accessed 2026-07-22
- URL or DOI: https://fred.stlouisfed.org/docs/api/fred/series/series_vintagedates.html
- Accessed: 2026-07-22
- Jurisdiction: United States
- Supports: Vintage dates represent dates when series values were revised or newly released.
- Limitations: Date-level vintage semantics can be coarser than intraday consumer availability.
Full dependency-light reference implementations in both supported languages.
export interface BitemporalRecord {
entity: string;
feature: string;
observation_time: string;
available_at: string;
revision: number;
value: number;
record_id: string;
[key: string]: unknown;
}
function time(value: string, field = "timestamp"): number {
if (typeof value !== "string" || !/(Z|[+-]\d{2}:\d{2})$/.test(value)) {
throw new Error(`${field} must be an ISO-8601 timestamp with a UTC offset`);
}
const parsed = Date.parse(value);
if (!Number.isFinite(parsed)) throw new Error(`invalid ${field}: ${value}`);
return parsed;
}
function validated(records: BitemporalRecord[]): BitemporalRecord[] {
const seen = new Set<string>();
records.forEach((record, index) => {
for (const field of ["entity", "feature", "record_id"] as const) {
if (typeof record[field] !== "string" || !record[field]) throw new Error(`record ${index} has invalid ${field}`);
}
if (seen.has(record.record_id)) throw new Error(`duplicate record_id: ${record.record_id}`);
seen.add(record.record_id);
time(record.observation_time, "observation_time");
time(record.available_at, "available_at");
if (!Number.isInteger(record.revision) || record.revision < 0) throw new Error(`record ${index} has invalid revision`);
if (!Number.isFinite(record.value)) throw new Error(`record ${index} has invalid value`);
});
return records;
}
export function asOfSnapshot(records: BitemporalRecord[], knowledgeTime: string, validTime?: string) {
const knowledgeCutoff = time(knowledgeTime, "knowledge_time");
const validCutoff = time(validTime ?? knowledgeTime, "valid_time");
const eligible = validated(records).filter(
(record) =>
time(record.observation_time) <= validCutoff &&
time(record.available_at) <= knowledgeCutoff,
);
const groups = new Map<string, BitemporalRecord[]>();
for (const record of eligible) {
const key = `${record.entity}\u0000${record.feature}`;
groups.set(key, [...(groups.get(key) ?? []), record]);
}
return [...groups.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([, rows]) => {
const latestObservation = Math.max(...rows.map((row) => time(row.observation_time)));
return rows
.filter((row) => time(row.observation_time) === latestObservation)
.sort((left, right) =>
time(right.available_at) - time(left.available_at) ||
right.revision - left.revision ||
right.record_id.localeCompare(left.record_id),
)[0];
});
}
export function leakageAudit(records: BitemporalRecord[], knowledgeTime: string, validTime?: string) {
const knowledgeCutoff = time(knowledgeTime, "knowledge_time");
const validCutoff = time(validTime ?? knowledgeTime, "valid_time");
return validated(records)
.filter(
(record) =>
time(record.observation_time) <= validCutoff &&
knowledgeCutoff < time(record.available_at),
)
.map((record) => ({ ...record, reason: "available_after_query_time" }));
}
The embedded lab now expands to its full document height, keeping the article as the only scroll surface.