Time Zones in JavaScript: How Not to Lose Your Mind

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Time is a Pain

\n\n

Everyone who has ever written code in JavaScript has encountered this pain. You show the user the time 12:00, and they write: "It's 14:00 for me, everything is broken." Or you save a date to the database, and when loading it shifts back by an hour. Sound familiar?

\n\n

Working with time zones in JavaScript is a topic that many developers put off "for later." And that's a mistake. Because if you don't figure it out right away, you'll end up rewriting half the application later.

\n\n

In this article, I'll tell you how to properly work with timezones in JS, show real code examples, and give you a few tricks that will save you hours of debugging. Let's go!

\n\n

Why Does JavaScript Handle Dates So Strangely?

\n\n

Let's start with the main thing: the Date object in JavaScript is, to put it mildly, not a gift. It stores time in milliseconds since January 1, 1970 UTC. And that would be fine, but:

\n\n
    \n
  • getMonth() returns months from 0 to 11 (January is 0, December is 11)
  • \n
  • The getHours(), getMinutes() methods return the time in the user's local time zone
  • \n
  • And the getUTCHours(), getUTCMinutes() methods return the time in UTC
  • \n
\n\n

And this is where the confusion begins. Let's say you write:

\n\n
const now = new Date();\nconsole.log(now.getHours());      // 14 — if you are in MSK\nconsole.log(now.getUTCHours());   // 11 — UTC
\n\n

If you save now to the database and then retrieve it on a server in a different timezone, you'll get a mess. Therefore, the first rule of working with dates and time zones in JavaScript is to always store time in UTC and convert it on the client.

\n\n

Intl.DateTimeFormat: The Built-in Savior

\n\n

Many people don't know, but modern JavaScript has a powerful built-in API for working with date formatting with timezone awareness. It's Intl.DateTimeFormat. It supports all major time zones and can format dates beautifully.

\n\n

Basic Example

\n\n
const date = new Date('2025-03-15T12:00:00Z');\n\nconst formatter = new Intl.DateTimeFormat('ru-RU', {\n    timeZone: 'America/New_York',\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n    hour: '2-digit',\n    minute: '2-digit'\n});\n\nconsole.log(formatter.format(date)); \n// "March 15, 2025, 08:00" — time in New York
\n\n

Notice: we pass the date in UTC (the letter Z at the end), and Intl.DateTimeFormat itself converts it to the desired timezone. This is ideal for displaying time to users from different regions.

\n\n

List of All Timezones

\n\n

If you don't know which timezone to specify, you can get the full list:

\n\n
console.log(Intl.supportedValuesOf('timeZone'));\n// ["Africa/Abidjan", "Africa/Accra", ..., "Pacific/Wallis"]
\n\n

This list includes over 500 timezones. Use it to make sure your timezone is supported.

\n\n

Moment.js: A Classic, But With Caveats

\n\n

For a long time, moment.js was the standard for working with dates in JavaScript. It can parse, format, manipulate dates, and of course, work with time zones via the moment-timezone plugin.

\n\n

Installation

\n\n
npm install moment moment-timezone
\n\n

Example with Timezones

\n\n
const moment = require('moment-timezone');\n\nconst now = moment();\nconsole.log(now.tz('Europe/Moscow').format('YYYY-MM-DD HH:mm'));\n// 2025-03-15 14:00 — Moscow time\n\nconst nyTime = moment.tz('2025-03-15 12:00', 'America/New_York');\nconsole.log(nyTime.clone().tz('Europe/Moscow').format('YYYY-MM-DD HH:mm'));\n// 2025-03-15 19:00 — 7 hour difference
\n\n

Important: moment.js is a legacy library. The authors themselves recommend switching

Blogs

Book Recommendations