I kept seeing notes with dates from the future.
A note I created tonight would show tomorrow's date. I'm in Mexico City, but my app was speaking UTC. Late at night, my notes appeared to time-travel to the next day.
My notes app doesn't have titles — the date is the name. So when dates were wrong, it wasn't a minor bug. I built this app, and even I couldn't tell when I wrote my own notes.
I'd stare at a note and think: is this from today? Yesterday? Why does it say tomorrow?
"March 18" makes your brain do work — what day is it today? But "today" or "yesterday"? Zero effort. The meaning lands instantly.
Here's how I fixed it — and why you don't need a backend solution at all.
The backend approach (and why I skipped it)
You could ask users to configure their timezone. Store it. Apply it server-side.
But then:
A note I created tonight would show tomorrow's date. I'm in Mexico City, but my app was speaking UTC. Late at night, my notes appeared to time-travel to the next day.
My notes app doesn't have titles — the date is the name. So when dates were wrong, it wasn't a minor bug. I built this app, and even I couldn't tell when I wrote my own notes.
I'd stare at a note and think: is this from today? Yesterday? Why does it say tomorrow?
"March 18" makes your brain do work — what day is it today? But "today" or "yesterday"? Zero effort. The meaning lands instantly.
Here's how I fixed it — and why you don't need a backend solution at all.
The backend approach (and why I skipped it)
You could ask users to configure their timezone. Store it. Apply it server-side.
But then:
- Extra friction — One more hurdle before the app feels usable
- Bad first impression — New users see broken dates until they dig into settings
- Stale data — User travels? Now their timezone is wrong until they remember to fix it
I wanted dates to just work. No setup. No maintenance.
The insight
The browser already knows the user's timezone. You don't need to store it. You don't need to ask for it. It's there — use it.
The pattern:
- Server renders UTC date
- JavaScript converts to local timezone
- Replace the server date with the converted one
No database changes. No user settings. The browser does the heavy lifting.
How to implement it
I use Alpine.js, but this works in React, Vue, vanilla JS — anything.
Server renders a fallback:
<span x-text="format('{{ $note->created_at->toIso8601String() }}', 'time')">
{{ $note->created_at->format('g:i A') }}
</span>The inner content is the UTC fallback — a safety net. The `x-text` swaps in the converted time once JavaScript runs.
The format function:
format(isoString, type) {
const date = new Date(isoString);
if (type === 'time') {
return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
}
const today = new Date();
today.setHours(0, 0, 0, 0);
const noteDate = new Date(date);
noteDate.setHours(0, 0, 0, 0);
const diffDays = (today - noteDate) / (1000 * 60 * 60 * 24);
if (diffDays === 0) return 'Today';
if (diffDays === 1) return 'Yesterday';
const options = date.getFullYear() === today.getFullYear()
? { month: 'long', day: 'numeric' }
: { month: 'long', day: 'numeric', year: 'numeric' };
return date.toLocaleDateString([], options);
}Key pieces:
- `toLocaleTimeString` / `toLocaleDateString` — Browser's native locale handling. Set it and forget it.
- `setHours(0, 0, 0, 0)` — Strip the time. We only care about calendar days.
- "Today" / "Yesterday" — Words beat dates for recent items. Always.
- Year handling — Only show the year when it's different. Less noise.
The tradeoff
There's a brief flicker — UTC flashes before JavaScript kicks in.
I'm okay with that.
It's a functional fallback if JavaScript is disabled, which almost no one does. Progressive enhancement: show something useful immediately, then make it better.
Why this matters
"Today" is instant. "March 18" is a mini math problem.
Timezones are automatic. No configuration. No stale settings. No "wait, why does this say 3 AM?" when you're eating lunch.
Your users have enough to think about. Don't make them do arithmetic just to understand when something happened.
Takeaway
You don't need a backend solution. The browser already knows the timezone — use it.
Server-render a fallback. Convert with JavaScript. Show "today" and "yesterday" for recent items.
That's the whole thing.