Subscribe to get high-signal insights on how modern fintech is built.

engineering

Five Clocks in Every Transaction

A single payment carries five timestamps that each answer a different question, and confusing any two is how you fail an audit.

By Alex Kugell ·

When someone asks "when did this transaction happen?" you'd expect one answer. A customer sent $10,000 on Tuesday afternoon. Simple.

You get five answers. The customer tapped submit at 2:47 PM Eastern. Your ledger recorded the debit at 2:48 PM. The ACH operator's system logged receipt of the batch file at 7:47 PM UTC.

The value date, when interest stopped accruing for the sender, is the next business day. The settlement date, when the receiving bank actually got the money, is the day after that.

Same payment. Five different correct answers to "when."

Outside fintech, most engineers don't think twice about a single created_at column. Timestamps are timestamps. Pick one and move on.

But each of those five clocks answers a different question, for a different audience, with real money attached to getting it wrong. Confuse any two and you'll misaccrue interest, break reconciliation, or hand an auditor the wrong number.

Five timestamps, five questions

Think of it like mailing a package. The date you write on the return label. The date the post office stamps it. The date FedEx scans it at the hub. The date it shows as "delivered." The date the recipient opens the box.

Five timestamps for one package, and if the insurance claim asks "when was it sent?" the answer depends on which clock the claims adjuster uses.

Request time is when the customer pressed the button. Your application server logged the HTTP request. This is the timestamp on the customer's receipt, and the only one they think exists.

Booking time is when your ledger actually posted the entry. In a well-built system, this is milliseconds after request time. In a system with queued writes or async processing, it might be seconds or minutes later.

The gap matters when a customer checks their balance between request and booking. They see stale data, and if your support team searches by booking time, they'll insist the customer is confused.

Processor timestamp is when the payment rail's system recorded the instruction. For ACH, this is when the operator received the batch file. For cards, it's when the issuer processed the authorization.

The processor runs on UTC. Your application probably does too, but your customer was thinking in Eastern time and your ops team is reading Pacific.

Value date is when money becomes "real" for accounting purposes. For the sender, interest stops accruing on the debited amount. For the receiver, interest starts.

Usually value date matches booking date. But a bank can backdate a value date to cover a processing delay, or forward-date it for a scheduled payment.

Settlement date is when actual dollars move between banks. ACH typically takes T+1 or T+2, while Fedwire settles same-day. Card transactions run in daily batches, often the next business day for the acquiring bank, with the card network settling with the issuer on its own schedule.

Each clock ticks independently. That's the engineering problem.

Where the clocks diverge

Most of the time, the five timestamps cluster close enough that nobody notices. A Fedwire sent at 11 AM settles the same day, and if the value date is today, three of the five clocks effectively agree.

The divergence happens at the edges. And the edges are where the bugs live.

Cut-off times create cliffs. Same-day ACH runs three processing windows per day, with the final submission deadline at 4:45 PM ET and settlement by 6:00 PM. Miss it by two minutes and your payment settles tomorrow instead of today.

A payment submitted at 4:44 PM makes the cut-off, but at 4:46 PM it queues for the next business day. Your booking time says Tuesday for both, while settlement says Tuesday for one and Wednesday for the other.

Wire transfers have the same cliff. Fedwire's processing day runs about 22 hours, closing around 6:45 PM ET. A wire submitted at 6:44 PM settles today, but at 6:46 PM it queues for tomorrow. Your system recorded both on the same calendar day, yet your bank posts them on different ones.

The Friday afternoon problem is the classic case. A customer sends a payment at 4 PM on Friday. Your ledger books it Friday, and the value date is Friday.

But ACH doesn't settle over weekends. So settlement happens Monday.

Who pays interest for Saturday and Sunday? The sender's account was debited (for accounting purposes) on Friday, but the receiver's bank doesn't have the money until Monday. That's three days of float.

On a $100 million corporate payment at current rates (roughly 3.6% annual), one day of float is about $9,900. Three days is roughly $30,000. That money belongs to someone, and which timestamp you use determines who.

Multiply by hundreds of large-value payments per day across a mid-size bank. A systematic one-day error in value date assignment adds up faster than most teams expect.

Time zones make it worse. A customer in Los Angeles submits a payment at 1:30 PM Pacific. That's 4:30 PM Eastern, after the same-day ACH cut-off. The customer's local clock says early afternoon while the payment rail says too late.

So your system shows "submitted at 1:30 PM" and "settles tomorrow" on the same screen. The customer calls support, support sees the booking time, and nobody can explain the gap without understanding that the cut-off runs on a different clock.

The Postgres trap

Most applications store timestamps in one of two Postgres column types: timestamp or timestamptz. The difference shapes how your data behaves across time zones, and getting it wrong introduces silent errors that may not surface for months.

timestamp (without time zone) stores exactly what you give it. Insert 2024-10-07 14:47:00 and you get back 2024-10-07 14:47:00. It has no time zone awareness.

If your application inserts Eastern time but another service reads it expecting UTC, the stored value doesn't convert. You've silently introduced a four- or five-hour error depending on daylight saving time.

timestamptz stores everything internally as UTC and converts on display based on the session's timezone setting. This is almost always what you want for event timestamps.

But value date and settlement date aren't moments in time. They're calendar days in a specific business context.

Store a value date as timestamptz and it can shift to a different calendar day depending on the reader's time zone. October 8 in UTC is still October 7 in Pacific. If your settlement reports run with a different timezone setting than the process that wrote the data, your value dates are wrong by a day.

The fix is straightforward. Event timestamps (request time, booking time, processor timestamp) go in timestamptz columns. Business dates (value date, settlement date) go in plain date columns. No time component, no zone ambiguity.

But you have to make this decision at schema design time. Migrating a live ledger's timestamp columns touches every query, every report, and every downstream consumer.

One ACH Payment, Five Timestamps
Request time
2:47 PM
Customer submits payment
Booking time
2:48 PM
Ledger records the debit
Processor timestamp
7:47 PM UTC
NACHA receives the file
Value date
Oct 8
Interest accrual starts
Settlement date
Oct 9
Funds arrive at receiving bank
Request time
2:47 PM
Customer submits payment
Booking time
2:48 PM
Ledger records the debit
Processor timestamp
7:47 PM UTC
NACHA receives the file
Value date
Oct 8
Interest accrual starts
Settlement date
Oct 9
Funds arrive at receiving bank
1 min
~5 hrs
same day
+1 day
Same payment, five correct answers to “when did this happen?”

Every regulator wants a different clock

Different auditors ask different questions about the same transaction, and each question requires a different timestamp.

An AML investigator needs request time, to reconstruct when a customer initiated a suspicious transfer. A SOX auditor needs booking time, to verify when revenue was recognized in the ledger. A banking regulator checking reserve requirements needs settlement date, to determine when funds were actually available. Dispute resolution teams need all five, because the customer's story ("I sent it Tuesday afternoon") references request time, the bank's internal records reference booking time, and the settlement date determines which business day the payment counts against.

A transaction table with a single created_at column has already failed. It answered one question and made four others unanswerable. By the time an auditor asks, you can't reconstruct the missing timestamps from a single field.

Store all five, label them unambiguously, and let each one answer the question it was designed for. "When did this transaction happen?" was always five questions wearing one sentence.

Sources

Alex Kugell
Alex KugellAI/ML Engineer, Fintech Infrastructure

Writes Ledger Drift. Engineer building AI systems and financial infrastructure. Open source contributor to x402, ACK, and MPP – agentic payment protocols.

Frequently Asked Questions

Why do financial transactions need five timestamps instead of one?
Each timestamp answers a different question about the same payment. Request time records when the customer initiated the action. Booking time marks when the ledger posted the entry. The processor timestamp shows when the payment rail received the instruction. Value date determines when interest starts or stops accruing. Settlement date records when actual money moved between banks. An auditor, a customer, and a finance team will each ask about the same transaction and need different timestamps to get the right answer.
How much money can a one-day value date error cost?
On a $100 million corporate payment at current rates (roughly 3.6% annual), one day of float costs about $9,900. Over a weekend where the value date is Friday but settlement happens Monday, that gap becomes roughly $30,000 in interest that belongs to someone. Multiply by hundreds of large-value payments per day, and a systematic value date error in a bank's ledger creates losses that compound silently before anyone spots the pattern in reconciliation.
What is the difference between timestamp and timestamptz in Postgres for financial data?
Postgres timestamp stores the exact value you insert with no time zone awareness, so a value written in Eastern time reads back the same digits even when the session expects UTC, creating a silent four- or five-hour error depending on daylight saving time. timestamptz converts everything to UTC internally and converts back on read based on the session's time zone setting. Event timestamps like request time and booking time belong in timestamptz columns. Business dates like value date and settlement date belong in plain date columns with no time component, because a timestamptz value can shift to a different calendar day depending on the reading session's time zone.
What happens when you submit a payment one minute after the ACH cut-off?
Same-day ACH has three processing windows per day, with the final submission deadline at 4:45 PM ET. A payment submitted at 4:44 PM makes the cut-off and settles the same evening. The same payment at 4:46 PM misses it and settles the next business day. Your booking time shows both payments on Tuesday, but one settles Tuesday and the other settles Wednesday. That two-minute difference created a full calendar day of divergence between your ledger's booking time and the bank's settlement date, which surfaces as a timing break in overnight reconciliation.
Which transaction timestamp do regulators and auditors actually care about?
Different regulators need different timestamps from the same transaction. An AML investigator asks about request time to reconstruct when a customer initiated a suspicious transfer. A SOX auditor needs booking time to verify when revenue was recognized. A banking regulator checking reserve requirements uses settlement date to determine when funds were actually available. Dispute resolution needs all five because the customer's account references request time while the bank's records reference settlement. Storing only a single created_at column makes four of these five questions permanently unanswerable.

Built by Trio, a fintech-native engineering partner helping teams build the next generation of financial technology and infrastructure.

Subscribe to Ledger Drift for high-signal insights into how modern fintech is built, from systems to code to teams.

Keep reading

analysisX Money Launched at 6% YieldX Money offers 6% when the federal funds rate is 3.75% to 4%, and nobody has explained where the extra 200 basis points come from.
opinionGPU Securitization Will End BadlyCoreWeave pays five times more in interest than it earns, and pension funds hold the paper.
fintechHow Payments Actually WorkA card tap takes two seconds to approve and three days to settle, and the gap is where payments actually happen.
View more ›