Writing Date and Time Code With LLMs in November 2026: Store UTC, Name the Time Zone, and Test the Daylight Saving Edges
Date and time code looks simple until it breaks. A report counts one day twice, a reminder fires an hour late, or a nightly job runs zero times on the night the clocks change. LLMs are good at writing date and time code quickly, but they tend to write the happy path: one time zone, no daylight saving change, and a server clock that matches the user's. This guide shows how to get date and time code from a model that holds up in production.
The short version: store instants in UTC, keep the user's named time zone next to anything that is really a local time, and test the days when the clock jumps. The rest of this post explains each step with examples you can copy.
One instant, several wall-clock readings. Your code needs to know which one it is holding. Image: Clocks London, Berlin, Jersey. Jersey War Tunnels, 2009 by fw190a8, CC BY-SA 2.0, via Wikimedia Commons.
Why models get time wrong
Most date bugs come from mixing up two different things:
- An instant: a single point on the global timeline, like "the payment was captured at 2026-11-12T08:15:00Z". It is the same moment everywhere.
- A local (wall clock) time: what a clock on the wall shows in a place, like "the store opens at 9:00 in Chicago". Which instant that is depends on the date and the time zone rules for that date.
A model asked to "add a reminder at 9am every day" will often turn 9:00 into a fixed UTC offset, such as UTC-6, and store that. It works until daylight saving time ends and the reminder starts firing at 8:00 local time. The model did not do anything strange; it just was not told which kind of time it was handling. Your job is to tell it.
Step 1: Tell the model what kind of time each field is
Before you ask for code, list the time fields and label each one. A prompt like this works well:
I am writing a Python 3.12 service with PostgreSQL.
Time fields:
- created_at: instant when the row was created. Store as timestamptz in UTC.
- reminder_local_time: wall-clock time the user picked, e.g. 09:00.
- reminder_tz: IANA time zone name chosen by the user, e.g. "America/Chicago".
Rules:
- Never store a fixed UTC offset for a user's local time.
- Use zoneinfo, not pytz.
- All "now" values must come from an injected clock so tests can control them.
Write a function next_reminder_utc(now_utc, local_time, tz_name) that returns
the next instant the reminder should fire, as an aware UTC datetime.
Naming the language version and the libraries matters. Python's zoneinfo module has been in the standard library since 3.9, and datetime.utcnow() is deprecated as of 3.12 because it returns a naive datetime. If you do not say which version you use, a model may mix older and newer styles in the same file.
Step 2: Store instants in UTC, and local times with a zone name
The storage rules are simple and boring, which is what you want:
- For events that already happened (created, paid, logged in), store an aware timestamp in UTC. In PostgreSQL, use
timestamptz, which stores an absolute instant and converts on input and output. - For future local times that a person picked (meetings, store hours, alarms), store the local date or time plus an IANA zone name like
Europe/Berlin. Do not convert it to UTC at save time, because the zone's rules can change before the date arrives. - Never store an abbreviation like "EST" or "CST" as the zone. Abbreviations are ambiguous (CST is used for more than one zone) and do not say whether daylight saving applies.
- When you serialize a timestamp for an API, use ISO 8601 / RFC 3339 with an explicit offset or
Z, for example2026-11-12T08:15:00Z.
Ask the model to review its own schema against these rules. A useful follow-up prompt is: "List every column or field that holds a time, say whether it is an instant or a local time, and show how its zone is known." If it cannot answer for a field, that field is a bug waiting to happen.
Step 3: Ban naive datetimes at the boundary
A naive datetime has no zone attached. Python will happily compare and subtract naive values, so mistakes stay silent. Ask the model to add a guard wherever times enter your code:
from datetime import datetime, timezone
def require_aware_utc(value: datetime) -> datetime:
if value.tzinfo is None or value.utcoffset() is None:
raise ValueError(f"naive datetime not allowed: {value!r}")
return value.astimezone(timezone.utc)
Then search the code it produced for the usual suspects: datetime.now() with no argument, datetime.utcnow(), datetime.fromtimestamp() with no tz, and string parsing that drops the offset. In JavaScript, look for new Date("2026-11-12") style parsing and month arithmetic, since Date months are zero-based (January is 0). Each of these is fine in a quick script and risky in a service.
Step 4: Handle the two daylight saving traps on purpose
Daylight saving time creates two kinds of local times that break naive code:
- Times that do not exist. In the United States in 2026, clocks jumped forward on Sunday, March 8. In
America/Los_Angeles, 2:30 a.m. that day never happened. - Times that happen twice. On Sunday, November 1, 2026, US clocks fell back, so 1:30 a.m. in
America/Los_Angeleshappened once in daylight time and once in standard time.
Other regions change on different dates. The European Union, for example, changes on the last Sunday of March and the last Sunday of October, so in 2026 that was March 29 and October 25. Many places do not observe daylight saving at all. This is exactly why you store a zone name and let the time zone database do the work instead of hard-coding rules.
Decide what your app should do in each case and write it into the prompt. For a daily reminder, a common choice is: if the local time does not exist, fire at the first valid minute after it; if it happens twice, fire only on the first occurrence. In Python, the fold attribute on a datetime selects which of two repeated times you mean (fold=0 is the first, fold=1 the second). Ask the model to explain, in a comment, exactly how its code handles both cases. If the comment is vague, the code probably is too.
When clocks fall back, the same local hour happens twice. Test that night on purpose. Image: Daylight saving time lapse, 2013-10-27 by Per A.J. Andersson, CC BY-SA 3.0, via Wikimedia Commons.
Step 5: Write the edge-case tests before you trust the code
The fastest way to catch time bugs is a small table of tests around the dates when clocks change. Inject the clock so tests do not depend on the machine running them, then ask the model for tests like these:
from datetime import datetime, time, timezone
import pytest
from reminders import next_reminder_utc
UTC = timezone.utc
@pytest.mark.parametrize("now_utc, local, tz, expected_utc", [
# Ordinary day in Chicago (standard time, UTC-6)
(datetime(2026, 11, 12, 12, 0, tzinfo=UTC), time(9, 0), "America/Chicago",
datetime(2026, 11, 13, 15, 0, tzinfo=UTC)),
# Summer day in Chicago (daylight time, UTC-5)
(datetime(2026, 7, 1, 12, 0, tzinfo=UTC), time(9, 0), "America/Chicago",
datetime(2026, 7, 1, 14, 0, tzinfo=UTC)),
# A zone without daylight saving
(datetime(2026, 7, 1, 12, 0, tzinfo=UTC), time(9, 0), "Asia/Tokyo",
datetime(2026, 7, 2, 0, 0, tzinfo=UTC)),
])
def test_next_reminder(now_utc, local, tz, expected_utc):
assert next_reminder_utc(now_utc, local, tz) == expected_utc
Then add the hard cases yourself: a 2:30 a.m. reminder in America/Los_Angeles computed on March 7, 2026, and a 1:30 a.m. reminder computed on October 31, 2026. Work out the expected answers by hand from your chosen rules, not by running the code, or you will just be freezing whatever the code already does. A few more cases worth having:
- The end of a month and February 29 (2028 is the next leap year), if you add months or years.
- A "now" that is a few seconds before and after the target time, to check you do not skip or double a run.
- A user whose zone is many hours from the server, such as
Pacific/Auckland, so "today" is a different date for them.
If any test fails, paste the failing case and the full assertion message back to the model and ask it to fix the cause, not the test. A model that "fixes" a DST test by changing the expected value is telling you it does not understand the rule you picked.
Step 6: Show people the time in their own zone
Display is the last place time goes wrong. Convert from UTC to the viewer's zone only when you render, and say which zone you used. "Nov 12, 2026, 12:15 AM PST" is clear; "00:15" is not. For anything people will act on, like a deadline or a meeting, show the zone label every time.
Also be careful with "days". A daily sales report grouped by UTC date will split a California evening across two days. Ask the model to group by the business's zone explicitly, for example in PostgreSQL with date_trunc('day', created_at AT TIME ZONE 'America/Los_Angeles'), and to state that choice in the report heading.
Step 7: Keep the time zone database current
Time zone rules change when governments change them, sometimes with short notice. Your code is only as correct as the tz database it reads. Ask the model where its code gets zone data on your platform, then make sure you update it:
- On Linux servers and containers, the system tzdata package. Slim container images sometimes leave it out, so check that it is installed.
- In Python,
zoneinfouses the system data and can fall back to thetzdatapackage from PyPI, which is useful on Windows. - In browsers and Node.js, the runtime's bundled data, which updates when you update the runtime.
Add tzdata to the list of dependencies you update on a schedule. It is a small, easy step that prevents a class of bugs nobody will think to look for.
A checklist to paste into your next prompt
Before writing date/time code:
1. Label each field: instant (store UTC) or local time (store with IANA zone name).
2. No naive datetimes; reject them at input boundaries.
3. Get "now" from an injected clock.
4. Say how nonexistent and repeated local times are handled.
5. Include tests for DST start, DST end, a no-DST zone, and a far-away zone.
6. Convert to the viewer's zone only at display time, with a zone label.
7. State where tz data comes from and how it is updated.
Time code is a place where a confident-looking answer can be wrong in ways that only show up twice a year. Give the model the rules up front, make it explain its choices, and let a table of edge-case tests have the final word.
Comments
Post a Comment