2 min read

UTC Everywhere: How to Stop Date Bugs in Laravel + Vue Apps

Date bugs don’t crash apps — they quietly destroy trust. After fighting timezones across Laravel and Vue apps at multiple companies and personal projects, here’s the UTC-everywhere approach that actually stops recurring date bugs for good.
UTC Everywhere: How to Stop Date Bugs in Laravel + Vue Apps

Date bugs are the kind that don’t crash your app.

They quietly ruin trust.

A meeting shows up on the wrong day.
A recurring event fires twice.
A user in another timezone swears your app is broken.

And the worst part?
Everything looks correct in the database.

If you’ve built real Laravel + Vue apps, you’ve probably fought this war already. I have too. Multiple times. At companies. On personal projects. Each time thinking, “Surely this time we’ve solved it.”

Spoiler: you haven’t… until you go UTC everywhere.

This article isn’t theory. It’s the hard-earned rulebook that finally stops date bugs for good.


The Core Rule (Burn This Into Your Brain)

Store everything in UTC. Always. No exceptions.

Not “mostly UTC.”
Not “UTC except for display.”
Not “UTC but this one column is local.”

Everything.

Dates. Datetimes. Timestamps. Recurring events. Logs. Schedules. Tokens. Expirations.

UTC is the language your system speaks internally.
Local time is just a translation layer.

Once you truly commit to that idea, everything else gets simpler.


Why Date Bugs Keep Happening

Most apps fail because they mix responsibilities.

  • The backend sometimes assumes local time
  • The frontend sometimes sends local time pretending it’s UTC
  • The database stores a mix of both
  • Developers “fix” bugs by adding offsets in random places

This creates time drift. Invisible drift. The kind that only shows up:

  • during DST changes
  • for users in other timezones
  • with recurring events
  • when editing existing records

By the time you notice, production data is already poisoned.


The Laravel Side: Make UTC Non-Negotiable

1. Set Laravel’s App Timezone to UTC

In config/app.php:

'timezone' => 'UTC',

This ensures:

  • now()
  • Carbon::now()
  • model timestamps
  • queue jobs
  • scheduled tasks

all speak UTC by default.


2. Store Datetimes as UTC in the Database

Your database should never know what timezone a user lives in.

That means:

  • DATETIME or TIMESTAMP values are UTC
  • no offsets
  • no “local” columns
  • no magic conversions

If you ever see a column named something like event_time_local, that’s a warning sign.


3. Normalize Dates at the Boundary (Requests In)

Any time the frontend sends a date:

  • assume it is local to the user
  • convert it to UTC immediately
  • store only the UTC version

Example mental model:

“The frontend speaks local. The backend translates once, then forgets.”

If you delay conversion, bugs creep in.


4. Always Return ISO 8601 UTC Strings

When sending dates back to the frontend:

{
  "starts_at": "2026-02-05T16:00:00Z"
}

ISO 8601 with Z is your contract.


The Vue Side: Timezones Are a UI Concern

Vue should never guess what the backend meant.

1. Treat All API Dates as UTC

When data enters your frontend:

  • assume it is UTC
  • parse it explicitly as UTC
  • never assume browser locale magically matches intent

2. Convert to Local Time Only for Display

The only place local time belongs is:

  • UI rendering
  • date pickers
  • human-readable labels

The moment a user edits or submits a date:

  • convert it back to UTC
  • send UTC to the API

3. Recurring Events Are Where Apps Go to Die

Recurring events expose every flaw in your system.

The fix:

  • recurrence rules are defined in UTC
  • recurrence IDs are derived from UTC dates
  • conversions only happen when displaying or selecting

The One Pattern That Actually Works

UTC is storage and logic.
Local time is presentation only.


The “We’ll Just Fix It Later” Trap

Once mixed-timezone data exists:

  • migrations become dangerous
  • historical data becomes ambiguous
  • bug fixes turn into guesswork

Stop the bleeding early.


Final Takeaway

UTC everywhere isn’t optional. It’s survival.