●SECURITY — Claude Security, which scans a connected GitHub repository and returns CWE-tagged findings with a suggested patch, now runs on the latest Mythos generation. It is in public beta for Enterprise plans●DESIGN — The reasoning behind it is worth noting: a capable model is riskiest with direct access, and constraining output to a fixed shape such as a patch or an alert lowers that risk considerably●COMMERCE — Anthropic published blueprints for commerce agents. Shopper-facing agents suggest and add to cart; merchant-facing agents advise on inventory and pricing. Neither completes the purchase●CLI — Claude Code v2.1.256 fixes launch failures on macOS 12 Monterey, along with errors in remote and scheduled sessions●AUTO — v2.1.257 adds a Containment Escape rule to auto mode, guarding against unauthorized cloud credential use and cross-tenant operations. If you live in auto mode, it is worth checking what now stops●LIMITS — The 50% weekly limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promo baseline, and this applies only to Claude Code weekly limits●SECURITY — Claude Security, which scans a connected GitHub repository and returns CWE-tagged findings with a suggested patch, now runs on the latest Mythos generation. It is in public beta for Enterprise plans●DESIGN — The reasoning behind it is worth noting: a capable model is riskiest with direct access, and constraining output to a fixed shape such as a patch or an alert lowers that risk considerably●COMMERCE — Anthropic published blueprints for commerce agents. Shopper-facing agents suggest and add to cart; merchant-facing agents advise on inventory and pricing. Neither completes the purchase●CLI — Claude Code v2.1.256 fixes launch failures on macOS 12 Monterey, along with errors in remote and scheduled sessions●AUTO — v2.1.257 adds a Containment Escape rule to auto mode, guarding against unauthorized cloud credential use and cross-tenant operations. If you live in auto mode, it is worth checking what now stops●LIMITS — The 50% weekly limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promo baseline, and this applies only to Claude Code weekly limits
Aligning Log Timezones at Display Time, or Fixing Them at Write Time
AdMob, the store reports, and my own logs each ended the day at a different moment. Here is how I went back and forth between display-side and write-side timezone handling, and what I settled on.
I was lining up last month's numbers when I noticed it. The day that AdMob reported, the day that the App Store Connect sales report referred to, and the day my own script had written into its log were all slightly out of step with one another.
The monthly totals matched closely enough. Broken out by day, only the two days at the edges refused to agree. I assumed I had dropped rows somewhere and rewrote my extraction filters several times. The extraction was fine. The three sources simply cut the day at three different moments.
Claude Code v2.1.257 added a Time format setting and a timeZone setting, so the timestamps I read are now mine to shape. More options are welcome, and they also bring a new question — do I align what I read, or do I fix what gets written?
I started with the first and came back to the second. Here is what the round trip taught me.
Two different jobs, easily confused
These solve different problems. If you think about them together, you lose track of which one you are touching.
Aspect
Align at display
Fix at write
Where you change it
CLI or viewer settings
The code that emits the log
What it covers
Only the screens you personally read
Every line written from now on
Existing data
Reinterpreted, never rewritten
Unchanged, so you need a migration
Effect on others
None
Reaches every reader and tool
How easily undone
Revert the setting
Formats end up mixed, so it is sticky
The display side is one line in the settings file.
{ "timeZone": "Asia/Tokyo"}
The write side means putting a function in the path. I placed one at the entrance of my aggregation script.
from datetime import datetime, timezone, timedeltaJST = timezone(timedelta(hours=9))def to_fixed_offset(value, assume=None): """Everything passes through here before it is logged. Naive values fail loudly.""" if isinstance(value, str): # Accept "2026-09-01T03:12:00Z" style input as well value = datetime.fromisoformat(value.replace("Z", "+00:00")) if value.tzinfo is None: if assume is None: raise ValueError( f"received a naive timestamp: {value!r} " "(pass assume= to state the source's timezone explicitly)" ) value = value.replace(tzinfo=assume) # Records stay in UTC. Shifting happens when reading, not here. return value.astimezone(timezone.utc).isoformat(timespec="seconds")
The point is that assume has no default and is never filled in silently. If a naive timestamp gets quietly completed, you can never again tell which rows came in under which assumption. Failing is the safer behaviour.
What I stopped noticing once the display was aligned
For a while I tried to solve the whole thing on the display side. One line in the settings file and every timestamp on screen reads in my local time. It is genuinely easier to read.
It did not work out well. Once everything looks aligned, you stop being able to see that it is not.
AdMob closes its day on Pacific time. App Store Connect uses its own basis. My own log was being written inside a container, in UTC. Show all three in local time and they look like they were measured with the same ruler. They were only sitting next to each other with three different rulers still in place.
That is why only the edge days disagreed. Days in the middle land in the same bucket under any of those definitions, so the monthly total looks healthy. The only days that reveal the problem are the first and the last — which are also the days that have not finished settling yet, so I kept setting them aside as "not final".
Aligning the display buys readability by giving up one of the signals that something is wrong. It took me three rewrites of the same aggregation before I drew that line.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You will be able to decide whether timezone belongs on the display side or the write side, based on how your own sources are shaped
✦You will be able to catch mixed offsets before you start reconciling, instead of spending a full day hunting for numbers that refuse to line up
✦You will be able to move your pipeline to ISO 8601 with explicit offsets at write time while still reading everything in your local time
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
When I switched to changing what gets written, I allowed myself three rules. More than that and I would not have kept them.
Write in UTC, always with an explicit offset.2026-09-01T03:12:00+00:00, so that whoever reads it later has nothing to guess. I accept a trailing Z on input, but I emit the explicit form.
Refuse naive values. The function above raises. When a source only ever returns naive timestamps, I write the assume for that source, so the assumption lives in code rather than in my memory.
Give each notion of "a day" its own name. My tables now carry report_day_admob, report_day_appstore, and run_day_jst as separate columns instead of one shared date.
The third rule did the most work. When the name is the same, the version of me reading it six weeks later treats them as the same thing. Splitting the column names forces the reconciliation code to state which day it is grouping by.
When I later folded Stripe payment logs into the same table, the same habit carried over. Payments already arrive in UTC, so instead of shifting anything I added one more explicitly named column.
A check that runs before reconciliation, not after
The migration window was the dangerous part. New lines carried offsets while older lines were still naive. Run an aggregation across that boundary and half your rows land nine hours away.
So I wrote something to run before the reconciliation starts.
import reimport sysfrom collections import Counterfrom pathlib import PathTS = re.compile(r"\b(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}:\d{2})(Z|[+-]\d{2}:?\d{2})?")def scan(log_dir): kinds = Counter() samples = {} for path in Path(log_dir).rglob("*.log"): for lineno, line in enumerate(path.read_text(encoding="utf-8", errors="replace").splitlines(), 1): m = TS.search(line) if not m: continue offset = m.group(3) kind = "naive" if offset is None else ("utc" if offset in ("Z", "+00:00", "+0000") else offset) kinds[kind] += 1 samples.setdefault(kind, f"{path.name}:{lineno}: {line[:80]}") return kinds, samplesif __name__ == "__main__": kinds, samples = scan(sys.argv[1]) for kind, count in kinds.most_common(): print(f"{kind:>10} {count:>7} {samples[kind]}") # Do not aggregate until the formats converge to one if len(kinds) > 1: print("\nMixed formats found. Normalise before aggregating.") sys.exit(1)
The last two lines are the whole point. A report that a human reads and judges is a report I skip on a busy morning. An exit code removes the judgement from the loop.
Since adding it, I have not once started an aggregation in the middle of a migration. It has not fired often, but every time it did, the formats really were mixed.
Where I landed
I now use both. Records are fixed to UTC with explicit offsets, and only the display is shifted to my local time.
Layer
Rule
Why
Write
UTC, offset required
Later readers should not have to guess
Store and reconcile
No conversion
Converting erases the original basis
Display
Shift to local time
I am the reader, and my internal clock is local
Start of an investigation
Look at the raw value once, unshifted
Keep the signal that something is off
Only the fourth row is a habit rather than a mechanism. When numbers disagree, the first thing I open is the raw log rather than the formatted view. Formatting makes things easier to read once you already have a question. It does not hand you the question.
Claude Code's timeZone setting lives above that fourth row. Now that shifting the display is supported properly, I have one fewer excuse for leaving the write side undefined.
Which side to start with
The right starting point depends on the shape of your sources. These are the rules of thumb I use.
One source only? Start with display. If you write the log and you are the only reader, changing the write format is not worth the effort.
Three or more sources? Start with the write side. Ads, store, and payments already put me past that line.
Someone else reads the log? Start with the write side. Display settings differ per reader, so you cannot count on them agreeing.
Cannot discard history? Start with the mixed-format check. Seeing the affected window matters more than deciding on a format.
Shifting is a decision you make for readers; fixing is a decision you make about your own responsibility as a writer. Get the order wrong and assumptions keep mixing behind a screen that reads beautifully. That is the one ordering I try not to break, even on a rushed day.
One thing to check tomorrow morning
Open a single log from your aggregation or monitoring and look at the end of a timestamp. If there is no offset there, that log currently carries an assumption that only the person who wrote it knows about.
Start there, and whether the check script is worth adding will answer itself. That is exactly where I restarted, staring at one line while trying to work out why two days at the end of the month refused to agree.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.