Skip to content

JavaScript Dates & Times — Date, Intl.DateTimeFormat, and the Temporal API

DodaTech Updated 2026-06-29 5 min read

In this tutorial, you will learn about JavaScript Dates & Times. We cover key concepts, practical examples, and best practices to help you master this topic.

Dates and times are notoriously tricky in JavaScript. The legacy Date object has quirks — months are zero-indexed, timezone handling is limited, and Parsing is inconsistent. Modern JavaScript provides the Intl.DateTimeFormat API for formatting, and the upcoming Temporal API promises a complete overhaul.

At DodaTech, date/time handling is critical for scheduling security scans, computing Compliance Windows, and displaying timezone-aware reports to global customers.

What You'll Learn

  • Creating and manipulating Date objects
  • Date formatting with Intl.DateTimeFormat
  • Timezone conversion and handling
  • Date arithmetic and difference calculation
  • The Temporal API (future standard)
  • Common pitfalls and best practices

The Date Object

Creating Dates

// Current date/time
const now = new Date();
console.log(now); // e.g., "2026-06-29T12:34:56.789Z"

// From timestamp (milliseconds since Unix epoch)
const epoch = new Date(0);
console.log(epoch); // "1970-01-01T00:00:00.000Z"

// From date string (avoid: parsing is implementation-dependent)
const fromString = new Date("2026-06-29T10:00:00");

// From components (months are 0-indexed!)
const fromComponents = new Date(2026, 5, 29, 10, 30, 0);
//                                    ^^ June = 5

Warning: Month Indexing

// Months are 0-11, not 1-12:
const jan = new Date(2026, 0, 1);  // January 1
const dec = new Date(2026, 11, 1); // December 1

// Using month names is clearer:
function makeDate(year, monthName, day) {
  const months = [
    "january", "february", "march", "april", "may", "june",
    "july", "august", "september", "october", "november", "december"
  ];
  const monthIndex = months.indexOf(monthName.toLowerCase());
  if (monthIndex === -1) throw new Error("Invalid month");
  return new Date(year, monthIndex, day);
}

Getting and Setting Components

const d = new Date(2026, 5, 29, 10, 30, 45);

console.log(d.getFullYear());      // 2026
console.log(d.getMonth());         // 5 (June)
console.log(d.getDate());          // 29
console.log(d.getDay());           // 1 (Monday; 0=Sun)
console.log(d.getHours());         // 10
console.log(d.getMinutes());       // 30
console.log(d.getSeconds());       // 45
console.log(d.getMilliseconds());  // 0
console.log(d.getTime());          // ms since epoch

// UTC variants:
console.log(d.getUTCFullYear());   // 2026
console.log(d.getUTCHours());      // depends on offset

// Setting (mutates in place):
d.setFullYear(2027);
d.setMonth(11, 25); // December 25

Date Formatting with Intl

const date = new Date(2026, 5, 29, 14, 30, 0);

// Basic formatting
console.log(new Intl.DateTimeFormat("en-US").format(date));
// "6/29/2026"

console.log(new Intl.DateTimeFormat("en-GB").format(date));
// "29/06/2026"

console.log(new Intl.DateTimeFormat("de-DE").format(date));
// "29.6.2026"

// Custom options
const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short"
});
console.log(formatter.format(date));
// "Monday, June 29, 2026 at 02:30 PM GMT"

// Supported tokens: weekday (short/long/narrow)
//   era, year (numeric/2-digit), month (numeric/2-digit/short/long/narrow)
//   day (numeric/2-digit), hour, minute, second, timeZoneName

Timezone Handling

const date = new Date("2026-06-29T10:00:00"); // Local interpretation

// Display in different timezones
const options = {
  timeZone: "America/New_York",
  timeZoneName: "short"
};
console.log(date.toLocaleString("en-US", {
  ...options,
  timeZone: "America/New_York"
}));

// List all IANA timezones (Node.js)
// const timezones = Intl.supportedValuesOf("timeZone");

// Converting between timezones
function convertTimezone(date, targetTZ) {
  return new Date(date.toLocaleString("en-US", {
    timeZone: targetTZ
  }));
}

// Timezone offset
const offset = date.getTimezoneOffset(); // minutes from UTC
// +330 means UTC+5:30 (India)

Date Arithmetic

// Add days (milliseconds approach)
function addDays(date, days) {
  const result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}

function addMonths(date, months) {
  const result = new Date(date);
  result.setMonth(result.getMonth() + months);
  return result;
}

// Difference between dates
function daysBetween(d1, d2) {
  const msInDay = 86400000;
  // Remove time component for date-only comparison
  const t1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate());
  const t2 = new Date(d2.getFullYear(), d2.getMonth(), d2.getDate());
  return Math.floor((t2 - t1) / msInDay);
}

// Check if date falls in a range
function isInRange(date, start, end) {
  return date >= start && date <= end;
}

Common Pitfalls

// 1. Month is 0-indexed
new Date(2026, 0, 1);  // January 1 (correct)
new Date(2026, 1, 1);  // February 1 (not January!)

// 2. Date parsing is inconsistent
new Date("2026-06-29");       // UTC midnight
new Date("06/29/2026");       // Local midnight
new Date("June 29, 2026");    // Local midnight

// Always use ISO 8601 format or parse explicitly

// 3. Comparison uses timestamps
const d1 = new Date(2026, 0, 1);
const d2 = new Date(2026, 0, 1);
console.log(d1 === d2);       // false (object comparison)
console.log(d1.getTime() === d2.getTime());  // true

// 4. Mutating methods
const original = new Date();
const copy = new Date(original);
copy.setDate(copy.getDate() + 1);  // Won't affect original

The Temporal API (Future Standard)

Temporal is TC39's modern replacement for Date. It's in Stage 3 as of 2026:

// Active proposal, may change:
// https://github.com/tc39/proposal-temporal

// PlainDate (date without time)
// Temporal.PlainDate.from("2026-06-29");

// PlainTime (time without date)
// Temporal.PlainTime.from("14:30:00");

// ZonedDateTime (date + time + timezone)
// Temporal.ZonedDateTime.from("2026-06-29T14:30:00[America/New_York]");

// Duration (time span)
// Temporal.Duration.from({ days: 7, hours: 2 });

// Arithmetic is cleaner:
// date.add({ months: 1 });
// date.until(today);  // returns Duration

Practice Questions

  1. Write a function that returns the last day of a given month.

  2. Write a function that lists all Mondays in a given year.

  3. Write a function that calculates the user's age based on birthdate (accounting for leap years).

  4. Write a function that formats a date as "X hours ago", "X days ago", "X months ago" (relative time).

  5. Implement a countdown timer that shows remaining days, hours, minutes, and seconds.

Challenge: Recurring Schedule Parser

Build a class that parses and evaluates recurring schedule rules (similar to cron but friendlier):

  • "every weekday at 9am"
  • "every Monday, Wednesday at 2pm"
  • "15th of every month"
  • "last Friday of month"

Given a date, return the next occurrence. This is similar to how DodaTech schedules recurring security scans — a cron-free approach that's easier for users to configure.

Real-World Task: Timezone-Aware Event Scheduler

Write a function that takes:

  • An array of user timezone IDs (e.g., ["America/New_York", "Asia/Tokyo"])
  • An event time in UTC
  • An event duration in hours

Returns: "optimized" meeting times that fall within 9am-5pm local for each participant.

This is the core logic behind scheduling tools, and it maps directly to DodaTech's compliance window scheduling for multi-region deployments.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro