Time Zones in TypeScript: How Not to Lose Your Mind Over Dates

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 Every Developer's Headache

\n\n

Have you ever caught a bug where a user from Vladivostok sees tomorrow's date even though it's today on the calendar? Or when a server in AWS us-east-1 sends a time that your client in Kaliningrad interprets as 3 AM? Welcome to the world of time zones in TypeScript.

\n\n

The problem is that JavaScript (and TypeScript as its superset) was originally designed for browsers. The built-in Date object is essentially a disguised UTC timestamp. It doesn't know how to work with time zones the way we need. And if you're writing a serious TypeScript application — whether it's a React admin panel, a Node.js backend, or a React Native mobile app — you can't get by without proper timezone handling.

\n\n

In this article, I'll show you how to tame time. We'll cover basic concepts, learn to use modern libraries, and avoid common pitfalls. Let's go!

\n\n

What You Need to Know About Time Zones Before Writing Code

\n\n

Before diving into TypeScript, let's get our facts straight. There are two types of time in the world: absolute (UTC) and local (what the user sees).

\n\n
    \n
  • UTC (Coordinated Universal Time) — is the standard. Servers, databases, and APIs should store time in UTC. This is a hard rule.
  • \n
  • IANA Time Zone Database — is a dictionary of time zones like Europe/Moscow, Asia/Vladivostok. These strings, not abbreviations like MSK or PST, should be used in code.
  • \n
  • Offset — is the difference from UTC, for example +03:00. But remember: the offset can change due to daylight saving time (DST). Therefore, storing an offset instead of an IANA zone is a bad idea.
  • \n
\n\n
\n

Golden rule: store in UTC, convert on the client, use IANA strings to identify zones.

\n
\n\n

Why Native Date Isn't Enough: Pain and Suffering

\n\n

Let's face it: new Date() in TypeScript is like a Swiss Army knife with a broken blade. It can only store a timestamp and display it in the browser's or server's local zone. Try doing something more complex:

\n\n
// Bad example: trying to work with zones using native tools\nconst date = new Date('2024-03-10T12:00:00');\nconsole.log(date.toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' }));\n// Output: "10.03.2024, 15:00:00" — but this is only a string, not an object!\n\n// What if you need to get a Date in another zone?\n// Oops... native JS cannot create a Date tied to a zone.\n
\n\n

The problem is that Date always stores time in UTC. All methods like getHours() simply convert UTC to the browser's local time. You cannot get a Date object that "thinks" it's in New York.

\n\n

That's why we use libraries. In the TypeScript ecosystem, there are two main candidates: Luxon and date-fns-tz.

\n\n

Luxon: Heavy Artillery for Working with Dates

\n\n

Luxon is a modern library from the creator of Moment.js (which is now deprecated). It's written from scratch, uses native Intl.DateTimeFormat, and is well-typed for TypeScript.

\n\n

Installing Luxon

\n\n
npm install luxon\nnpm install --save-dev @types/luxon\n
\n\n

Creating a Date in a Specific Time Zone

\n\n
import { DateTime } from 'luxon';\n\n// Create a date in the Europe/Moscow zone\nconst moscowTime = DateTime.fromObject(\n  { year: 2024, month: 3, day: 10, hour: 12 },\n  { zone: 'Europe/Moscow' }\n);\nconsole.log(moscowTime.toISO());\n// Output: "2024-03-10T12:00:00.000+03:00"\n\n// Convert to another zone\nconst nyTime = moscowTime.setZone('America/New_York');\nconsole.log(nyTime.toISO());\n// Output: "2024-03-10T05:00:00.00                

Blogs

Book Recommendations