Portrait of Dmytro Nasyrov wearing a dark suit and light blue shirt against a dark background.

Production Architecture with Dmytro Nasyrov

Archives
Software development
About Dmytro Nasyrov
Log in
Subscribe
November 2, 2026

Reporting Time: Keep the Event Clock and Business Day

Two 02:30 events fall an hour apart. Use an event-to-day record to preserve offsets, reporting cutoffs and late-arrival decisions.

A report can preserve every event and still put the right events in the wrong day. The source clock, the receiving system and the reporting calendar answer different questions. Keep the original event timestamp, identify the instant it represents and assign that instant to a versioned business-day interval. Record arrival time separately so a delayed message cannot silently become today's activity.

The decision becomes visible when clocks move backward. Two events can both display 02:30 while occurring an hour apart. A report that keeps only the date and local time cannot distinguish them. A report that keeps only UTC can distinguish the instants, but still needs the reporting zone and cutoff to explain their business-day labels.

Start with the reporting boundary in the source

Gambit Stream's reconciliation evidence article, by Maksym Diduk, separates a defined reporting period from a later reconciliation close. It also calls for recording the configuration baseline used by a reconciliation run. These are useful engineering distinctions: period membership, waiting for delayed records and reproducing the rule set are separate responsibilities.

The source's operating targets are its own proposed framing. They do not supply a universal reporting calendar or establish how every platform assigns event timestamps. This article takes the boundary problem into a general software-reporting example, with no jurisdiction-specific reporting advice.

The fictional report R-27 below uses Europe/Berlin, a 04:00 local cutoff and a two-hour arrival allowance. Those choices belong to the example. They are not reported settings of a Gambit Stream deployment. The purpose is to make one assignment reproducible from its inputs and to show what must remain unchanged when a late event or a rule revision appears.

Keep occurrence, receipt and publication on separate clocks

An occurrence timestamp describes the event the source says happened. A received timestamp describes when a particular system observed its message. A report publication timestamp describes when a selected result became available. A retry can change the second observation without changing the underlying occurrence. Republishing can change the third without moving the event into another day.

Name the event whose occurrence matters. A request created, a request accepted and an operation completed may be different events with different timestamps. Selecting the completion time for one row and the request time for another makes a daily total inconsistent even if every conversion to UTC is correct. Put that selection rule in the report definition. Retain the receiving boundary as well. First receipt at an integration gateway and ingestion into an analytical store are different observations. Recording both can locate delay. Replacing the first with the second on each replay destroys that evidence. A reprocessing timestamp belongs in the processing history, alongside the original receipt record.

Apache Beam's model documentation distinguishes event time from processing time and explains why arrival order can differ from event order. That distinction is useful beyond streaming frameworks. It does not determine which business event your report should count or authorize a particular late-data policy. Those decisions remain part of the report contract.

Also separate data quality from transport delay. If a receiving timestamp appears earlier than the claimed occurrence, investigate clock error, a future-dated event or an interpretation mistake. Silently clamping the difference to zero makes a latency chart cleaner while hiding the evidence needed to explain the row.

Preserve the timestamp before interpreting it

Keep the exact source representation and the field's declared meaning. A timestamp with an explicit offset can identify an instant under the supported timestamp format. A date and time with no offset may require additional source context. The receiving server's default zone is not evidence of what the sender meant.

RFC 3339 states:

Numeric offsets are calculated as "local time minus UTC".

IETF, Date and Time on the Internet: Timestamps, section 4.2

For the example, 2026-10-25T02:30:00+02:00 corresponds to 2026-10-25T00:30:00Z. A second value, 2026-10-25T02:30:00+01:00, corresponds to 2026-10-25T01:30:00Z. Removing the offset before normalization discards the distinction. Sorting the displayed local values cannot recover it.

An offset is not a named time zone's complete rule history. Keep the source's declared zone separately when supplied, and keep the report's selected zone separately again. They may serve different purposes. A source can serialize a UTC instant while an operator reports it against a regional business calendar.

If an offset and a named zone conflict, preserve both inputs and surface the inconsistency. RFC 9557 explicitly addresses inconsistent offset and time-zone information in its extended timestamp format. Do not silently reinterpret a source field to make the inconsistency disappear. Define the accepted formats and the error path before importing the data.

Resolve a business day into an interval

A business-day label needs a rule. Specify the reporting zone, the local cutoff and the naming convention for the interval. R-27 labels a day by the local date on which its 04:00 interval starts. An event before the next day's cutoff therefore retains the previous starting date's label. Another report could use a different convention, so the label alone is insufficient.

Resolve the start boundary and the next boundary independently using the selected zone rules. Convert both to UTC and classify the event against that interval. Use an inclusive start and exclusive end: an instant equal to the end belongs to the next interval. This avoids counting a boundary event in two adjacent days.

Do not construct every business day by adding 24 elapsed hours to the previous UTC start. Regional clock changes can make the elapsed duration different. Nor should an implementation subtract a fixed elapsed offset from an event and assume the resulting date always matches the intended local cutoff. Build the calendar boundaries first, then compare instants.

A cutoff can itself be ambiguous or nonexistent on a transition date. A calendar using 02:30 needs an explicit rule for a repeated or skipped 02:30. Possible product choices include rejecting the configuration or resolving it according to a documented policy. Choosing one requires the report owner's agreement; a date library's default is not that agreement.

Version this business rule independently from the event schema. The same unmodified source event can receive a different derived day if the report zone, cutoff or naming convention changes. A stored assignment should identify the rule version that produced it so a later reviewer can explain the result without guessing which configuration was active.

Follow two 02:30 events through a 25-hour day

R-27's illustrative business day starts on October 24, 2026, at 04:00 in Europe/Berlin and ends at the next local 04:00. Under the timezone data used for the local checks, the start is 2026-10-24T02:00:00Z and the end is 2026-10-25T03:00:00Z. The elapsed interval is 25 hours.

Event Original source timestamp Normalized instant Assigned business day
E-101 2026-10-25T02:30:00+02:00 2026-10-25T00:30:00Z 2026-10-24
E-102 2026-10-25T02:30:00+01:00 2026-10-25T01:30:00Z 2026-10-24
E-103 2026-10-25T04:00:00+01:00 2026-10-25T03:00:00Z 2026-10-25

E-101 and E-102 share a local wall-clock display. Their explicit offsets establish different instants, separated by one hour. Both fall inside the October 24 interval. E-103 is exactly the exclusive end, so it starts the next business day. No event needs its occurrence timestamp rewritten to achieve those assignments.

A midnight-based grouping would answer a different question. All three source strings carry October 25 as their local calendar date, while this report intentionally groups the first two into the interval labeled October 24. Neither label should be presented without its defining calendar. Otherwise two correct reports can appear contradictory because their headings conceal different membership rules.

The dates and conversions in this example were checked in a small local model. That check establishes the arithmetic and interval behavior under the recorded timezone data. It does not verify a provider feed, an operator's reporting definition or a deployed data pipeline. Preserve that scope when reusing the example in a specification.

Diagnose the repeated-time row before changing the report

Suppose a reviewer sees E-101 and E-102 beside identical 02:30 displays and suspects duplication. Start with event identity, then compare the full source timestamps. The offsets show different instants. Removing one row because the display matches would change membership without evidence that the source event repeated.

Next compare the challenged revision. A copy of R-27 revision 1 contains one event; the approved revision 2 contains two. A reader holding the earlier file can correctly report a count of one while a current dashboard reports two. Check the revision identifiers before treating that disagreement as another ingestion failure. Re-running the import in response could create a new problem while leaving the publication mismatch unexplained.

The useful repair depends on the finding. A missing offset calls for source clarification. A hidden report revision calls for visible version information. A repeated delivery calls for the source's deduplication rule. These cases can produce similar screenshots, but they do not justify the same data edit.

Keep one event-to-day assignment record

The reusable object is an assignment record, separate from the immutable source message. It should let a reviewer move from a report row back to the original time evidence and forward to the exact published revision that included it.

Field Fictional E-102 entry Decision supported
Event identity source S, event E-102 Distinguish an event from a delivery attempt
Original occurrence 2026-10-25T02:30:00+01:00 Preserve the sender's stated time and offset
Normalized instant 2026-10-25T01:30:00Z Compare against absolute interval boundaries
First receipt 2026-10-25T05:10:00Z Evaluate arrival against the close policy
Reporting calendar Europe/Berlin, 04:00, rule C-7 Explain zone, cutoff and date-label convention
Interval and label start 02:00Z Oct 24; end 03:00Z Oct 25; label Oct 24 Reproduce membership without server defaults
Arrival disposition after two-hour allowance; amendment review Keep lateness separate from occurrence
Report membership R-27 revision 2 after approval Identify the result that actually contains the row

Retain the timezone-data release or a reproducible rules artifact with C-7. If the runtime cannot expose a release identifier, capture the deployed data package through the build manifest rather than inventing a version. Storing the already resolved boundaries provides additional evidence of the decision made at that time.

An unresolved timestamp deserves an unresolved assignment. Keep a reason code and a route for resolving it. A null business day is more honest than a plausible date derived from an unknown sender zone. Make unresolved rows visible beside the report's coverage information so they cannot disappear from a successful-import count.

Let late arrival change disposition, not occurrence

In the fictional policy, the October 24 interval ends at 03:00Z on October 25. The two-hour allowance ends at 05:00Z. E-102 first arrives at 05:10Z, after that allowance. Its event time still places it inside the October 24 interval. Its arrival determines how the closed report handles it.

For R-27, revision 1 contains E-101 but not the unavailable E-102. A reviewer later approves an amendment that includes E-102 in revision 2. The illustrative event count changes from one to two. Retain the original revision and identify the added event. Do not overwrite revision 1 and leave its earlier readers with no explanation for the changed count.

Other report contracts might hold the result provisional longer, reject an unsupported record or expose a separate adjustment view. Whatever the choice, retain the event-time classification and the publication disposition as separate fields. Receiving yesterday's event today should not become an undocumented reason to classify its occurrence as today.

A watermark can support an estimate of completeness in a streaming system. It is not independent proof that every source event exists in the pipeline. Monitor missing sequences or expected source batches where those signals exist, and state what the source actually guarantees. A completed timer cannot establish completeness for a source whose delivery obligations were never defined.

Repeat delivery is a different condition. The same event arriving again after close does not automatically become another late adjustment. Use the source's identity and revision semantics to distinguish a duplicate message from a corrected event. A timestamp match alone is inadequate: the example already contains two legitimate events with the same displayed local time.

Treat corrections as new evidence

A correction can change the event's timestamp, its business status or its relationship to another event. Preserve the original source value and record the correcting evidence. An authorized timestamp correction may move an event between intervals. A new receipt timestamp caused by replay should not do so on its own.

Specify which event each report measures. A report of completed operations, a report of incoming messages and a report of adjustments can intentionally use different clocks. Their dates need not match. Name those clocks in the report definitions so a reconciliation does not try to force distinct business questions into one daily total.

Keep an amendment reason and its effective scope. If a corrected occurrence moves E-102 into another day, the publication process needs to explain removal from one membership set and addition to the other. Avoid a one-sided correction that changes only the newly selected report and leaves the old report's row untouched.

Separate a historical correction from a calendar-policy change. The first says the previous input or interpretation was wrong for that event. The second says a different rule now applies. Both may require new derived assignments, but they should not share an undifferentiated last-updated field that conceals why a previously published day changed.

Make an export reproducible from its membership

A file named for October 24 is not a complete reporting specification. Include the calendar identifier, resolved UTC interval and rule version in its manifest. Record the report revision and the selection or snapshot that fixed its membership. Generation time describes when the file was built; it should not masquerade as the period it covers.

Expose the original timestamp and the normalized instant where the report's audience needs both. A human-readable local column should include its offset or another unambiguous representation when repeated times are possible. Keep machine-oriented fields distinct from localized display text so a spreadsheet import does not quietly erase the offset.

To reproduce revision 1, use its original membership and historical interpretation rules. To produce a current restatement, select and label a new revision. Running today's query against mutable tables and today's zone configuration may be useful analysis, but it is not evidence that the old file can be reproduced.

IANA's timezone database theory explains the database's scope and the practical limits of representing civil-time history. Capture the actual rules used by the application. A familiar zone name is not a complete deployment artifact, and a new runtime can ship different data under the same identifier.

Exercise the boundaries that can change a day

Start with the interval edges: immediately before the start, exactly at the start, immediately before the end and exactly at the end. The last belongs to the next interval. Choose test precision that matches the service contract. A whole-second fixture does not verify a system that accepts finer-grained timestamps at its boundary.

Then exercise repeated and missing local times. Keep both explicit-offset 02:30 events from the example. Supply an unqualified repeated time and require the configured ambiguity path. Supply a cutoff that falls in a skipped local period and require the calendar's documented resolution. Do not let a passing conversion test stand in for a policy that has never been chosen.

The opposite seasonal transition matters too. For the same 04:00 cutoff, the example interval starting March 28, 2026, in Europe/Berlin ends after 23 elapsed hours. Check adjacent intervals for gaps and overlaps in UTC. Coverage should remain continuous even when their durations differ.

Test a repeated message, a corrected source timestamp and a rule change effective at a future boundary. A replay should preserve first receipt; a correction should retain its predecessor; a rule change should not silently rewrite published membership. These are proposed acceptance conditions for an implementation. The local checks accompanying this article cover the illustrative arithmetic and classification, not a production acceptance run.

Stop an assignment when the required evidence is missing

An imported row can be syntactically valid while lacking the information the report needs. A source field may contain a calendar date without a time, an offset-free local timestamp or an occurrence time whose business meaning changed between schema versions. Preserve what arrived and state which assignment input is missing. Avoid turning a parser's successful return into a claim of sufficient evidence.

For an unresolved row, retain its event reference and source-schema version, the missing fact and the responsible resolution path. If the report excludes it, disclose the excluded count and reason alongside the published coverage. A successful daily job with unresolved rows is a different result from a complete report with no exceptions.

Resolution should be attributable. Record the supplied offset, corrected field meaning or approved calendar interpretation, together with the evidence that justified it. Then derive a new assignment and apply the report's amendment process. Keep the earlier unresolved state so a reviewer can reconstruct why revision 1 lacked the event and revision 2 included it.

A manual override needs the same discipline.

Change a calendar without rewriting history

Give a reporting-calendar change an effective boundary and an owner. A new cutoff introduced in the middle of an existing interval creates an awkward transition that needs an explicit migration rule. Prefer a reviewed sequence of old and new intervals whose membership can be checked for omissions and duplication.

Run a comparison before activating the change. Identify which events would move between labels under the candidate calendar. Distinguish the expected reclassification from missing or duplicated source records. A total that remains unchanged can conceal large movements between days, so inspect membership differences as well as aggregate counts.

Retain the current published result while the candidate is reviewed. If the change is rejected, the source events should remain intact. Rolling back a configuration setting does not automatically restore report revisions already issued under it. Any withdrawal or replacement needs its own publication decision and visible lineage.

The release packet should contain the old and candidate calendar definitions, their resolved boundaries and a small set of attributable changed assignments. Add the acceptance conditions that remain untested. This gives the report owner something concrete to approve without implying that a timezone-data update or a clean query run supplied business authorization.

Hand off an assignment someone else can reproduce

For the next reporting discrepancy, ask for one original event, its first receipt observation and the report revision being challenged. Add the calendar rule and its resolved interval. This packet distinguishes a timestamp interpretation defect from a late-arrival decision or a report-version mismatch before anyone changes the data.

Repeat the check when a source changes timestamp formats, a report adopts a new cutoff, a runtime updates timezone data or a correction moves an occurrence across an interval boundary. Preserve the event clock and the business-day decision together. A reviewer should be able to reconstruct the label while still seeing exactly when the source said the event happened.

More insights to read

  • The Balance Is Right, Now Prove the Settlement Was
  • Working with Time and Timezones (Group Draft Note)
  • Date and Time on the Internet: Timestamps
  • Basics of the Beam model
  • Theory and pragmatics of the tz code and data

About the author

Portrait of Dmytro Nasyrov wearing a dark suit and light blue shirt against a dark background.

Dmytro Nasyrov. Photo supplied by the author.

Written by Dmytro Nasyrov PhD, software architect with 24 years of production experience. Dmytro is the founder of Gambit Stream. His writing covers software scope, integration boundaries and operating ownership.

Don't miss what's next. Subscribe to Production Architecture with Dmytro Nasyrov:
← Newer Feature Flags: Give Temporary Flags an Expiry Decision Older → Cache Invalidation: Verify the Reader’s Version
Software development
LinkedIn
Twitter
YouTube
Powered by Buttondown, the easiest way to start and grow your newsletter.