Advanced Date and Time Handling in TypeScript: Methods and Examples

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

Advanced Date and Time Handling in TypeScript: From Typing to Complex Operations

\n\n

Working with dates and time is one of the most common and simultaneously tricky tasks in web development. At an advanced level, simply using new Date() is no longer sufficient. TypeScript provides a powerful type system, and modern libraries (Luxon, Date-fns) allow you to write safe, predictable, and performant code. In this article, we will explore advanced techniques: strict typing, working with time zones, complex formatting and parsing, as well as creating custom utilities.

\n\n

1. Strict Date Typing: Custom Types and Branded Types

\n\n

The Date type in TypeScript does not protect against passing an incorrect value. For example, a function expecting a birth date might receive the current timestamp. The solution is to use Branded Types or Nominal Typing.

\n\n
// Branded Type for a birth date\ntype BirthDate = Date & { __brand: 'BirthDate' };\n\nfunction createBirthDate(date: Date): BirthDate {\n  // Validation: date must not be in the future\n  if (date.getTime() > Date.now()) {\n    throw new Error('Birth date cannot be in the future');\n  }\n  return date as BirthDate;\n}\n\nfunction calculateAge(birthDate: BirthDate): number {\n  const diff = Date.now() - birthDate.getTime();\n  return Math.floor(diff / (1000 * 60 * 60 * 24 * 365.25));\n}\n\n// Usage\nconst rawDate = new Date('1990-05-15');\nconst birth = createBirthDate(rawDate);\nconsole.log(calculateAge(birth)); // OK\n\n// Error: Argument of type 'Date' is not assignable to parameter of type 'BirthDate'\n// console.log(calculateAge(new Date())); \n
\n\n

This approach ensures that only a validated birth date is passed to the calculateAge function. For more complex scenarios, you can create types for timestamps (UnixTimestamp), intervals (TimeInterval), etc.

\n\n

2. Advanced Formatting and Parsing with Intl.DateTimeFormat

\n\n

The built-in Intl.DateTimeFormat object allows flexible date formatting without third-party libraries. At an advanced level, we can create reusable formatters and handle complex locales.

\n\n
// Creating a formatter with options\nconst formatter = new Intl.DateTimeFormat('ru-RU', {\n  year: 'numeric',\n  month: 'long',\n  day: 'numeric',\n  weekday: 'long',\n  hour: '2-digit',\n  minute: '2-digit',\n  timeZone: 'Europe/Moscow',\n});\n\n// Formatting\nconsole.log(formatter.format(new Date())); \n// "пятница, 25 октября 2024 г., 14:30"\n\n// Parsing (custom utility)\nfunction parseLocalizedDate(dateStr: string, locale: string): Date | null {\n  const parts = dateStr.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n  if (!parts) return null;\n  \n  const [, year, month, day] = parts;\n  return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));\n}\n
\n\n

Tip: For performance, create formatters once and reuse them. Avoid creating a new instance on every call.

\n\n

3. Working with Time Zones and the Luxon Library

\n\n

The built-in Date handles time zones poorly. Luxon is a modern library from the creator of Moment.js that integrates perfectly with TypeScript.

\n\n
import { DateTime, Settings } from 'luxon';\n\n// Setting the default time zone\nSettings.defaultZone = 'Europe/Moscow';\n\n// Creating a date with an explicit zone\nconst meeting = DateTime.fromISO('2024-11-01T15:00:00', { zone: 'America/New_York' });\nconsole.log(meeting.toISO()); // 2024-11-01T15:00:00.000-04:00\n\n// Converting to another zone\nconst moscowTime = meeting.setZone('Europe/Moscow');\nconsole.log(moscowTime.toFormat('dd.MM.yyyy HH:mm')); // 01.11.2024 22:00\n\n// Working with intervals\nconst start = DateTi                

Blogs

Book Recommendations