13.7 Text, dates and files
- Approved by
- Panaversity ·
In everyday life. At 9 p.m. on Monday in Columbus, it is already Tuesday morning in Tokyo. The same moment falls on a different date, depending on where you are. And a date written "10/11" may be October 11 or November 10, depending on the reader's country. Text and dates look simple, and both carry hidden rules.
What you will learn. Why a computer can get a date a day wrong, and which days to test on purpose, along with line endings and files.
Why it matters. Date code can pass on one computer and fail on the next.
What goes wrong with text?
A string is text. Programs split it, trim it and compare it. Three things go wrong often:
- Blank space and line endings. Windows ends each line with two invisible characters, a carriage return
\rand a newline\n. Mac and Linux use only\n. A TypeScript script that splits on\nalone leaves a\rstuck to the last value on each line, so"open\r"is not"open". It is like a space at the end of a spreadsheet cell: the two look the same, but a lookup does not match them. Lab 12's brief warned the agent about this bug. TypeScript'strim()and Python'sstrip()remove blank space from the ends. - Commas inside values. Brightline's exports have no commas inside values and no quoted fields, so splitting each line on commas works. Real exports often have a vendor name such as "Smith, Jones and Co.", in quotes, and splitting on commas cuts it in two. They need a CSV parser. A parser is code that reads the format properly. It comes from a library, ready-made code like a package. Ask the agent to use one for real data.
- Case and spelling.
"Net 30"and"net 30"are different strings.
How do I read a file?
Reading a file gives you its whole content as one string. The code then splits it into lines and values. When a file is missing, the program stops with an error that names the file's path, its address in the folders. Concept 13.8 shows how to read it. Brightline's scripts read a whole file at once, with TypeScript's readFileSync and Python's Path.read_text.
Reading a file with with. Agent-written Python often opens a file with a with block instead of reading it all at once. with opens the file and closes it when the block ends, even if an error stops the block. It is like a door that closes itself, whether you walk out or are pushed. Run it from the brightline-ap folder, because a short path such as data/vendors.csv is read from the folder the program runs in:
In Python.
with open("data/vendors.csv", encoding="utf-8") as f:
header = f.readline().strip()
print(header)
print(f.closed)vendor_id,vendor_name,terms
Truereadline() reads one line. The second line of the output shows the file was closed after the block. Chapter 14 shows the same with shape closing a connection to an API.

Figure 13.9. A with block. Whether the block ends normally or an error stops it, the file is closed.
Why are dates hard?
Brightline writes every date as ISO text: year, month, day, such as 2026-11-06. ISO dates sort correctly as text, because each has the same length, zeros included, year first. That is why the Chapter 12 script can compare due <= cutoff directly. Date math is harder than it looks.
| Case | Example | Result |
|---|---|---|
| Crossing a month end | 2026-10-20 plus 15 days | 2026-11-04 |
| Crossing a year end | 2026-12-20 plus 30 days | 2027-01-19 |
| A leap year | 2028-02-15 plus 15 days | 2028-03-01, because February 2028 has 29 days |
Why can a test pass on one computer and fail on another?
Because of time zones. A date, such as October 20, is the same everywhere. A moment is one instant, and it can fall on different dates in different places: 9 p.m. on Monday in Columbus is already Tuesday morning in Tokyo. In TypeScript, Date holds a moment, and some ways of making one use the computer's local time zone. So the same script can give a different day on a computer in New York and one in Tokyo. Brightline's code does date math in UTC, the world reference time, so the result is the same on every computer.
At work. The agent's first due-date function read the date with new Date("2026-10-07"), which is midnight UTC. Then it added the days and wrote the date back out in the computer's local time. Ana's tests passed on the team's build server, the shared computer that runs the tests, which is set to UTC. On her computer in Columbus they failed by one day. The agent had mixed local time and UTC, so the function was not pure: its answer depended on the computer. The fix was to build every date in UTC, and the lab runs the tests in three time zones to check it.
Here is that first function with Net 30 from October 7, on computers in three time zones. In October, midnight UTC on October 7 is still 8 p.m. on October 6 in Columbus.
| Computer's time zone | Due date | Why |
|---|---|---|
| UTC | November 6 | The date stays October 7, so the answer is right |
| Columbus | November 5 | Four hours behind UTC, so the date starts as October 6 |
| Tokyo | November 6 | Nine hours ahead, so the date stays October 7 |
In TypeScript. addDays starts at midnight UTC on the date it is given, adds the days in UTC, and keeps the first 10 characters of the result: the year, month and day. The Z in T00:00:00Z means UTC. Its answers should match the table above.
function addDays(isoDate: string, days: number): string {
const date = new Date(`${isoDate}T00:00:00Z`);
date.setUTCDate(date.getUTCDate() + days);
return date.toISOString().slice(0, 10);
}
console.log(addDays("2026-10-20", 15), addDays("2026-12-20", 30), addDays("2028-02-15", 15));2026-11-04 2027-01-19 2028-03-01This next line depends on the computer's time zone. It was run on a computer set to New York time.
console.log(new Date(2026, 9, 20).toISOString());2026-10-20T04:00:00.000Znew Date(2026, 9, 20) means midnight on October 20 in local time. The 9 means October, because months count from 0. New York is four hours behind UTC in October, so that moment is 4 a.m. in UTC. Tokyo is nine hours ahead, so on a computer there it prints 3 p.m. on the 19th.
Last, a line from a Windows file, with \r at its end. Predict both values.
const line = "INV-8301,V001,open\r";
console.log(line.split(",")[2] === "open", line.trim().split(",")[2] === "open");false trueIn Python. Python's date is a calendar day with no time and no time zone, and a timedelta is a length of time, such as 15 days.
from datetime import date, timedelta
def add_days(iso_date: str, days: int) -> str:
return (date.fromisoformat(iso_date) + timedelta(days=days)).isoformat()
print(add_days("2026-10-20", 15), add_days("2026-12-20", 30), add_days("2028-02-15", 15))
line = "INV-8301,V001,open\r"
print(line.split(",")[2] == "open", line.strip().split(",")[2] == "open")2026-11-04 2027-01-19 2028-03-01
False TrueWhere the languages differ.
| TypeScript | Python | |
|---|---|---|
| A calendar day | No such type. Date is a moment, so use UTC | date, with no time zone |
"2026-10-20" read as a date | UTC midnight | The calendar day |
| Months in a date built from parts | Count from 0 (October is 9) | Count from 1 |
| Splitting lines | split(/\r?\n/) handles both endings | splitlines() handles both endings |
The operating rule: keep dates as ISO text, do date math in UTC or with a calendar-day type, and test month ends, year ends, leap days and Windows line endings on purpose.

Figure 13.10. Date math crosses edges. The edges are where the bugs are, so they are where the tests go.
Practice it. Practice: text, dates and files, in your own tool, ten minutes.
Check yourself
Question 1 / 8
0 answered
A TypeScript script splits a Windows file on \n alone. What happens to the last value on each line?
- Owner
- Panaversity