TT Lab
Get started
Learn Learning paths Courses

The Language of Banking

Build the Engine That Computes Value Dates

Continue in TT Lab

Goal

You separate the receipt instant from the calendar date and build an engine that computes the value date with a holiday calendar and a cutoff time. You handle the counterparty institution's time zone and Seoul's daylight saving stretch of 1987 and 1988 correctly, and recompute all records for wrongly assigned value dates without overwriting the ledger.

Why it matters

An instant is one, but which day that instant is on gets an answer only once you decide on a time zone. At a bank, the cutoff time and the business-day calendar attach to this too, so the date on which money actually moves becomes not a stored fact but the product of rules. If this computation is scattered across several places in the code, the definition of "yesterday's transactions" differs by system, and that divergence does not look like an outage and survives until settlement reconciliation. Holidays and cutoff times change every year, so you put them in data, not code. That is why this lab's engine reads the calendar and settings from the SQLite it receives as --db. The grader does not trust the wording you wrote. It runs your engine with a temporary DB holding a holiday calendar and cutoff time that it made itself and compares the judgments against the values it computed itself. An engine with the answers embedded does not pass.

Steps

  1. Create and run /root/bizday/gen_bizday.py to make /root/bizday/txn.db. It holds 300 transfer receipts, 48 migrated transactions, 6 holidays, and 3 setting lines.
  2. Write the transactions whose UTC date and KST date split into /root/bizday/kst_shift.csv, and three numbers into /root/bizday/bucket.txt.
  3. Implement is-bizday, next-bizday, and add-bizdays in /root/bizday/bizday.py. The calendar is read from the holiday table of --db.
  4. Add value-date to bizday.py. The cutoff time and time zone are read from sys_param.
  5. Add localize to bizday.py, and write the transactions whose date splits between Seoul and London into /root/bizday/counterparty.csv.
  6. Write the 48 migrated transactions into /root/bizday/legacy_offsets.csv with local time and offset, and write three numbers into /root/bizday/dst_seoul.txt.
  7. Do not overwrite the ledger's value_date; create a value_date table in txn.db to recompute the 300 records, and write only the transactions that change into /root/bizday/redate.csv.
  8. Write the recomputed per-value-date tally into /root/bizday/valuedate_summary.csv and the report into /root/bizday/bizday_report.md.

Notes

Build the receipt ledger snapshot

Create and run /root/bizday/gen_bizday.py to make /root/bizday/txn.db. It holds 300 transfer receipts, 48 migrated transactions, 6 holidays, and 3 setting lines.

There are four tables. The received_utc of txn is in the shape 2026-06-29T01:02:03Z, and value_date uses the first 10 characters of received_utc as they are, per the current core system rule. That this rule is wrong is the starting point of this lab.

Separate the instant from the calendar date

Write the transactions whose UTC date and KST date differ into /root/bizday/kst_shift.csv with the header txn_id,received_utc,utc_date,kst_date, and write shift_count=, utc_days=, and kst_days= into /root/bizday/bucket.txt.

Read the instant with datetime.fromisoformat, move it with astimezone(ZoneInfo("Asia/Seoul")), and then take .date(). You must not change the order. utc_days and kst_days are the number of distinct dates that come out when bucketing by each.

Move the business-day calendar into the engine

Implement is-bizday, next-bizday, and add-bizdays in /root/bizday/bizday.py. Holidays are read from the holiday table of the DB received with --db.

A weekend is weekday() >= 5 and holidays come from the table. reason is one of bizday, weekend, and holiday. The grader runs with a temporary DB holding holidays different from the learner DB, so if you embed the calendar in code, it fails.

Requests after the cutoff go to the next business day

Add value-date to bizday.py. Read the time zone and the cutoff time from sys_param, and if the time is at or past the cutoff, add a day and then push to a business day.

Do the cutoff comparison after moving to local time. If you compare against a UTC time string, it is off by an hour in the stretch that had daylight saving time. The boundary is >= — a request that comes in exactly on the hour rolls over. The grader picks a different cutoff time on each run.

Move to the counterparty institution's calendar

Add localize to bizday.py, and write the transactions whose date splits between Seoul and London into /root/bizday/counterparty.csv with the header txn_id,received_utc,seoul_date,london_date.

Write utc_offset with a sign and two-digit hours and minutes, like +09:00. local must be an RFC 3339 string with an offset — if you strip the offset, that string is no longer an instant. London is UTC+1 in summer.

Find the one hour of summer 1987

Write the 48 migrated transactions into /root/bizday/legacy_offsets.csv as txn_id,received_utc,seoul_local,utc_offset, and write kdt_rows=, kst_rows=, and date_shift_rows= into /root/bizday/dst_seoul.txt.

Asia/Seoul is UTC+9 now, but in the summers of 1987 and 1988 there was daylight saving time. zoneinfo knows that fact, so ask it rather than computing it yourself. date_shift_rows is the number of migrated transactions whose KST date differs from the UTC date.

Recompute value dates without overwriting the ledger

Create a value_date(txn_id, value_date) table in txn.db, recompute the value dates of the 300 records into it, and write only the transactions that differ from the old value date into /root/bizday/redate.csv as txn_id,received_utc,old_value_date,new_value_date. Do not touch txn.value_date.

Do not reimplement the rule; import bizday.py as a module (put /root/bizday on sys.path and import). If you erase the old value dates the ledger recorded, you can no longer tell which rule assigned those values. Put the recomputed result in the new table.

Carry it into the tally and the report

Write the recomputed count and amount per value date into /root/bizday/valuedate_summary.csv as value_date,txn_count,amount_sum in ascending order, and write five sections, ## 무엇이 틀렸나, ## 결제일을 어떻게 계산하나, ## 상대방 표준시, ## 과거 서머타임, and ## 재발 방지, into /root/bizday/bizday_report.md (the Korean headings mean: What was wrong, How the value date is computed, The counterparty's time zone, Past daylight saving time, and Preventing recurrence).

You get the tally by joining the value_date table and txn. In the report, write the number of changed records, the cutoff time, which time zone you compute in, the counterparty institution's time zone, and the 1987 and 1988 offsets, in numbers and names. It is also worth checking that dates that fall on holidays are missing from the tally.